diff options
author | Jean-Francois Moine <moinejf@free.fr> | 2014-03-20 06:49:55 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-03-25 14:09:20 -0400 |
commit | 6a91a17bd7b92b2d2aa9ece85457f52a62fd7708 (patch) | |
tree | ebf648847abeaeb5fe9973c1d7cfc2aca2414e05 /sound | |
parent | 015f630de86c8a79df45c475c34087d3e96b882a (diff) |
ASoC: simple-card: Handle many DAI links
Some simple audio cards may have many DAI links.
This patch extends the simple-card driver for handling such cards.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/generic/simple-card.c | 190 |
1 files changed, 121 insertions, 69 deletions
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 1e865c5d377f..21f1ccbdf582 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c | |||
@@ -70,13 +70,16 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) | |||
70 | snd_soc_card_get_drvdata(rtd->card); | 70 | snd_soc_card_get_drvdata(rtd->card); |
71 | struct snd_soc_dai *codec = rtd->codec_dai; | 71 | struct snd_soc_dai *codec = rtd->codec_dai; |
72 | struct snd_soc_dai *cpu = rtd->cpu_dai; | 72 | struct snd_soc_dai *cpu = rtd->cpu_dai; |
73 | int ret; | 73 | struct simple_dai_props *dai_props; |
74 | int num, ret; | ||
74 | 75 | ||
75 | ret = __asoc_simple_card_dai_init(codec, &priv->dai_props->codec_dai); | 76 | num = rtd - rtd->card->rtd; |
77 | dai_props = &priv->dai_props[num]; | ||
78 | ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai); | ||
76 | if (ret < 0) | 79 | if (ret < 0) |
77 | return ret; | 80 | return ret; |
78 | 81 | ||
79 | ret = __asoc_simple_card_dai_init(cpu, &priv->dai_props->cpu_dai); | 82 | ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai); |
80 | if (ret < 0) | 83 | if (ret < 0) |
81 | return ret; | 84 | return ret; |
82 | 85 | ||
@@ -148,13 +151,47 @@ asoc_simple_card_sub_parse_of(struct device_node *np, | |||
148 | return 0; | 151 | return 0; |
149 | } | 152 | } |
150 | 153 | ||
154 | static int simple_card_cpu_codec_of(struct device_node *node, | ||
155 | int daifmt, | ||
156 | struct snd_soc_dai_link *dai_link, | ||
157 | struct simple_dai_props *dai_props) | ||
158 | { | ||
159 | struct device_node *np; | ||
160 | int ret; | ||
161 | |||
162 | /* CPU sub-node */ | ||
163 | ret = -EINVAL; | ||
164 | np = of_get_child_by_name(node, "simple-audio-card,cpu"); | ||
165 | if (np) { | ||
166 | ret = asoc_simple_card_sub_parse_of(np, daifmt, | ||
167 | &dai_props->cpu_dai, | ||
168 | &dai_link->cpu_of_node, | ||
169 | &dai_link->cpu_dai_name); | ||
170 | of_node_put(np); | ||
171 | } | ||
172 | if (ret < 0) | ||
173 | return ret; | ||
174 | |||
175 | /* CODEC sub-node */ | ||
176 | ret = -EINVAL; | ||
177 | np = of_get_child_by_name(node, "simple-audio-card,codec"); | ||
178 | if (np) { | ||
179 | ret = asoc_simple_card_sub_parse_of(np, daifmt, | ||
180 | &dai_props->codec_dai, | ||
181 | &dai_link->codec_of_node, | ||
182 | &dai_link->codec_dai_name); | ||
183 | of_node_put(np); | ||
184 | } | ||
185 | return ret; | ||
186 | } | ||
187 | |||
151 | static int asoc_simple_card_parse_of(struct device_node *node, | 188 | static int asoc_simple_card_parse_of(struct device_node *node, |
152 | struct simple_card_data *priv, | 189 | struct simple_card_data *priv, |
153 | struct device *dev) | 190 | struct device *dev, |
191 | int multi) | ||
154 | { | 192 | { |
155 | struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; | 193 | struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; |
156 | struct asoc_simple_dai *codec_dai = &priv->dai_props->codec_dai; | 194 | struct simple_dai_props *dai_props = priv->dai_props; |
157 | struct asoc_simple_dai *cpu_dai = &priv->dai_props->cpu_dai; | ||
158 | struct device_node *np; | 195 | struct device_node *np; |
159 | char *name; | 196 | char *name; |
160 | unsigned int daifmt; | 197 | unsigned int daifmt; |
@@ -183,78 +220,71 @@ static int asoc_simple_card_parse_of(struct device_node *node, | |||
183 | return ret; | 220 | return ret; |
184 | } | 221 | } |
185 | 222 | ||
186 | /* CPU sub-node */ | 223 | /* loop on the DAI links */ |
187 | ret = -EINVAL; | 224 | np = NULL; |
188 | np = of_get_child_by_name(node, "simple-audio-card,cpu"); | 225 | for (;;) { |
189 | if (np) { | 226 | if (multi) { |
190 | ret = asoc_simple_card_sub_parse_of(np, daifmt, | 227 | np = of_get_next_child(node, np); |
191 | cpu_dai, | 228 | if (!np) |
192 | &dai_link->cpu_of_node, | 229 | break; |
193 | &dai_link->cpu_dai_name); | 230 | } |
194 | of_node_put(np); | ||
195 | } | ||
196 | if (ret < 0) | ||
197 | return ret; | ||
198 | 231 | ||
199 | /* CODEC sub-node */ | 232 | ret = simple_card_cpu_codec_of(multi ? np : node, |
200 | ret = -EINVAL; | 233 | daifmt, dai_link, dai_props); |
201 | np = of_get_child_by_name(node, "simple-audio-card,codec"); | 234 | if (ret < 0) |
202 | if (np) { | 235 | goto err; |
203 | ret = asoc_simple_card_sub_parse_of(np, daifmt, | ||
204 | codec_dai, | ||
205 | &dai_link->codec_of_node, | ||
206 | &dai_link->codec_dai_name); | ||
207 | of_node_put(np); | ||
208 | } | ||
209 | if (ret < 0) | ||
210 | return ret; | ||
211 | 236 | ||
212 | /* | 237 | /* |
213 | * overwrite cpu_dai->fmt as its DAIFMT_MASTER bit is based on CODEC | 238 | * overwrite cpu_dai->fmt as its DAIFMT_MASTER bit is based on CODEC |
214 | * while the other bits should be identical unless buggy SW/HW design. | 239 | * while the other bits should be identical unless buggy SW/HW design. |
215 | */ | 240 | */ |
216 | cpu_dai->fmt = codec_dai->fmt; | 241 | dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt; |
242 | |||
243 | if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { | ||
244 | ret = -EINVAL; | ||
245 | goto err; | ||
246 | } | ||
247 | |||
248 | /* simple-card assumes platform == cpu */ | ||
249 | dai_link->platform_of_node = dai_link->cpu_of_node; | ||
217 | 250 | ||
218 | if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) | 251 | name = devm_kzalloc(dev, |
219 | return -EINVAL; | 252 | strlen(dai_link->cpu_dai_name) + |
253 | strlen(dai_link->codec_dai_name) + 2, | ||
254 | GFP_KERNEL); | ||
255 | sprintf(name, "%s-%s", dai_link->cpu_dai_name, | ||
256 | dai_link->codec_dai_name); | ||
257 | dai_link->name = dai_link->stream_name = name; | ||
258 | |||
259 | if (!multi) | ||
260 | break; | ||
261 | |||
262 | dai_link++; | ||
263 | dai_props++; | ||
264 | } | ||
220 | 265 | ||
221 | /* card name is created from CPU/CODEC dai name */ | 266 | /* card name is created from CPU/CODEC dai name */ |
222 | name = devm_kzalloc(dev, | 267 | dai_link = priv->snd_card.dai_link; |
223 | strlen(dai_link->cpu_dai_name) + | ||
224 | strlen(dai_link->codec_dai_name) + 2, | ||
225 | GFP_KERNEL); | ||
226 | sprintf(name, "%s-%s", dai_link->cpu_dai_name, | ||
227 | dai_link->codec_dai_name); | ||
228 | if (!priv->snd_card.name) | 268 | if (!priv->snd_card.name) |
229 | priv->snd_card.name = name; | 269 | priv->snd_card.name = dai_link->name; |
230 | dai_link->name = dai_link->stream_name = name; | ||
231 | |||
232 | /* simple-card assumes platform == cpu */ | ||
233 | dai_link->platform_of_node = dai_link->cpu_of_node; | ||
234 | 270 | ||
235 | dev_dbg(dev, "card-name : %s\n", name); | 271 | dev_dbg(dev, "card-name : %s\n", priv->snd_card.name); |
236 | dev_dbg(dev, "platform : %04x\n", daifmt); | 272 | dev_dbg(dev, "platform : %04x\n", daifmt); |
273 | dai_props = priv->dai_props; | ||
237 | dev_dbg(dev, "cpu : %s / %04x / %d\n", | 274 | dev_dbg(dev, "cpu : %s / %04x / %d\n", |
238 | dai_link->cpu_dai_name, | 275 | dai_link->cpu_dai_name, |
239 | cpu_dai->fmt, | 276 | dai_props->cpu_dai.fmt, |
240 | cpu_dai->sysclk); | 277 | dai_props->cpu_dai.sysclk); |
241 | dev_dbg(dev, "codec : %s / %04x / %d\n", | 278 | dev_dbg(dev, "codec : %s / %04x / %d\n", |
242 | dai_link->codec_dai_name, | 279 | dai_link->codec_dai_name, |
243 | codec_dai->fmt, | 280 | dai_props->codec_dai.fmt, |
244 | codec_dai->sysclk); | 281 | dai_props->codec_dai.sysclk); |
245 | |||
246 | /* | ||
247 | * soc_bind_dai_link() will check cpu name | ||
248 | * after of_node matching if dai_link has cpu_dai_name. | ||
249 | * but, it will never match if name was created by fmt_single_name() | ||
250 | * remove cpu_dai_name to escape name matching. | ||
251 | * see | ||
252 | * fmt_single_name() | ||
253 | * fmt_multiple_name() | ||
254 | */ | ||
255 | dai_link->cpu_dai_name = NULL; | ||
256 | 282 | ||
257 | return 0; | 283 | return 0; |
284 | |||
285 | err: | ||
286 | of_node_put(np); | ||
287 | return ret; | ||
258 | } | 288 | } |
259 | 289 | ||
260 | /* update the reference count of the devices nodes at end of probe */ | 290 | /* update the reference count of the devices nodes at end of probe */ |
@@ -284,11 +314,20 @@ static int asoc_simple_card_probe(struct platform_device *pdev) | |||
284 | struct snd_soc_dai_link *dai_link; | 314 | struct snd_soc_dai_link *dai_link; |
285 | struct device_node *np = pdev->dev.of_node; | 315 | struct device_node *np = pdev->dev.of_node; |
286 | struct device *dev = &pdev->dev; | 316 | struct device *dev = &pdev->dev; |
287 | int ret; | 317 | int num_links, multi, ret; |
318 | |||
319 | /* get the number of DAI links */ | ||
320 | if (np && of_get_child_by_name(np, "simple-audio-card,dai-link")) { | ||
321 | num_links = of_get_child_count(np); | ||
322 | multi = 1; | ||
323 | } else { | ||
324 | num_links = 1; | ||
325 | multi = 0; | ||
326 | } | ||
288 | 327 | ||
289 | /* allocate the private data and the DAI link array */ | 328 | /* allocate the private data and the DAI link array */ |
290 | priv = devm_kzalloc(dev, | 329 | priv = devm_kzalloc(dev, |
291 | sizeof(*priv) + sizeof(*dai_link), | 330 | sizeof(*priv) + sizeof(*dai_link) * num_links, |
292 | GFP_KERNEL); | 331 | GFP_KERNEL); |
293 | if (!priv) | 332 | if (!priv) |
294 | return -ENOMEM; | 333 | return -ENOMEM; |
@@ -300,23 +339,36 @@ static int asoc_simple_card_probe(struct platform_device *pdev) | |||
300 | priv->snd_card.dev = dev; | 339 | priv->snd_card.dev = dev; |
301 | dai_link = priv->dai_link; | 340 | dai_link = priv->dai_link; |
302 | priv->snd_card.dai_link = dai_link; | 341 | priv->snd_card.dai_link = dai_link; |
303 | priv->snd_card.num_links = 1; | 342 | priv->snd_card.num_links = num_links; |
304 | 343 | ||
305 | /* get room for the other properties */ | 344 | /* get room for the other properties */ |
306 | priv->dai_props = devm_kzalloc(dev, | 345 | priv->dai_props = devm_kzalloc(dev, |
307 | sizeof(*priv->dai_props), | 346 | sizeof(*priv->dai_props) * num_links, |
308 | GFP_KERNEL); | 347 | GFP_KERNEL); |
309 | if (!priv->dai_props) | 348 | if (!priv->dai_props) |
310 | return -ENOMEM; | 349 | return -ENOMEM; |
311 | 350 | ||
312 | if (np && of_device_is_available(np)) { | 351 | if (np && of_device_is_available(np)) { |
313 | 352 | ||
314 | ret = asoc_simple_card_parse_of(np, priv, dev); | 353 | ret = asoc_simple_card_parse_of(np, priv, dev, multi); |
315 | if (ret < 0) { | 354 | if (ret < 0) { |
316 | if (ret != -EPROBE_DEFER) | 355 | if (ret != -EPROBE_DEFER) |
317 | dev_err(dev, "parse error %d\n", ret); | 356 | dev_err(dev, "parse error %d\n", ret); |
318 | goto err; | 357 | goto err; |
319 | } | 358 | } |
359 | |||
360 | /* | ||
361 | * soc_bind_dai_link() will check cpu name | ||
362 | * after of_node matching if dai_link has cpu_dai_name. | ||
363 | * but, it will never match if name was created by fmt_single_name() | ||
364 | * remove cpu_dai_name to escape name matching. | ||
365 | * see | ||
366 | * fmt_single_name() | ||
367 | * fmt_multiple_name() | ||
368 | */ | ||
369 | if (num_links == 1) | ||
370 | dai_link->cpu_dai_name = NULL; | ||
371 | |||
320 | } else { | 372 | } else { |
321 | struct asoc_simple_card_info *cinfo; | 373 | struct asoc_simple_card_info *cinfo; |
322 | 374 | ||