simple-scu-card.c 7.9 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 27
	struct simple_dai_props {
		struct asoc_simple_dai dai;
	} *dai_props;
28
	struct snd_soc_dai_link *dai_link;
29
	struct asoc_simple_card_data adata;
30 31
};

32
#define simple_priv_to_card(priv) (&(priv)->snd_card)
33
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
34 35
#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))
36

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

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

48
	return asoc_simple_card_clk_enable(&dai_props->dai);
49 50
}

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

58
	asoc_simple_card_clk_disable(&dai_props->dai);
59 60
}

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

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

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

80
	return asoc_simple_card_init_dai(dai, &dai_props->dai);
81 82
}

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

88
	asoc_simple_card_convert_fixup(&priv->adata, params);
89 90 91 92

	return 0;
}

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

104
	if (is_fe) {
105 106
		int is_single_links = 0;

107 108 109 110 111 112 113 114
		/* BE is dummy */
		dai_link->codec_of_node		= NULL;
		dai_link->codec_dai_name	= "snd-soc-dummy-dai";
		dai_link->codec_name		= "snd-soc-dummy";

		/* FE settings */
		dai_link->dynamic		= 1;
		dai_link->dpcm_merged_format	= 1;
115 116 117 118

		ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
						 &is_single_links);
		if (ret)
119
			return ret;
120

121
		ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, &dai_props->dai);
122 123 124
		if (ret < 0)
			return ret;

125 126 127 128 129
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"fe.%s",
							dai_link->cpu_dai_name);
		if (ret < 0)
			return ret;
130

131
		asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
132 133 134 135 136
	} 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";
137

138 139
		/* BE settings */
		dai_link->no_pcm		= 1;
140
		dai_link->be_hw_params_fixup	= asoc_simple_card_be_hw_params_fixup;
141 142

		ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
143 144
		if (ret < 0)
			return ret;
145

146
		ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, &dai_props->dai);
147 148 149
		if (ret < 0)
			return ret;

150 151 152 153 154 155
		ret = asoc_simple_card_set_dailink_name(dev, dai_link,
							"be.%s",
							dai_link->codec_dai_name);
		if (ret < 0)
			return ret;

156
		snd_soc_of_parse_audio_prefix(card,
157 158 159
					      &priv->codec_conf,
					      dai_link->codec_of_node,
					      PREFIX "prefix");
160 161
	}

162
	ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai);
163 164 165
	if (ret)
		return ret;

166 167 168 169
	ret = asoc_simple_card_canonicalize_dailink(dai_link);
	if (ret < 0)
		return ret;

170
	dai_link->dai_fmt		= daifmt;
171 172
	dai_link->dpcm_playback		= 1;
	dai_link->dpcm_capture		= 1;
173 174
	dai_link->ops			= &asoc_simple_card_ops;
	dai_link->init			= asoc_simple_card_dai_init;
175

176
	return 0;
177 178
}

179
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
180

181
{
182
	struct device *dev = simple_priv_to_dev(priv);
183
	struct device_node *np;
184
	struct snd_soc_card *card = simple_priv_to_card(priv);
185
	struct device_node *node = dev->of_node;
186 187
	unsigned int daifmt = 0;
	bool is_fe;
188 189 190 191 192
	int ret, i;

	if (!node)
		return -EINVAL;

193 194 195 196
	ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
	if (ret < 0)
		return ret;

197
	ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0);
198 199 200
	if (ret < 0)
		return ret;

201
	asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata);
202 203

	/* find 1st codec */
204 205 206 207
	np = of_get_child_by_name(node, PREFIX "codec");
	if (!np)
		return -ENODEV;

208
	ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt);
209 210
	if (ret < 0)
		return ret;
211 212 213 214

	i = 0;
	for_each_child_of_node(node, np) {
		is_fe = false;
215
		if (strcmp(np->name, PREFIX "cpu") == 0)
216 217
			is_fe = true;

218
		ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe);
219 220 221 222 223
		if (ret < 0)
			return ret;
		i++;
	}

224
	ret = asoc_simple_card_parse_card_name(card, PREFIX);
225 226
	if (ret < 0)
		return ret;
227 228 229 230

	return 0;
}

231
static int asoc_simple_card_probe(struct platform_device *pdev)
232
{
233
	struct simple_card_data *priv;
234
	struct snd_soc_dai_link *dai_link;
235
	struct simple_dai_props *dai_props;
236
	struct snd_soc_card *card;
237
	struct device *dev = &pdev->dev;
238
	struct device_node *np = dev->of_node;
239
	int num, ret;
240 241 242 243 244 245

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

246 247
	num = of_get_child_count(np);

248 249
	dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL);
	dai_link  = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL);
250
	if (!dai_props || !dai_link)
251 252
		return -ENOMEM;

253 254
	priv->dai_props				= dai_props;
	priv->dai_link				= dai_link;
255 256

	/* Init snd_soc_card */
257 258 259 260 261 262 263
	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;
264

265
	ret = asoc_simple_card_parse_of(priv);
266 267 268 269 270 271
	if (ret < 0) {
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "parse error %d\n", ret);
		goto err;
	}

272
	snd_soc_card_set_drvdata(card, priv);
273

274
	ret = devm_snd_soc_register_card(dev, card);
275 276 277 278
	if (ret < 0)
		goto err;

	return 0;
279
err:
280
	asoc_simple_card_clean_reference(card);
281 282 283 284

	return ret;
}

285
static int asoc_simple_card_remove(struct platform_device *pdev)
286 287 288
{
	struct snd_soc_card *card = platform_get_drvdata(pdev);

289
	return asoc_simple_card_clean_reference(card);
290 291
}

292 293 294 295 296 297 298
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);

299
static struct platform_driver asoc_simple_card = {
300
	.driver = {
301
		.name = "simple-scu-audio-card",
302
		.pm = &snd_soc_pm_ops,
303
		.of_match_table = asoc_simple_of_match,
304
	},
305 306
	.probe = asoc_simple_card_probe,
	.remove = asoc_simple_card_remove,
307 308
};

309
module_platform_driver(asoc_simple_card);
310

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