simple-card.c 10.7 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 26 27
	struct simple_dai_props {
		struct asoc_simple_dai cpu_dai;
		struct asoc_simple_dai codec_dai;
	} *dai_props;
	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
28 29
};

30
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
31
				       struct asoc_simple_dai *set)
32
{
33
	int ret;
34

35 36
	if (set->fmt) {
		ret = snd_soc_dai_set_fmt(dai, set->fmt);
37 38 39 40
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_fmt error\n");
			goto err;
		}
41 42
	}

43
	if (set->sysclk) {
44
		ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
45 46 47 48 49 50
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_sysclk error\n");
			goto err;
		}
	}

51 52 53 54 55 56 57 58 59 60
	if (set->slots) {
		ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
						set->slots,
						set->slot_width);
		if (ret && ret != -ENOTSUPP) {
			dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
			goto err;
		}
	}

61
	ret = 0;
62

63
err:
64 65 66
	return ret;
}

67 68
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
69
	struct simple_card_data *priv =
70
				snd_soc_card_get_drvdata(rtd->card);
71 72
	struct snd_soc_dai *codec = rtd->codec_dai;
	struct snd_soc_dai *cpu = rtd->cpu_dai;
73 74
	struct simple_dai_props *dai_props;
	int num, ret;
75

76 77 78
	num = rtd - rtd->card->rtd;
	dai_props = &priv->dai_props[num];
	ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
79 80
	if (ret < 0)
		return ret;
81

82
	ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai);
83 84
	if (ret < 0)
		return ret;
85 86 87 88

	return 0;
}

89 90
static int
asoc_simple_card_sub_parse_of(struct device_node *np,
91
			      unsigned int daifmt,
92
			      struct asoc_simple_dai *dai,
93 94
			      const struct device_node **p_node,
			      const char **name)
95
{
96
	struct device_node *node;
97 98 99 100 101 102 103
	struct clk *clk;
	int ret;

	/*
	 * get node via "sound-dai = <&phandle port>"
	 * it will be used as xxx_of_node on soc_bind_dai_link()
	 */
104 105
	node = of_parse_phandle(np, "sound-dai", 0);
	if (!node)
106
		return -ENODEV;
107
	*p_node = node;
108 109

	/* get dai->name */
110
	ret = snd_soc_of_get_dai_name(np, name);
111
	if (ret < 0)
112
		return ret;
113

114 115 116
	/* parse TDM slot */
	ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
	if (ret)
117
		return ret;
118

119 120 121 122 123
	/*
	 * bitclock-inversion, frame-inversion
	 * bitclock-master,    frame-master
	 * and specific "format" if it has
	 */
124
	dai->fmt = snd_soc_of_parse_daifmt(np, NULL, NULL, NULL);
125
	dai->fmt |= daifmt;
126 127 128 129 130

	/*
	 * dai->sysclk come from
	 *  "clocks = <&xxx>" (if system has common clock)
	 *  or "system-clock-frequency = <xxx>"
131
	 *  or device's module clock.
132
	 */
133 134 135 136
	if (of_property_read_bool(np, "clocks")) {
		clk = of_clk_get(np, 0);
		if (IS_ERR(clk)) {
			ret = PTR_ERR(clk);
137
			return ret;
138 139 140 141
		}

		dai->sysclk = clk_get_rate(clk);
	} else if (of_property_read_bool(np, "system-clock-frequency")) {
142 143 144
		of_property_read_u32(np,
				     "system-clock-frequency",
				     &dai->sysclk);
145
	} else {
146
		clk = of_clk_get(node, 0);
147 148
		if (!IS_ERR(clk))
			dai->sysclk = clk_get_rate(clk);
149
	}
150

151
	return 0;
152 153
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
static int simple_card_cpu_codec_of(struct device_node *node,
				int daifmt,
				struct snd_soc_dai_link *dai_link,
				struct simple_dai_props *dai_props)
{
	struct device_node *np;
	int ret;

	/* CPU sub-node */
	ret = -EINVAL;
	np = of_get_child_by_name(node, "simple-audio-card,cpu");
	if (np) {
		ret = asoc_simple_card_sub_parse_of(np, daifmt,
						&dai_props->cpu_dai,
						&dai_link->cpu_of_node,
						&dai_link->cpu_dai_name);
		of_node_put(np);
	}
	if (ret < 0)
		return ret;

	/* CODEC sub-node */
	ret = -EINVAL;
	np = of_get_child_by_name(node, "simple-audio-card,codec");
	if (np) {
		ret = asoc_simple_card_sub_parse_of(np, daifmt,
						&dai_props->codec_dai,
						&dai_link->codec_of_node,
						&dai_link->codec_dai_name);
		of_node_put(np);
	}
	return ret;
}

188
static int asoc_simple_card_parse_of(struct device_node *node,
189
				     struct simple_card_data *priv,
190 191
				     struct device *dev,
				     int multi)
192
{
193
	struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
194
	struct simple_dai_props *dai_props = priv->dai_props;
195 196
	struct device_node *np;
	char *name;
197
	unsigned int daifmt;
198
	int ret;
199

200 201 202
	/* parsing the card name from DT */
	snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");

203
	/* get CPU/CODEC common format via simple-audio-card,format */
204 205
	daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,", NULL,
					 NULL) &
206 207
		(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);

208 209 210 211 212 213 214 215
	/* off-codec widgets */
	if (of_property_read_bool(node, "simple-audio-card,widgets")) {
		ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
					"simple-audio-card,widgets");
		if (ret)
			return ret;
	}

