simple-card.c 12.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * ASoC simple sound card support
 *
 * Copyright (C) 2012 Renesas Solutions Corp.
 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
11
#include <linux/clk.h>
12
#include <linux/device.h>
13
#include <linux/module.h>
14
#include <linux/of.h>
15
#include <linux/platform_device.h>
16
#include <linux/string.h>
17
#include <sound/simple_card.h>
18 19
#include <sound/soc-dai.h>
#include <sound/soc.h>
20

21 22
struct simple_card_data {
	struct snd_soc_card snd_card;
23 24 25
	struct simple_dai_props {
		struct asoc_simple_dai cpu_dai;
		struct asoc_simple_dai codec_dai;
26
		unsigned int mclk_fs;
27
	} *dai_props;
28
	unsigned int mclk_fs;
29 30
	struct asoc_simple_jack hp_jack;
	struct asoc_simple_jack mic_jack;
31
	struct snd_soc_dai_link *dai_link;
32 33
};

34
#define simple_priv_to_card(priv) (&(priv)->snd_card)
35
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
36 37
#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
38

39 40
#define DAI	"sound-dai"
#define CELL	"#sound-dai-cells"
41 42
#define PREFIX	"simple-audio-card,"

43 44 45 46 47
static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
	struct simple_dai_props *dai_props =
48
		simple_priv_to_props(priv, rtd->num);
49 50
	int ret;

51
	ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai);
52 53
	if (ret)
		return ret;
54

55
	ret = asoc_simple_card_clk_enable(&dai_props->codec_dai);
56
	if (ret)
57
		asoc_simple_card_clk_disable(&dai_props->cpu_dai);
58 59 60 61 62 63 64 65 66

	return ret;
}

static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
	struct simple_dai_props *dai_props =
67
		simple_priv_to_props(priv, rtd->num);
68

69
	asoc_simple_card_clk_disable(&dai_props->cpu_dai);
70

71
	asoc_simple_card_clk_disable(&dai_props->codec_dai);
72 73
}

74 75 76 77 78 79 80 81 82 83 84 85
static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
				    unsigned long rate)
{
	if (!simple_dai->clk)
		return 0;

	if (clk_get_rate(simple_dai->clk) == rate)
		return 0;

	return clk_set_rate(simple_dai->clk, rate);
}

86 87 88 89 90
static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
				      struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
91
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
92
	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
93 94
	struct simple_dai_props *dai_props =
		simple_priv_to_props(priv, rtd->num);
95
	unsigned int mclk, mclk_fs = 0;
96 97
	int ret = 0;

98 99 100 101 102 103 104
	if (priv->mclk_fs)
		mclk_fs = priv->mclk_fs;
	else if (dai_props->mclk_fs)
		mclk_fs = dai_props->mclk_fs;

	if (mclk_fs) {
		mclk = params_rate(params) * mclk_fs;
105 106 107 108 109 110 111 112 113

		ret = asoc_simple_set_clk_rate(&dai_props->codec_dai, mclk);
		if (ret < 0)
			return ret;

		ret = asoc_simple_set_clk_rate(&dai_props->cpu_dai, mclk);
		if (ret < 0)
			return ret;

114 115
		ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
					     SND_SOC_CLOCK_IN);
116 117 118 119 120 121 122
		if (ret && ret != -ENOTSUPP)
			goto err;

		ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
					     SND_SOC_CLOCK_OUT);
		if (ret && ret != -ENOTSUPP)
			goto err;
123
	}
124
	return 0;
125
err:
126 127 128
	return ret;
}

129
static const struct snd_soc_ops asoc_simple_card_ops = {
130 131
	.startup = asoc_simple_card_startup,
	.shutdown = asoc_simple_card_shutdown,
132 133 134
	.hw_params = asoc_simple_card_hw_params,
};

135 136
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
137
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
138 139
	struct snd_soc_dai *codec = rtd->codec_dai;
	struct snd_soc_dai *cpu = rtd->cpu_dai;
140 141
	struct simple_dai_props *dai_props =
		simple_priv_to_props(priv, rtd->num);
142
	int ret;
143

144
	ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
145 146
	if (ret < 0)
		return ret;
147

148
	ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
149 150
	if (ret < 0)
		return ret;
151 152 153 154

	return 0;
}

155
static int asoc_simple_card_dai_link_of(struct device_node *node,
156
					struct simple_card_data *priv,
157
					int idx,
158
					bool is_top_level_node)
