ASoC: simple-scu-card: tidyup asoc_simple_card_parse_links() method
[deliverable/linux.git] / sound / soc / generic / simple-scu-card.c
1 /*
2 * ASoC simple SCU sound card support
3 *
4 * Copyright (C) 2015 Renesas Solutions Corp.
5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6 *
7 * based on ${LINUX}/sound/soc/generic/simple-card.c
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13 #include <linux/clk.h>
14 #include <linux/device.h>
15 #include <linux/module.h>
16 #include <linux/of.h>
17 #include <linux/of_device.h>
18 #include <linux/platform_device.h>
19 #include <linux/string.h>
20 #include <sound/jack.h>
21 #include <sound/soc.h>
22 #include <sound/soc-dai.h>
23 #include <sound/simple_card_utils.h>
24
25 static const struct of_device_id asoc_simple_card_of_match[] = {
26 { .compatible = "renesas,rsrc-card", },
27 { .compatible = "simple-scu-audio-card", },
28 {},
29 };
30 MODULE_DEVICE_TABLE(of, asoc_simple_card_of_match);
31
32 struct asoc_simple_card_priv {
33 struct snd_soc_card snd_card;
34 struct snd_soc_codec_conf codec_conf;
35 struct asoc_simple_dai *dai_props;
36 struct snd_soc_dai_link *dai_link;
37 u32 convert_rate;
38 u32 convert_channels;
39 };
40
41 #define simple_priv_to_dev(priv) ((priv)->snd_card.dev)
42 #define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
43 #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
44
45 #define DAI "sound-dai"
46 #define CELL "#sound-dai-cells"
47 #define PREFIX "simple-audio-card,"
48
49 static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
50 {
51 struct snd_soc_pcm_runtime *rtd = substream->private_data;
52 struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
53 struct asoc_simple_dai *dai_props =
54 simple_priv_to_props(priv, rtd->num);
55
56 return clk_prepare_enable(dai_props->clk);
57 }
58
59 static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
60 {
61 struct snd_soc_pcm_runtime *rtd = substream->private_data;
62 struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
63 struct asoc_simple_dai *dai_props =
64 simple_priv_to_props(priv, rtd->num);
65
66 clk_disable_unprepare(dai_props->clk);
67 }
68
69 static struct snd_soc_ops asoc_simple_card_ops = {
70 .startup = asoc_simple_card_startup,
71 .shutdown = asoc_simple_card_shutdown,
72 };
73
74 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
75 {
76 struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
77 struct snd_soc_dai *dai;
78 struct snd_soc_dai_link *dai_link;
79 struct asoc_simple_dai *dai_props;
80 int num = rtd->num;
81
82 dai_link = simple_priv_to_link(priv, num);
83 dai_props = simple_priv_to_props(priv, num);
84 dai = dai_link->dynamic ?
85 rtd->cpu_dai :
86 rtd->codec_dai;
87
88 return asoc_simple_card_init_dai(dai, dai_props);
89 }
90
91 static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
92 struct snd_pcm_hw_params *params)
93 {
94 struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
95 struct snd_interval *rate = hw_param_interval(params,
96 SNDRV_PCM_HW_PARAM_RATE);
97 struct snd_interval *channels = hw_param_interval(params,
98 SNDRV_PCM_HW_PARAM_CHANNELS);
99
100 if (priv->convert_rate)
101 rate->min =
102 rate->max = priv->convert_rate;
103
104 if (priv->convert_channels)
105 channels->min =
106 channels->max = priv->convert_channels;
107
108 return 0;
109 }
110
111 static int asoc_simple_card_parse_links(struct device_node *np,
112 struct asoc_simple_card_priv *priv,
113 unsigned int daifmt,
114 int idx, bool is_fe)
115 {
116 struct device *dev = simple_priv_to_dev(priv);
117 struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
118 struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx);
119 int ret;
120
121 /* Parse TDM slot */
122 ret = snd_soc_of_parse_tdm_slot(np,
123 &dai_props->tx_slot_mask,
124 &dai_props->rx_slot_mask,
125 &dai_props->slots,
126 &dai_props->slot_width);
127 if (ret)
128 return ret;
129
130 if (is_fe) {
131 int is_single_links = 0;
132
133 /* BE is dummy */
134 dai_link->codec_of_node = NULL;
135 dai_link->codec_dai_name = "snd-soc-dummy-dai";
136 dai_link->codec_name = "snd-soc-dummy";
137
138 /* FE settings */
139 dai_link->dynamic = 1;
140 dai_link->dpcm_merged_format = 1;
141
142 ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
143 &is_single_links);
144 if (ret)
145 return ret;
146
147 ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props);
148 if (ret < 0)
149 return ret;
150
151 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
152 "fe.%s",
153 dai_link->cpu_dai_name);
154 if (ret < 0)
155 return ret;
156
157 asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
158 } else {
159 /* FE is dummy */
160 dai_link->cpu_of_node = NULL;
161 dai_link->cpu_dai_name = "snd-soc-dummy-dai";
162 dai_link->cpu_name = "snd-soc-dummy";
163
164 /* BE settings */
165 dai_link->no_pcm = 1;
166 dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup;
167
168 ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
169 if (ret < 0)
170 return ret;
171
172 ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props);
173 if (ret < 0)
174 return ret;
175
176 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
177 "be.%s",
178 dai_link->codec_dai_name);
179 if (ret < 0)
180 return ret;
181
182 snd_soc_of_parse_audio_prefix(&priv->snd_card,
183 &priv->codec_conf,
184 dai_link->codec_of_node,
185 PREFIX "prefix");
186 }
187
188 ret = asoc_simple_card_canonicalize_dailink(dai_link);
189 if (ret < 0)
190 return ret;
191
192 dai_link->dai_fmt = daifmt;
193 dai_link->dpcm_playback = 1;
194 dai_link->dpcm_capture = 1;
195 dai_link->ops = &asoc_simple_card_ops;
196 dai_link->init = asoc_simple_card_dai_init;
197
198 dev_dbg(dev, "\t%s / %04x / %d\n",
199 dai_link->name,
200 dai_link->dai_fmt,
201 dai_props->sysclk);
202
203 return 0;
204 }
205
206 static int asoc_simple_card_dai_link_of(struct device_node *node,
207 struct asoc_simple_card_priv *priv)
208 {
209 struct device *dev = simple_priv_to_dev(priv);
210 struct device_node *np;
211 unsigned int daifmt = 0;
212 int ret, i;
213 bool is_fe;
214
215 /* find 1st codec */
216 np = of_get_child_by_name(node, PREFIX "codec");
217 if (!np)
218 return -ENODEV;
219
220 ret = asoc_simple_card_parse_daifmt(dev, node, np,
221 PREFIX, &daifmt);
222 if (ret < 0)
223 return ret;
224
225 i = 0;
226 for_each_child_of_node(node, np) {
227 is_fe = false;
228 if (strcmp(np->name, PREFIX "cpu") == 0)
229 is_fe = true;
230
231 ret = asoc_simple_card_parse_links(np, priv, daifmt, i, is_fe);
232 if (ret < 0)
233 return ret;
234 i++;
235 }
236
237 return 0;
238 }
239
240 static int asoc_simple_card_parse_of(struct device_node *node,
241 struct asoc_simple_card_priv *priv,
242 struct device *dev)
243 {
244 struct asoc_simple_dai *props;
245 struct snd_soc_dai_link *links;
246 int ret;
247 int num;
248
249 if (!node)
250 return -EINVAL;
251
252 num = of_get_child_count(node);
253 props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL);
254 links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL);
255 if (!props || !links)
256 return -ENOMEM;
257
258 priv->dai_props = props;
259 priv->dai_link = links;
260
261 /* Init snd_soc_card */
262 priv->snd_card.owner = THIS_MODULE;
263 priv->snd_card.dev = dev;
264 priv->snd_card.dai_link = priv->dai_link;
265 priv->snd_card.num_links = num;
266 priv->snd_card.codec_conf = &priv->codec_conf;
267 priv->snd_card.num_configs = 1;
268
269 ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing");
270 if (ret < 0)
271 return ret;
272
273 /* sampling rate convert */
274 of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate);
275
276 /* channels transfer */
277 of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels);
278
279 ret = asoc_simple_card_dai_link_of(node, priv);
280 if (ret < 0)
281 return ret;
282
283 ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
284 if (ret < 0)
285 return ret;
286
287 dev_dbg(dev, "New card: %s\n",
288 priv->snd_card.name ? priv->snd_card.name : "");
289 dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
290 dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
291
292 return 0;
293 }
294
295 static int asoc_simple_card_probe(struct platform_device *pdev)
296 {
297 struct asoc_simple_card_priv *priv;
298 struct device_node *np = pdev->dev.of_node;
299 struct device *dev = &pdev->dev;
300 int ret;
301
302 /* Allocate the private data */
303 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
304 if (!priv)
305 return -ENOMEM;
306
307 ret = asoc_simple_card_parse_of(np, priv, dev);
308 if (ret < 0) {
309 if (ret != -EPROBE_DEFER)
310 dev_err(dev, "parse error %d\n", ret);
311 goto err;
312 }
313
314 snd_soc_card_set_drvdata(&priv->snd_card, priv);
315
316 ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
317 if (ret >= 0)
318 return ret;
319 err:
320 asoc_simple_card_clean_reference(&priv->snd_card);
321
322 return ret;
323 }
324
325 static int asoc_simple_card_remove(struct platform_device *pdev)
326 {
327 struct snd_soc_card *card = platform_get_drvdata(pdev);
328
329 return asoc_simple_card_clean_reference(card);
330 }
331
332 static struct platform_driver asoc_simple_card = {
333 .driver = {
334 .name = "simple-scu-audio-card",
335 .of_match_table = asoc_simple_card_of_match,
336 },
337 .probe = asoc_simple_card_probe,
338 .remove = asoc_simple_card_remove,
339 };
340
341 module_platform_driver(asoc_simple_card);
342
343 MODULE_ALIAS("platform:asoc-simple-scu-card");
344 MODULE_LICENSE("GPL v2");
345 MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
346 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
This page took 0.043608 seconds and 5 git commands to generate.