216
	/* DAPM routes */
217
	if (of_property_read_bool(node, "simple-audio-card,routing")) {
218
		ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
219
					"simple-audio-card,routing");
220 221 222
		if (ret)
			return ret;
	}
223

224 225 226 227 228 229 230 231
	/* loop on the DAI links */
	np = NULL;
	for (;;) {
		if (multi) {
			np = of_get_next_child(node, np);
			if (!np)
				break;
		}
232

233 234 235 236
		ret = simple_card_cpu_codec_of(multi ? np : node,
					daifmt, dai_link, dai_props);
		if (ret < 0)
			goto err;
237

238 239 240 241 242 243 244 245 246 247 248 249 250
		/*
		 * overwrite cpu_dai->fmt as its DAIFMT_MASTER bit is based on CODEC
		 * while the other bits should be identical unless buggy SW/HW design.
		 */
		dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt;

		if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
			ret = -EINVAL;
			goto err;
		}

		/* simple-card assumes platform == cpu */
		dai_link->platform_of_node = dai_link->cpu_of_node;
251

252 253 254 255 256 257 258 259 260 261 262 263 264 265
		name = devm_kzalloc(dev,
				    strlen(dai_link->cpu_dai_name)   +
				    strlen(dai_link->codec_dai_name) + 2,
				    GFP_KERNEL);
		sprintf(name, "%s-%s", dai_link->cpu_dai_name,
					dai_link->codec_dai_name);
		dai_link->name = dai_link->stream_name = name;

		if (!multi)
			break;

		dai_link++;
		dai_props++;
	}
266

267
	/* card name is created from CPU/CODEC dai name */
268
	dai_link = priv->snd_card.dai_link;
269
	if (!priv->snd_card.name)
270
		priv->snd_card.name = dai_link->name;
271

272
	dev_dbg(dev, "card-name : %s\n", priv->snd_card.name);
273
	dev_dbg(dev, "platform : %04x\n", daifmt);
274
	dai_props = priv->dai_props;
275
	dev_dbg(dev, "cpu : %s / %04x / %d\n",
276
		dai_link->cpu_dai_name,
277 278
		dai_props->cpu_dai.fmt,
		dai_props->cpu_dai.sysclk);
279
	dev_dbg(dev, "codec : %s / %04x / %d\n",
280
		dai_link->codec_dai_name,
281 282
		dai_props->codec_dai.fmt,
		dai_props->codec_dai.sysclk);
283

284
	return 0;
285 286 287 288

err:
	of_node_put(np);
	return ret;
289 290
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
/* update the reference count of the devices nodes at end of probe */
static int asoc_simple_card_unref(struct platform_device *pdev)
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);
	struct snd_soc_dai_link *dai_link;
	struct device_node *np;
	int num_links;

	for (num_links = 0, dai_link = card->dai_link;
	     num_links < card->num_links;
	     num_links++, dai_link++) {
		np = (struct device_node *) dai_link->cpu_of_node;
		if (np)
			of_node_put(np);
		np = (struct device_node *) dai_link->codec_of_node;
		if (np)
			of_node_put(np);
	}
	return 0;
}

