simple-scu-card.c 11.7 KB
Newer Older
1 2 3 4 5 6 7 8 9
// SPDX-License-Identifier: GPL-2.0
//
// ASoC simple SCU sound card support
//
// Copyright (C) 2015 Renesas Solutions Corp.
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
// based on ${LINUX}/sound/soc/generic/simple-card.c

10 11 12 13 14 15 16 17 18 19
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
20
#include <sound/simple_card_utils.h>
21

22
struct simple_card_data {
23
	struct snd_soc_card snd_card;
24
	struct simple_dai_props {
25 26
		struct asoc_simple_dai *cpu_dai;
		struct asoc_simple_dai *codec_dai;
27
		struct snd_soc_dai_link_component codecs;
28
		struct snd_soc_dai_link_component platform;
29
		struct asoc_simple_card_data adata;
30
		struct snd_soc_codec_conf *codec_conf;
31
	} *dai_props;
32
	struct snd_soc_dai_link *dai_link;
33
	struct asoc_simple_dai *dais;
34
	struct asoc_simple_card_data adata;
35
	struct snd_soc_codec_conf *codec_conf;
36 37
};

38
#define simple_priv_to_card(priv) (&(priv)->snd_card)
39
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
40 41
#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))
42

43 44
#define DAI	"sound-dai"
#define CELL	"#sound-dai-cells"
45
#define PREFIX	"simple-audio-card,"
46

47
static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
48 49
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
50
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
51
	struct simple_dai_props *dai_props =
52
		simple_priv_to_props(priv, rtd->num);
53 54 55 56 57 58 59 60 61
	int ret;

	ret = asoc_simple_card_clk_enable(dai_props->cpu_dai);
	if (ret)
		return ret;

	ret = asoc_simple_card_clk_enable(dai_props->codec_dai);
	if (ret)
		asoc_simple_card_clk_disable(dai_props->cpu_dai);
62

63
	return ret;
64 65
}

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

73 74 75
	asoc_simple_card_clk_disable(dai_props->cpu_dai);

	asoc_simple_card_clk_disable(dai_props->codec_dai);
76 77
}

78
static const struct snd_soc_ops asoc_simple_card_ops = {
79 80
	.startup = asoc_simple_card_startup,
	.shutdown = asoc_simple_card_shutdown,
81 82
};

83
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
84
{
85 86 87 88 89 90 91 92
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
	int ret;

	ret = asoc_simple_card_init_dai(rtd->codec_dai,
					dai_props->codec_dai);
	if (ret < 0)
		return ret;
93

94 95 96 97
	ret = asoc_simple_card_init_dai(rtd->cpu_dai,
					dai_props->cpu_dai);
	if (ret < 0)
		return ret;
98

99
	return 0;
100 101
}

102
static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
103 104
					struct snd_pcm_hw_params *params)
{
105
	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
106
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num);
107

108 109 110
	asoc_simple_card_convert_fixup(&dai_props->adata, params);

	/* overwrite by top level adata if exist */
111
	asoc_simple_card_convert_fixup(&priv->adata, params);
112 113 114 115

	return 0;
}

116 117 118
static int asoc_simple_card_dai_link_of(struct device_node *link,
					struct device_node *np,
					struct device_node *codec,
119
					struct simple_card_data *priv,
120 121
					int *dai_idx, int link_idx,
					int *conf_idx, int is_fe,
122
					bool is_top_level_node)
123
{
124
	struct device *dev = simple_priv_to_dev(priv);
125 126
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx);
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx);
127
	struct snd_soc_card *card = simple_priv_to_card(priv);
128
	struct asoc_simple_dai *dai;
129
	char *prefix = "";
130 131
	int ret;

132 133 134 135
	/* For single DAI link & old style of DT node */
	if (is_top_level_node)
		prefix = PREFIX;

136
	if (is_fe) {
137
		int is_single_links = 0;
138
		struct snd_soc_dai_link_component *codecs;
139

140
		/* BE is dummy */
141 142 143 144
		codecs			= dai_link->codecs;
		codecs->of_node		= NULL;
		codecs->dai_name	= "snd-soc-dummy-dai";
		codecs->name		= "snd-soc-dummy";
145 146 147 148

		/* FE settings */
		dai_link->dynamic		= 1;
		dai_link->dpcm_merged_format	= 1;
149

150 151 152
		dai =
		dai_props->cpu_dai	= &priv->dais[(*dai_idx)++];

153 154 155
		ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
						 &is_single_links);
		if (ret)
156
			return ret;
157

158
		ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai);
159 160 161
		if (ret < 0)
			return ret;

162 163 164 165 166
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"fe.%s",
							dai_link->cpu_dai_name);
		if (ret < 0)
			return ret;
167

168
		asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