159
{
160
	struct device *dev = simple_priv_to_dev(priv);
161 162
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
163 164
	struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
	struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
165
	struct device_node *cpu = NULL;
166
	struct device_node *plat = NULL;
167
	struct device_node *codec = NULL;
168 169
	char prop[128];
	char *prefix = "";
170
	int ret, single_cpu;
171

172
	/* For single DAI link & old style of DT node */
173
	if (is_top_level_node)
174
		prefix = PREFIX;
175 176

	snprintf(prop, sizeof(prop), "%scpu", prefix);
177 178
	cpu = of_get_child_by_name(node, prop);

179 180 181 182 183 184
	if (!cpu) {
		ret = -EINVAL;
		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
		goto dai_link_of_err;
	}

185 186 187
	snprintf(prop, sizeof(prop), "%splat", prefix);
	plat = of_get_child_by_name(node, prop);

188 189 190
	snprintf(prop, sizeof(prop), "%scodec", prefix);
	codec = of_get_child_by_name(node, prop);

191
	if (!codec) {
192
		ret = -EINVAL;
193
		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
194
		goto dai_link_of_err;
195
	}
196

197 198
	ret = asoc_simple_card_parse_daifmt(dev, node, codec,
					    prefix, &dai_link->dai_fmt);
199 200 201
	if (ret < 0)
		goto dai_link_of_err;

202
	of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
203

204 205 206 207 208 209 210 211 212 213 214 215 216
	ret = asoc_simple_card_parse_cpu(cpu, dai_link,
					 DAI, CELL, &single_cpu);
	if (ret < 0)
		goto dai_link_of_err;

	ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
	if (ret < 0)
		goto dai_link_of_err;

	ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
	if (ret < 0)
		goto dai_link_of_err;

217
	ret = asoc_simple_card_of_parse_tdm(cpu, cpu_dai);
218 219 220
	if (ret < 0)
		goto dai_link_of_err;

221
	ret = asoc_simple_card_of_parse_tdm(codec, codec_dai);
222 223 224
	if (ret < 0)
		goto dai_link_of_err;

225
	ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
226 227 228
	if (ret < 0)
		goto dai_link_of_err;

229
	ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
230 231 232
	if (ret < 0)
		goto dai_link_of_err;

233 234
	ret = asoc_simple_card_canonicalize_dailink(dai_link);
	if (ret < 0)
235
		goto dai_link_of_err;
236

237 238 239 240 241
	ret = asoc_simple_card_set_dailink_name(dev, dai_link,
						"%s-%s",
						dai_link->cpu_dai_name,
						dai_link->codec_dai_name);
	if (ret < 0)
242 243
		goto dai_link_of_err;

244
	dai_link->ops = &asoc_simple_card_ops;
245
	dai_link->init = asoc_simple_card_dai_init;
246

247
	asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
248

249
dai_link_of_err:
250 251 252
	of_node_put(cpu);
	of_node_put(codec);

253 254 255
	return ret;
}

256 257 258 259 260
static int asoc_simple_card_parse_aux_devs(struct device_node *node,
					   struct simple_card_data *priv)
{
	struct device *dev = simple_priv_to_dev(priv);
	struct device_node *aux_node;
261
	struct snd_soc_card *card = simple_priv_to_card(priv);
262 263 264 265 266 267 268 269 270
	int i, n, len;

	if (!of_find_property(node, PREFIX "aux-devs", &len))
		return 0;		/* Ok to have no aux-devs */

	n = len / sizeof(__be32);
	if (n <= 0)
		return -EINVAL;

271 272
	card->aux_dev = devm_kcalloc(dev,
			n, sizeof(*card->aux_dev), GFP_KERNEL);
273
	if (!card->aux_dev)
274 275 276 277 278 279
		return -ENOMEM;

	for (i = 0; i < n; i++) {
		aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
		if (!aux_node)
			return -EINVAL;
280
		card->aux_dev[i].codec_of_node = aux_node;
281 282
	}

283
	card->num_aux_devs = n;
284 285 286
	return 0;
}

287
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
288
{
289
	struct device *dev = simple_priv_to_dev(priv);
290
	struct snd_soc_card *card = simple_priv_to_card(priv);
291
	struct device_node *dai_link;
292
	struct device_node *node = dev->of_node;
293
	int ret;
294

295 296 297
	if (!node)
		return -EINVAL;

298 299
	dai_link = of_get_child_by_name(node, PREFIX "dai-link");

300 301 302
	ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
	if (ret < 0)
		goto card_parse_end;
303

304 305 306
	ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1);
	if (ret < 0)
		goto card_parse_end;
307

308
	/* Factor to mclk, used in hw_params() */
309
	of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
310

311
	/* Single/Muti DAI link(s) & New style of DT node */
312
	if (dai_link) {
313
		struct device_node *np = NULL;
314 315 316
		int i = 0;

		for_each_child_of_node(node, np) {
317
			dev_dbg(dev, "\tlink %d:\n", i);
318
			ret = asoc_simple_card_dai_link_of(np, priv,
319
							   i, false);
320 321
			if (ret < 0) {
				of_node_put(np);
322
				goto card_parse_end;
323
			}
324
			i++;
325
		}
326
	} else {
327
		/* For single DAI link & old style of DT node */
328
		ret = asoc_simple_card_dai_link_of(node, priv, 0, true);
329
		if (ret < 0)
330
			goto card_parse_end;
331
	}
332

333
	ret = asoc_simple_card_parse_card_name(card, PREFIX);