312 313
static int asoc_simple_card_probe(struct platform_device *pdev)
{
314
	struct simple_card_data *priv;
315
	struct snd_soc_dai_link *dai_link;
316
	struct device_node *np = pdev->dev.of_node;
317
	struct device *dev = &pdev->dev;
318 319 320 321 322 323 324 325 326 327
	int num_links, multi, ret;

	/* get the number of DAI links */
	if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) {
		num_links = of_get_child_count(np);
		multi = 1;
	} else {
		num_links = 1;
		multi = 0;
	}
328

329 330
	/* allocate the private data and the DAI link array */
	priv = devm_kzalloc(dev,
331
			sizeof(*priv) + sizeof(*dai_link) * num_links,
332
			GFP_KERNEL);
333
	if (!priv)
334 335
		return -ENOMEM;

336 337 338
	/*
	 * init snd_soc_card
	 */
339 340
	priv->snd_card.owner = THIS_MODULE;
	priv->snd_card.dev = dev;
341
	dai_link = priv->dai_link;
342
	priv->snd_card.dai_link = dai_link;
343
	priv->snd_card.num_links = num_links;
344

345 346
	/* get room for the other properties */
	priv->dai_props = devm_kzalloc(dev,
347
			sizeof(*priv->dai_props) * num_links,
348 349 350 351
			GFP_KERNEL);
	if (!priv->dai_props)
		return -ENOMEM;

352
	if (np && of_device_is_available(np)) {
353

354
		ret = asoc_simple_card_parse_of(np, priv, dev, multi);
355 356 357
		if (ret < 0) {
			if (ret != -EPROBE_DEFER)
				dev_err(dev, "parse error %d\n", ret);
358
			goto err;
359
		}
360 361 362 363 364 365 366 367 368 369 370 371 372

		/*
		 * soc_bind_dai_link() will check cpu name
		 * after of_node matching if dai_link has cpu_dai_name.
		 * but, it will never match if name was created by fmt_single_name()
		 * remove cpu_dai_name to escape name matching.
		 * see
		 *	fmt_single_name()
		 *	fmt_multiple_name()
		 */
		if (num_links == 1)
			dai_link->cpu_dai_name = NULL;

373
	} else {
374 375 376 377
		struct asoc_simple_card_info *cinfo;

		cinfo = dev->platform_data;
		if (!cinfo) {
378 379 380
			dev_err(dev, "no info for asoc-simple-card\n");
			return -EINVAL;
		}
381

382 383 384 385 386 387 388 389
		if (!cinfo->name	||
		    !cinfo->codec_dai.name	||
		    !cinfo->codec	||
		    !cinfo->platform	||
		    !cinfo->cpu_dai.name) {
			dev_err(dev, "insufficient asoc_simple_card_info settings\n");
			return -EINVAL;
		}
390

391
		priv->snd_card.name	= (cinfo->card) ? cinfo->card : cinfo->name;
392 393 394 395
		dai_link->name		= cinfo->name;
		dai_link->stream_name	= cinfo->name;
		dai_link->platform_name	= cinfo->platform;
		dai_link->codec_name	= cinfo->codec;
396 397
		dai_link->cpu_dai_name	= cinfo->cpu_dai.name;
		dai_link->codec_dai_name = cinfo->codec_dai.name;
398 399 400 401
		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));
402

403 404
		priv->dai_props->cpu_dai.fmt	|= cinfo->daifmt;
		priv->dai_props->codec_dai.fmt	|= cinfo->daifmt;
405 406 407 408 409
	}

	/*
	 * init snd_soc_dai_link
	 */
410
	dai_link->init = asoc_simple_card_dai_init;
411

412
	snd_soc_card_set_drvdata(&priv->snd_card, priv);
413

414 415 416 417 418
	ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);

err:
	asoc_simple_card_unref(pdev);
	return ret;
419 420
}

421 422 423 424 425 426
static const struct of_device_id asoc_simple_of_match[] = {
	{ .compatible = "simple-audio-card", },
	{},
};
MODULE_DEVICE_TABLE(of, asoc_simple_of_match);

427 428 429
static struct platform_driver asoc_simple_card = {
	.driver = {
		.name	= "asoc-simple-card",
430
		.owner = THIS_MODULE,
431
		.of_match_table = asoc_simple_of_match,
432 433 434 435 436 437
	},
	.probe		= asoc_simple_card_probe,
};

module_platform_driver(asoc_simple_card);

438
MODULE_ALIAS("platform:asoc-simple-card");
439 440 441
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ASoC Simple Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");