]> asedeno.scripts.mit.edu Git - linux.git/blob - sound/soc/generic/simple-scu-card.c
ASoC: simple-scu-card: support snd_soc_dai_link_component style for platform
[linux.git] / sound / soc / generic / simple-scu-card.c
1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // ASoC simple SCU sound card support
4 //
5 // Copyright (C) 2015 Renesas Solutions Corp.
6 // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
7 //
8 // based on ${LINUX}/sound/soc/generic/simple-card.c
9
10 #include <linux/clk.h>
11 #include <linux/device.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_device.h>
15 #include <linux/platform_device.h>
16 #include <linux/string.h>
17 #include <sound/jack.h>
18 #include <sound/soc.h>
19 #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 snd_soc_codec_conf codec_conf;
25         struct simple_dai_props {
26                 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 #define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
37 #define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
38
39 #define DAI     "sound-dai"
40 #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         .startup = asoc_simple_card_startup,
65         .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         struct snd_soc_dai *dai;
72         struct snd_soc_dai_link *dai_link;
73         struct simple_dai_props *dai_props;
74         int num = rtd->num;
75
76         dai_link        = simple_priv_to_link(priv, num);
77         dai_props       = simple_priv_to_props(priv, num);
78         dai             = dai_link->dynamic ?
79                                 rtd->cpu_dai :
80                                 rtd->codec_dai;
81
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                                         struct snd_pcm_hw_params *params)
87 {
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         return 0;
93 }
94
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         struct device *dev = simple_priv_to_dev(priv);
101         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         int ret;
105
106         if (is_fe) {
107                 int is_single_links = 0;
108                 struct snd_soc_dai_link_component *codecs;
109
110                 /* BE is dummy */
111                 codecs                  = dai_link->codecs;
112                 codecs->of_node         = NULL;
113                 codecs->dai_name        = "snd-soc-dummy-dai";
114                 codecs->name            = "snd-soc-dummy";
115
116                 /* FE settings */
117                 dai_link->dynamic               = 1;
118                 dai_link->dpcm_merged_format    = 1;
119
120                 ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
121                                                  &is_single_links);
122                 if (ret)
123                         return ret;
124
125                 ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, &dai_props->dai);
126                 if (ret < 0)
127                         return ret;
128
129                 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
130                                                         "fe.%s",
131                                                         dai_link->cpu_dai_name);
132                 if (ret < 0)
133                         return ret;
134
135                 asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
136         } else {
137                 /* FE is dummy */
138                 dai_link->cpu_of_node           = NULL;
139                 dai_link->cpu_dai_name          = "snd-soc-dummy-dai";
140                 dai_link->cpu_name              = "snd-soc-dummy";
141
142                 /* BE settings */
143                 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                 if (ret < 0)
148                         return ret;
149
150                 ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, &dai_props->dai);
151                 if (ret < 0)
152                         return ret;
153
154                 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
155                                                         "be.%s",
156                                                         dai_link->codecs->dai_name);
157                 if (ret < 0)
158                         return ret;
159
160                 snd_soc_of_parse_audio_prefix(card,
161                                               &priv->codec_conf,
162                                               dai_link->codecs->of_node,
163                                               PREFIX "prefix");
164         }
165
166         ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai);
167         if (ret)
168                 return ret;
169
170         ret = asoc_simple_card_canonicalize_dailink(dai_link);
171         if (ret < 0)
172                 return ret;
173
174         dai_link->dai_fmt               = daifmt;
175         dai_link->dpcm_playback         = 1;
176         dai_link->dpcm_capture          = 1;
177         dai_link->ops                   = &asoc_simple_card_ops;
178         dai_link->init                  = asoc_simple_card_dai_init;
179
180         return 0;
181 }
182
183 static int asoc_simple_card_parse_of(struct simple_card_data *priv)
184
185 {
186         struct device *dev = simple_priv_to_dev(priv);
187         struct device_node *np;
188         struct snd_soc_card *card = simple_priv_to_card(priv);
189         struct device_node *node = dev->of_node;
190         unsigned int daifmt = 0;
191         bool is_fe;
192         int ret, i;
193
194         if (!node)
195                 return -EINVAL;
196
197         ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
198         if (ret < 0)
199                 return ret;
200
201         ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0);
202         if (ret < 0)
203                 return ret;
204
205         asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata);
206
207         /* find 1st codec */
208         np = of_get_child_by_name(node, PREFIX "codec");
209         if (!np)
210                 return -ENODEV;
211
212         ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt);
213         if (ret < 0)
214                 return ret;
215
216         i = 0;
217         for_each_child_of_node(node, np) {
218                 is_fe = false;
219                 if (strcmp(np->name, PREFIX "cpu") == 0)
220                         is_fe = true;
221
222                 ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe);
223                 if (ret < 0)
224                         return ret;
225                 i++;
226         }
227
228         ret = asoc_simple_card_parse_card_name(card, PREFIX);
229         if (ret < 0)
230                 return ret;
231
232         return 0;
233 }
234
235 static int asoc_simple_card_probe(struct platform_device *pdev)
236 {
237         struct simple_card_data *priv;
238         struct snd_soc_dai_link *dai_link;
239         struct simple_dai_props *dai_props;
240         struct snd_soc_card *card;
241         struct device *dev = &pdev->dev;
242         struct device_node *np = dev->of_node;
243         int num, ret, i;
244
245         /* Allocate the private data */
246         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
247         if (!priv)
248                 return -ENOMEM;
249
250         num = of_get_child_count(np);
251
252         dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL);
253         dai_link  = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL);
254         if (!dai_props || !dai_link)
255                 return -ENOMEM;
256
257         /*
258          * Use snd_soc_dai_link_component instead of legacy style
259          * It is codec only. but cpu/platform will be supported in the future.
260          * see
261          *      soc-core.c :: snd_soc_init_multicodec()
262          */
263         for (i = 0; i < num; i++) {
264                 dai_link[i].codecs      = &dai_props[i].codecs;
265                 dai_link[i].num_codecs  = 1;
266                 dai_link[i].platform    = &dai_props[i].platform;
267         }
268
269         priv->dai_props                         = dai_props;
270         priv->dai_link                          = dai_link;
271
272         /* Init snd_soc_card */
273         card = simple_priv_to_card(priv);
274         card->owner             = THIS_MODULE;
275         card->dev               = dev;
276         card->dai_link          = priv->dai_link;
277         card->num_links         = num;
278         card->codec_conf        = &priv->codec_conf;
279         card->num_configs       = 1;
280
281         ret = asoc_simple_card_parse_of(priv);
282         if (ret < 0) {
283                 if (ret != -EPROBE_DEFER)
284                         dev_err(dev, "parse error %d\n", ret);
285                 goto err;
286         }
287
288         snd_soc_card_set_drvdata(card, priv);
289
290         ret = devm_snd_soc_register_card(dev, card);
291         if (ret < 0)
292                 goto err;
293
294         return 0;
295 err:
296         asoc_simple_card_clean_reference(card);
297
298         return ret;
299 }
300
301 static int asoc_simple_card_remove(struct platform_device *pdev)
302 {
303         struct snd_soc_card *card = platform_get_drvdata(pdev);
304
305         return asoc_simple_card_clean_reference(card);
306 }
307
308 static const struct of_device_id asoc_simple_of_match[] = {
309         { .compatible = "renesas,rsrc-card", },
310         { .compatible = "simple-scu-audio-card", },
311         {},
312 };
313 MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
314
315 static struct platform_driver asoc_simple_card = {
316         .driver = {
317                 .name = "simple-scu-audio-card",
318                 .pm = &snd_soc_pm_ops,
319                 .of_match_table = asoc_simple_of_match,
320         },
321         .probe = asoc_simple_card_probe,
322         .remove = asoc_simple_card_remove,
323 };
324
325 module_platform_driver(asoc_simple_card);
326
327 MODULE_ALIAS("platform:asoc-simple-scu-card");
328 MODULE_LICENSE("GPL v2");
329 MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
330 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");