169
	} else {
170 171
		struct snd_soc_codec_conf *cconf;

172 173 174 175
		/* FE is dummy */
		dai_link->cpu_of_node		= NULL;
		dai_link->cpu_dai_name		= "snd-soc-dummy-dai";
		dai_link->cpu_name		= "snd-soc-dummy";
176

177 178
		/* BE settings */
		dai_link->no_pcm		= 1;
179
		dai_link->be_hw_params_fixup	= asoc_simple_card_be_hw_params_fixup;
180

181 182 183
		dai =
		dai_props->codec_dai	= &priv->dais[(*dai_idx)++];

184 185 186
		cconf =
		dai_props->codec_conf	= &priv->codec_conf[(*conf_idx)++];

187
		ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
188 189
		if (ret < 0)
			return ret;
190

191
		ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai);
192 193 194
		if (ret < 0)
			return ret;

195 196
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"be.%s",
197
							dai_link->codecs->dai_name);
198 199 200
		if (ret < 0)
			return ret;

201
		/* check "prefix" from top node */
202
		snd_soc_of_parse_audio_prefix(card, cconf,
203
					      dai_link->codecs->of_node,
204
					      PREFIX "prefix");
205
		/* check "prefix" from each node if top doesn't have */
206 207
		if (!cconf->of_node)
			snd_soc_of_parse_node_prefix(np, cconf,
208 209
						     dai_link->codecs->of_node,
						     "prefix");
210 211
	}

212 213
	asoc_simple_card_parse_convert(dev, link, prefix, &dai_props->adata);

214
	ret = asoc_simple_card_of_parse_tdm(np, dai);
215 216 217
	if (ret)
		return ret;

218 219 220 221
	ret = asoc_simple_card_canonicalize_dailink(dai_link);
	if (ret < 0)
		return ret;

222 223 224 225 226
	ret = asoc_simple_card_parse_daifmt(dev, link, codec,
					    prefix, &dai_link->dai_fmt);
	if (ret < 0)
		return ret;

227 228
	dai_link->dpcm_playback		= 1;
	dai_link->dpcm_capture		= 1;
229 230
	dai_link->ops			= &asoc_simple_card_ops;
	dai_link->init			= asoc_simple_card_dai_init;
231

232
	return 0;
233 234
}

235
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
236

237
{
238
	struct device *dev = simple_priv_to_dev(priv);
239 240
	struct device_node *top = dev->of_node;
	struct device_node *node;
241
	struct device_node *np;
242
	struct device_node *codec;
243
	struct snd_soc_card *card = simple_priv_to_card(priv);
244
	bool is_fe;
245
	int ret, loop;
246
	int dai_idx, link_idx, conf_idx;
247

248
	if (!top)
249 250
		return -EINVAL;

251 252 253 254
	ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
	if (ret < 0)
		return ret;

255
	ret = asoc_simple_card_of_parse_routing(card, PREFIX);
256 257 258
	if (ret < 0)
		return ret;

259
	asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata);
260

261
	loop = 1;
262 263
	link_idx = 0;
	dai_idx = 0;
264
	conf_idx = 0;
265 266 267 268
	node = of_get_child_by_name(top, PREFIX "dai-link");
	if (!node) {
		node = dev->of_node;
		loop = 0;
269 270
	}

271 272 273 274 275 276 277 278 279 280
	do  {
		codec = of_get_child_by_name(node,
					     loop ? "codec" : PREFIX "codec");
		if (!codec)
			return -ENODEV;

		for_each_child_of_node(node, np) {
			is_fe = (np != codec);

			ret = asoc_simple_card_dai_link_of(node, np, codec, priv,
281
							   &dai_idx, link_idx++,
282
							   &conf_idx,
283
							   is_fe, !loop);
284 285 286 287 288 289
			if (ret < 0)
				return ret;
		}
		node = of_get_next_child(top, node);
	} while (loop && node);

290
	ret = asoc_simple_card_parse_card_name(card, PREFIX);
291 292
	if (ret < 0)
		return ret;