334 335 336 337
	if (ret < 0)
		goto card_parse_end;

	ret = asoc_simple_card_parse_aux_devs(node, priv);
338

339 340 341 342
card_parse_end:
	of_node_put(dai_link);

	return ret;
343 344
}

345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
static int asoc_simple_soc_card_probe(struct snd_soc_card *card)
{
	struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
	int ret;

	ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX);
	if (ret < 0)
		return ret;

	ret = asoc_simple_card_init_mic(card, &priv->mic_jack, PREFIX);
	if (ret < 0)
		return ret;

	return 0;
}

361 362
static int asoc_simple_card_probe(struct platform_device *pdev)
{
363
	struct simple_card_data *priv;
364
	struct snd_soc_dai_link *dai_link;
365
	struct simple_dai_props *dai_props;
366
	struct device *dev = &pdev->dev;
367
	struct device_node *np = dev->of_node;
368
	struct snd_soc_card *card;
369
	int num, ret;
370

371
	/* Get the number of DAI links */
372
	if (np && of_get_child_by_name(np, PREFIX "dai-link"))
373
		num = of_get_child_count(np);
374
	else
375
		num = 1;
376

377
	/* Allocate the private data and the DAI link array */
378
	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
379
	if (!priv)
380 381
		return -ENOMEM;

382 383
	dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL);
	dai_link  = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL);
384 385 386 387 388 389
	if (!dai_props || !dai_link)
		return -ENOMEM;

	priv->dai_props			= dai_props;
	priv->dai_link			= dai_link;

390
	/* Init snd_soc_card */
391 392 393 394 395
	card = simple_priv_to_card(priv);
	card->owner		= THIS_MODULE;
	card->dev		= dev;
	card->dai_link		= priv->dai_link;
	card->num_links		= num;
396
	card->probe		= asoc_simple_soc_card_probe;
397

398
	if (np && of_device_is_available(np)) {
399

400
		ret = asoc_simple_card_parse_of(priv);
401 402 403
		if (ret < 0) {
			if (ret != -EPROBE_DEFER)
				dev_err(dev, "parse error %d\n", ret);
404
			goto err;
405
		}
406

407
	} else {
408 409 410 411
		struct asoc_simple_card_info *cinfo;

		cinfo = dev->platform_data;
		if (!cinfo) {
412 413 414
			dev_err(dev, "no info for asoc-simple-card\n");
			return -EINVAL;
		}
415

416 417 418 419
		if (!cinfo->name ||
		    !cinfo->codec_dai.name ||
		    !cinfo->codec ||
		    !cinfo->platform ||
420 421 422 423
		    !cinfo->cpu_dai.name) {
			dev_err(dev, "insufficient asoc_simple_card_info settings\n");
			return -EINVAL;
		}
424

425
		card->name		= (cinfo->card) ? cinfo->card : cinfo->name;
426 427 428 429
		dai_link->name		= cinfo->name;
		dai_link->stream_name	= cinfo->name;
		dai_link->platform_name	= cinfo->platform;
		dai_link->codec_name	= cinfo->codec;
430 431
		dai_link->cpu_dai_name	= cinfo->cpu_dai.name;
		dai_link->codec_dai_name = cinfo->codec_dai.name;
432
		dai_link->dai_fmt	= cinfo->daifmt;
433
		dai_link->init		= asoc_simple_card_dai_init;
434 435 436 437
		memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai,
					sizeof(priv->dai_props->cpu_dai));
		memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
					sizeof(priv->dai_props->codec_dai));
438 439
	}

440
	snd_soc_card_set_drvdata(card, priv);
441

442
	ret = devm_snd_soc_register_card(dev, card);
443 444 445 446
	if (ret < 0)
		goto err;

	return 0;
447
err:
448
	asoc_simple_card_clean_reference(card);
449

450
	return ret;
451 452
}

453 454
static int asoc_simple_card_remove(struct platform_device *pdev)
{
455 456
	struct snd_soc_card *card = platform_get_drvdata(pdev);

457
	return asoc_simple_card_clean_reference(card);
458 459
}

460 461 462 463 464 465
static const struct of_device_id asoc_simple_of_match[] = {
	{ .compatible = "simple-audio-card", },
	{},
};
MODULE_DEVICE_TABLE(of, asoc_simple_of_match);

466 467
static struct platform_driver asoc_simple_card = {
	.driver = {
468
		.name = "asoc-simple-card",
469
		.pm = &snd_soc_pm_ops,
470
		.of_match_table = asoc_simple_of_match,
471
	},
472
	.probe = asoc_simple_card_probe,
473
	.remove = asoc_simple_card_remove,
474 475 476 477
};

module_platform_driver(asoc_simple_card);

478
MODULE_ALIAS("platform:asoc-simple-card");
K
Kuninori Morimoto 已提交
479
MODULE_LICENSE("GPL v2");
480 481
MODULE_DESCRIPTION("ASoC Simple Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");