simple-scu-card.c 8.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 24
	struct snd_soc_card snd_card;
	struct snd_soc_codec_conf codec_conf;
25 26
	struct simple_dai_props {
		struct asoc_simple_dai dai;
27
		struct snd_soc_dai_link_component codecs;
28
		struct snd_soc_dai_link_component platform;
29
	} *dai_props;
30
	struct snd_soc_dai_link *dai_link;
31
	struct asoc_simple_card_data adata;
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
#define PREFIX	"simple-audio-card,"
42

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

50
	return asoc_simple_card_clk_enable(&dai_props->dai);
51 52
}

53
static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
54 55
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
56
	struct simple_card_data *priv =	snd_soc_card_get_drvdata(rtd->card);
57
	struct simple_dai_props *dai_props =
58
		simple_priv_to_props(priv, rtd->num);
59

60
	asoc_simple_card_clk_disable(&dai_props->dai);
61 62
}

63
static const struct snd_soc_ops asoc_simple_card_ops = {
64 65
	.startup = asoc_simple_card_startup,
	.shutdown = asoc_simple_card_shutdown,
66 67
};

68
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
69
{
70
	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
71 72
	struct snd_soc_dai *dai;
	struct snd_soc_dai_link *dai_link;
73
	struct simple_dai_props *dai_props;
74
	int num = rtd->num;
75

76 77
	dai_link	= simple_priv_to_link(priv, num);
	dai_props	= simple_priv_to_props(priv, num);
78 79 80 81
	dai		= dai_link->dynamic ?
				rtd->cpu_dai :
				rtd->codec_dai;

82
	return asoc_simple_card_init_dai(dai, &dai_props->dai);
83 84
}

85
static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
86 87
					struct snd_pcm_hw_params *params)
{
88
	struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
89

90
	asoc_simple_card_convert_fixup(&priv->adata, params);
91 92 93 94

	return 0;
}

95
static int asoc_simple_card_dai_link_of(struct device_node *np,
96
					struct simple_card_data *priv,
97
					unsigned int daifmt,
98
					int idx, bool is_fe)
99
{
100 101
	struct device *dev = simple_priv_to_dev(priv);
	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
102
	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
103
	struct snd_soc_card *card = simple_priv_to_card(priv);
104 105
	int ret;

106
	if (is_fe) {
107
		int is_single_links = 0;
108
		struct snd_soc_dai_link_component *codecs;
109

110
		/* BE is dummy */
111 112 113 114
		codecs			= dai_link->codecs;
		codecs->of_node		= NULL;
		codecs->dai_name	= "snd-soc-dummy-dai";
		codecs->name		= "snd-soc-dummy";
115 116 117 118

		/* FE settings */
		dai_link->dynamic		= 1;
		dai_link->dpcm_merged_format	= 1;
119 120 121 122

		ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
						 &is_single_links);
		if (ret)
123
			return ret;
124

125
		ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, &dai_props->dai);
126 127 128
		if (ret < 0)
			return ret;

129 130 131 132 133
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"fe.%s",
							dai_link->cpu_dai_name);
		if (ret < 0)
			return ret;
134

135
		asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
136 137 138 139 140
	} else {
		/* 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";
141

142 143
		/* BE settings */
		dai_link->no_pcm		= 1;
144
		dai_link->be_hw_params_fixup	= asoc_simple_card_be_hw_params_fixup;
145 146

		ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
147 148
		if (ret < 0)
			return ret;
149

150
		ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, &dai_props->dai);
151 152 153
		if (ret < 0)
			return ret;

154 155
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"be.%s",
156
							dai_link->codecs->dai_name);
157 158 159
		if (ret < 0)
			return ret;

160
		/* check "prefix" from top node */
161
		snd_soc_of_parse_audio_prefix(card,
162
					      &priv->codec_conf,
163
					      dai_link->codecs->of_node,
164
					      PREFIX "prefix");
165 166 167 168 169 170
		/* check "prefix" from each node if top doesn't have */
		if (!priv->codec_conf.of_node)
			snd_soc_of_parse_node_prefix(np,
						     &priv->codec_conf,
						     dai_link->codecs->of_node,
						     "prefix");
171 172
	}

173
	ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai);
174 175 176
	if (ret)
		return ret;