293 294 295 296

	return 0;
}

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
static void asoc_simple_card_get_dais_count(struct device *dev,
					    int *link_num,
					    int *dais_num,
					    int *ccnf_num)
{
	struct device_node *top = dev->of_node;
	struct device_node *node;
	int loop;
	int num;

	/*
	 * link_num :	number of links.
	 *		CPU-Codec / CPU-dummy / dummy-Codec
	 * dais_num :	number of DAIs
	 * ccnf_num :	number of codec_conf
	 *		same number for "dummy-Codec"
	 *
	 * ex1)
	 * CPU0 --- Codec0	link : 5
	 * CPU1 --- Codec1	dais : 7
	 * CPU2 -/		ccnf : 1
	 * CPU3 --- Codec2
	 *
	 *	=> 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec
	 *	=> 7 DAIs  = 4xCPU + 3xCodec
	 *	=> 1 ccnf  = 1xdummy-Codec
	 *
	 * ex2)
	 * CPU0 --- Codec0	link : 5
	 * CPU1 --- Codec1	dais : 6
	 * CPU2 -/		ccnf : 1
	 * CPU3 -/
	 *
	 *	=> 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec
	 *	=> 6 DAIs  = 4xCPU + 2xCodec
	 *	=> 1 ccnf  = 1xdummy-Codec
	 *
	 * ex3)
	 * CPU0 --- Codec0	link : 6
	 * CPU1 -/		dais : 6
	 * CPU2 --- Codec1	ccnf : 2
	 * CPU3 -/
	 *
	 *	=> 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec
	 *	=> 6 DAIs  = 4xCPU + 2xCodec
	 *	=> 2 ccnf  = 2xdummy-Codec
	 */
	if (!top) {
		(*link_num) = 1;
		(*dais_num) = 2;
		(*ccnf_num) = 0;
		return;
	}

	loop = 1;
	node = of_get_child_by_name(top, PREFIX "dai-link");
	if (!node) {
		node = top;
		loop = 0;
	}

	do {
		num = of_get_child_count(node);
		(*dais_num) += num;
		if (num > 2) {
			(*link_num) += num;
			(*ccnf_num)++;
		} else {
			(*link_num)++;
		}
		node = of_get_next_child(top, node);
	} while (loop && node);
}

371
static int asoc_simple_card_probe(struct platform_device *pdev)
372
{
373
	struct simple_card_data *priv;
374
	struct snd_soc_dai_link *dai_link;
375
	struct simple_dai_props *dai_props;
376
	struct asoc_simple_dai *dais;
377
	struct snd_soc_card *card;
378
	struct snd_soc_codec_conf *cconf;
379
	struct device *dev = &pdev->dev;
380 381
	int ret, i;
	int lnum = 0, dnum = 0, cnum = 0;
382 383 384 385 386 387

	/* Allocate the private data */
	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

388 389 390
	asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum);
	if (!lnum || !dnum)
		return -EINVAL;
391

392 393
	dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL);
	dai_link  = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL);
394
	dais      = devm_kcalloc(dev, dnum, sizeof(*dais),      GFP_KERNEL);
395
	cconf     = devm_kcalloc(dev, cnum, sizeof(*cconf),     GFP_KERNEL);
396
	if (!dai_props || !dai_link || !dais)
397 398
		return -ENOMEM;

399 400 401 402 403 404
	/*
	 * Use snd_soc_dai_link_component instead of legacy style
	 * It is codec only. but cpu/platform will be supported in the future.
	 * see
	 *	soc-core.c :: snd_soc_init_multicodec()
	 */
405
	for (i = 0; i < lnum; i++) {
406 407
		dai_link[i].codecs	= &dai_props[i].codecs;
		dai_link[i].num_codecs	= 1;
408
		dai_link[i].platform	= &dai_props[i].platform;
409 410
	}

411 412
	priv->dai_props				= dai_props;
	priv->dai_link				= dai_link;
413
	priv->dais				= dais;
414
	priv->codec_conf			= cconf;
415 416

	/* Init snd_soc_card */
417 418 419 420
	card = simple_priv_to_card(priv);
	card->owner		= THIS_MODULE;
	card->dev		= dev;
	card->dai_link		= priv->dai_link;
421
	card->num_links		= lnum;
422 423
	card->codec_conf	= cconf;
	card->num_configs	= cnum;
424

425
	ret = asoc_simple_card_parse_of(priv);
426 427 428 429 430 431
	if (ret < 0) {
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "parse error %d\n", ret);
		goto err;
	}

432
	snd_soc_card_set_drvdata(card, priv);
433

434
	ret = devm_snd_soc_register_card(dev, card);
435 436 437 438
	if (ret < 0)
		goto err;

	return 0;
439
err:
440
	asoc_simple_card_clean_reference(card);
441 442 443 444

	return ret;
}

445
static int asoc_simple_card_remove(struct platform_device *pdev)
446 447 448
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);

449
	return asoc_simple_card_clean_reference(card);
450 451
}

452 453 454 455 456 457 458
static const struct of_device_id asoc_simple_of_match[] = {
	{ .compatible = "renesas,rsrc-card", },
	{ .compatible = "simple-scu-audio-card", },
	{},
};
MODULE_DEVICE_TABLE(of, asoc_simple_of_match);

459
static struct platform_driver asoc_simple_card = {
460
	.driver = {
461
		.name = "simple-scu-audio-card",
462
		.pm = &snd_soc_pm_ops,
463
		.of_match_table = asoc_simple_of_match,
464
	},
465 466
	.probe = asoc_simple_card_probe,
	.remove = asoc_simple_card_remove,
467 468
};

469
module_platform_driver(asoc_simple_card);
470

471
MODULE_ALIAS("platform:asoc-simple-scu-card");
472
MODULE_LICENSE("GPL v2");
473
MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
474
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");