177 178 179 180
	ret = asoc_simple_card_canonicalize_dailink(dai_link);
	if (ret < 0)
		return ret;

181
	dai_link->dai_fmt		= daifmt;
182 183
	dai_link->dpcm_playback		= 1;
	dai_link->dpcm_capture		= 1;
184 185
	dai_link->ops			= &asoc_simple_card_ops;
	dai_link->init			= asoc_simple_card_dai_init;
186

187
	return 0;
188 189
}

190
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
191

192
{
193
	struct device *dev = simple_priv_to_dev(priv);
194
	struct device_node *np;
195
	struct snd_soc_card *card = simple_priv_to_card(priv);
196
	struct device_node *node = dev->of_node;
197 198
	unsigned int daifmt = 0;
	bool is_fe;
199 200 201 202 203
	int ret, i;

	if (!node)
		return -EINVAL;

204 205 206 207
	ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
	if (ret < 0)
		return ret;

208
	ret = asoc_simple_card_of_parse_routing(card, PREFIX);
209 210 211
	if (ret < 0)
		return ret;

212
	asoc_simple_card_parse_convert(dev, node, PREFIX, &priv->adata);
213 214

	/* find 1st codec */
215 216 217 218
	np = of_get_child_by_name(node, PREFIX "codec");
	if (!np)
		return -ENODEV;

219
	ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt);
220 221
	if (ret < 0)
		return ret;
222 223 224 225

	i = 0;
	for_each_child_of_node(node, np) {
		is_fe = false;
226
		if (of_node_name_eq(np, PREFIX "cpu"))
227 228
			is_fe = true;

229
		ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe);
230 231 232 233 234
		if (ret < 0)
			return ret;
		i++;
	}

235
	ret = asoc_simple_card_parse_card_name(card, PREFIX);
236 237
	if (ret < 0)
		return ret;
238 239 240 241

	return 0;
}

242
static int asoc_simple_card_probe(struct platform_device *pdev)
243
{
244
	struct simple_card_data *priv;
245
	struct snd_soc_dai_link *dai_link;
246
	struct simple_dai_props *dai_props;
247
	struct snd_soc_card *card;
248
	struct device *dev = &pdev->dev;
249
	struct device_node *np = dev->of_node;
250
	int num, ret, i;
251 252 253 254 255 256

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

257 258
	num = of_get_child_count(np);

259 260
	dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL);
	dai_link  = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL);
261
	if (!dai_props || !dai_link)
262 263
		return -ENOMEM;

264 265 266 267 268 269 270 271 272
	/*
	 * 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()
	 */
	for (i = 0; i < num; i++) {
		dai_link[i].codecs	= &dai_props[i].codecs;
		dai_link[i].num_codecs	= 1;
273
		dai_link[i].platform	= &dai_props[i].platform;
274 275
	}

276 277
	priv->dai_props				= dai_props;
	priv->dai_link				= dai_link;
278 279

	/* Init snd_soc_card */
280 281 282 283 284 285 286
	card = simple_priv_to_card(priv);
	card->owner		= THIS_MODULE;
	card->dev		= dev;
	card->dai_link		= priv->dai_link;
	card->num_links		= num;
	card->codec_conf	= &priv->codec_conf;
	card->num_configs	= 1;
287

288
	ret = asoc_simple_card_parse_of(priv);
289 290 291 292 293 294
	if (ret < 0) {
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "parse error %d\n", ret);
		goto err;
	}

295
	snd_soc_card_set_drvdata(card, priv);
296

297
	ret = devm_snd_soc_register_card(dev, card);
298 299 300 301
	if (ret < 0)
		goto err;

	return 0;
302
err:
303
	asoc_simple_card_clean_reference(card);
304 305 306 307

	return ret;
}

308
static int asoc_simple_card_remove(struct platform_device *pdev)
309 310 311
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);

312
	return asoc_simple_card_clean_reference(card);
313 314
}

315 316 317 318 319 320 321
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);

322
static struct platform_driver asoc_simple_card = {
323
	.driver = {
324
		.name = "simple-scu-audio-card",
325
		.pm = &snd_soc_pm_ops,
326
		.of_match_table = asoc_simple_of_match,
327
	},
328 329
	.probe = asoc_simple_card_probe,
	.remove = asoc_simple_card_remove,
330 331
};

332
module_platform_driver(asoc_simple_card);
333

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