diff options
Diffstat (limited to 'sound/soc/generic/simple-card.c')
-rw-r--r-- | sound/soc/generic/simple-card.c | 281 |
1 files changed, 175 insertions, 106 deletions
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 21f1ccbdf582..03a7fdcdf114 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c | |||
@@ -24,9 +24,32 @@ struct simple_card_data { | |||
24 | struct asoc_simple_dai cpu_dai; | 24 | struct asoc_simple_dai cpu_dai; |
25 | struct asoc_simple_dai codec_dai; | 25 | struct asoc_simple_dai codec_dai; |
26 | } *dai_props; | 26 | } *dai_props; |
27 | unsigned int mclk_fs; | ||
27 | struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ | 28 | struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ |
28 | }; | 29 | }; |
29 | 30 | ||
31 | static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, | ||
32 | struct snd_pcm_hw_params *params) | ||
33 | { | ||
34 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
35 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
36 | struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); | ||
37 | unsigned int mclk; | ||
38 | int ret = 0; | ||
39 | |||
40 | if (priv->mclk_fs) { | ||
41 | mclk = params_rate(params) * priv->mclk_fs; | ||
42 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, | ||
43 | SND_SOC_CLOCK_IN); | ||
44 | } | ||
45 | |||
46 | return ret; | ||
47 | } | ||
48 | |||
49 | static struct snd_soc_ops asoc_simple_card_ops = { | ||
50 | .hw_params = asoc_simple_card_hw_params, | ||
51 | }; | ||
52 | |||
30 | static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, | 53 | static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, |
31 | struct asoc_simple_dai *set) | 54 | struct asoc_simple_dai *set) |
32 | { | 55 | { |
@@ -66,8 +89,7 @@ err: | |||
66 | 89 | ||
67 | static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) | 90 | static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) |
68 | { | 91 | { |
69 | struct simple_card_data *priv = | 92 | struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); |
70 | snd_soc_card_get_drvdata(rtd->card); | ||
71 | struct snd_soc_dai *codec = rtd->codec_dai; | 93 | struct snd_soc_dai *codec = rtd->codec_dai; |
72 | struct snd_soc_dai *cpu = rtd->cpu_dai; | 94 | struct snd_soc_dai *cpu = rtd->cpu_dai; |
73 | struct simple_dai_props *dai_props; | 95 | struct simple_dai_props *dai_props; |
@@ -88,7 +110,6 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) | |||
88 | 110 | ||
89 | static int | 111 | static int |
90 | asoc_simple_card_sub_parse_of(struct device_node *np, | 112 | asoc_simple_card_sub_parse_of(struct device_node *np, |
91 | unsigned int daifmt, | ||
92 | struct asoc_simple_dai *dai, | 113 | struct asoc_simple_dai *dai, |
93 | const struct device_node **p_node, | 114 | const struct device_node **p_node, |
94 | const char **name) | 115 | const char **name) |
@@ -117,14 +138,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np, | |||
117 | return ret; | 138 | return ret; |
118 | 139 | ||
119 | /* | 140 | /* |
120 | * bitclock-inversion, frame-inversion | ||
121 | * bitclock-master, frame-master | ||
122 | * and specific "format" if it has | ||
123 | */ | ||
124 | dai->fmt = snd_soc_of_parse_daifmt(np, NULL); | ||
125 | dai->fmt |= daifmt; | ||
126 | |||
127 | /* | ||
128 | * dai->sysclk come from | 141 | * dai->sysclk come from |
129 | * "clocks = <&xxx>" (if system has common clock) | 142 | * "clocks = <&xxx>" (if system has common clock) |
130 | * or "system-clock-frequency = <xxx>" | 143 | * or "system-clock-frequency = <xxx>" |
@@ -151,37 +164,135 @@ asoc_simple_card_sub_parse_of(struct device_node *np, | |||
151 | return 0; | 164 | return 0; |
152 | } | 165 | } |
153 | 166 | ||
154 | static int simple_card_cpu_codec_of(struct device_node *node, | 167 | static int simple_card_dai_link_of(struct device_node *node, |
155 | int daifmt, | 168 | struct device *dev, |
156 | struct snd_soc_dai_link *dai_link, | 169 | struct snd_soc_dai_link *dai_link, |
157 | struct simple_dai_props *dai_props) | 170 | struct simple_dai_props *dai_props, |
171 | bool is_top_level_node) | ||
158 | { | 172 | { |
159 | struct device_node *np; | 173 | struct device_node *np = NULL; |
174 | struct device_node *bitclkmaster = NULL; | ||
175 | struct device_node *framemaster = NULL; | ||
176 | unsigned int daifmt; | ||
177 | char *name; | ||
178 | char prop[128]; | ||
179 | char *prefix = ""; | ||
160 | int ret; | 180 | int ret; |
161 | 181 | ||
162 | /* CPU sub-node */ | 182 | if (is_top_level_node) |
163 | ret = -EINVAL; | 183 | prefix = "simple-audio-card,"; |
164 | np = of_get_child_by_name(node, "simple-audio-card,cpu"); | 184 | |
165 | if (np) { | 185 | daifmt = snd_soc_of_parse_daifmt(node, prefix, |
166 | ret = asoc_simple_card_sub_parse_of(np, daifmt, | 186 | &bitclkmaster, &framemaster); |
167 | &dai_props->cpu_dai, | 187 | daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; |
168 | &dai_link->cpu_of_node, | 188 | |
169 | &dai_link->cpu_dai_name); | 189 | snprintf(prop, sizeof(prop), "%scpu", prefix); |
170 | of_node_put(np); | 190 | np = of_get_child_by_name(node, prop); |
191 | if (!np) { | ||
192 | ret = -EINVAL; | ||
193 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); | ||
194 | goto dai_link_of_err; | ||
195 | } | ||
196 | |||
197 | ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai, | ||
198 | &dai_link->cpu_of_node, | ||
199 | &dai_link->cpu_dai_name); | ||
200 | if (ret < 0) | ||
201 | goto dai_link_of_err; | ||
202 | |||
203 | dai_props->cpu_dai.fmt = daifmt; | ||
204 | switch (((np == bitclkmaster) << 4) | (np == framemaster)) { | ||
205 | case 0x11: | ||
206 | dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; | ||
207 | break; | ||
208 | case 0x10: | ||
209 | dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; | ||
210 | break; | ||
211 | case 0x01: | ||
212 | dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; | ||
213 | break; | ||
214 | default: | ||
215 | dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; | ||
216 | break; | ||
217 | } | ||
218 | |||
219 | of_node_put(np); | ||
220 | snprintf(prop, sizeof(prop), "%scodec", prefix); | ||
221 | np = of_get_child_by_name(node, prop); | ||
222 | if (!np) { | ||
223 | ret = -EINVAL; | ||
224 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); | ||
225 | goto dai_link_of_err; | ||
171 | } | 226 | } |
227 | |||
228 | ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai, | ||
229 | &dai_link->codec_of_node, | ||
230 | &dai_link->codec_dai_name); | ||
172 | if (ret < 0) | 231 | if (ret < 0) |
173 | return ret; | 232 | goto dai_link_of_err; |
233 | |||
234 | if (strlen(prefix) && !bitclkmaster && !framemaster) { | ||
235 | /* No dai-link level and master setting was not found from | ||
236 | sound node level, revert back to legacy DT parsing and | ||
237 | take the settings from codec node. */ | ||
238 | dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n", | ||
239 | __func__); | ||
240 | dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt = | ||
241 | snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) | | ||
242 | (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); | ||
243 | } else { | ||
244 | dai_props->codec_dai.fmt = daifmt; | ||
245 | switch (((np == bitclkmaster) << 4) | (np == framemaster)) { | ||
246 | case 0x11: | ||
247 | dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM; | ||
248 | break; | ||
249 | case 0x10: | ||
250 | dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS; | ||
251 | break; | ||
252 | case 0x01: | ||
253 | dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM; | ||
254 | break; | ||
255 | default: | ||
256 | dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS; | ||
257 | break; | ||
258 | } | ||
259 | } | ||
174 | 260 | ||
175 | /* CODEC sub-node */ | 261 | if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { |
176 | ret = -EINVAL; | 262 | ret = -EINVAL; |
177 | np = of_get_child_by_name(node, "simple-audio-card,codec"); | 263 | goto dai_link_of_err; |
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 | } | 264 | } |
265 | |||
266 | /* simple-card assumes platform == cpu */ | ||
267 | dai_link->platform_of_node = dai_link->cpu_of_node; | ||
268 | |||
269 | /* Link name is created from CPU/CODEC dai name */ | ||
270 | name = devm_kzalloc(dev, | ||
271 | strlen(dai_link->cpu_dai_name) + | ||
272 | strlen(dai_link->codec_dai_name) + 2, | ||
273 | GFP_KERNEL); | ||
274 | sprintf(name, "%s-%s", dai_link->cpu_dai_name, | ||
275 | dai_link->codec_dai_name); | ||
276 | dai_link->name = dai_link->stream_name = name; | ||
277 | dai_link->ops = &asoc_simple_card_ops; | ||
278 | |||
279 | dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); | ||
280 | dev_dbg(dev, "\tcpu : %s / %04x / %d\n", | ||
281 | dai_link->cpu_dai_name, | ||
282 | dai_props->cpu_dai.fmt, | ||
283 | dai_props->cpu_dai.sysclk); | ||
284 | dev_dbg(dev, "\tcodec : %s / %04x / %d\n", | ||
285 | dai_link->codec_dai_name, | ||
286 | dai_props->codec_dai.fmt, | ||
287 | dai_props->codec_dai.sysclk); | ||
288 | |||
289 | dai_link_of_err: | ||
290 | if (np) | ||
291 | of_node_put(np); | ||
292 | if (bitclkmaster) | ||
293 | of_node_put(bitclkmaster); | ||
294 | if (framemaster) | ||
295 | of_node_put(framemaster); | ||
185 | return ret; | 296 | return ret; |
186 | } | 297 | } |
187 | 298 | ||
@@ -192,18 +303,11 @@ static int asoc_simple_card_parse_of(struct device_node *node, | |||
192 | { | 303 | { |
193 | struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; | 304 | struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link; |
194 | struct simple_dai_props *dai_props = priv->dai_props; | 305 | struct simple_dai_props *dai_props = priv->dai_props; |
195 | struct device_node *np; | ||
196 | char *name; | ||
197 | unsigned int daifmt; | ||
198 | int ret; | 306 | int ret; |
199 | 307 | ||
200 | /* parsing the card name from DT */ | 308 | /* parsing the card name from DT */ |
201 | snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name"); | 309 | snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name"); |
202 | 310 | ||
203 | /* get CPU/CODEC common format via simple-audio-card,format */ | ||
204 | daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & | ||
205 | (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); | ||
206 | |||
207 | /* off-codec widgets */ | 311 | /* off-codec widgets */ |
208 | if (of_property_read_bool(node, "simple-audio-card,widgets")) { | 312 | if (of_property_read_bool(node, "simple-audio-card,widgets")) { |
209 | ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, | 313 | ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, |
@@ -220,71 +324,36 @@ static int asoc_simple_card_parse_of(struct device_node *node, | |||
220 | return ret; | 324 | return ret; |
221 | } | 325 | } |
222 | 326 | ||
223 | /* loop on the DAI links */ | 327 | /* Factor to mclk, used in hw_params() */ |
224 | np = NULL; | 328 | of_property_read_u32(node, "simple-audio-card,mclk-fs", |
225 | for (;;) { | 329 | &priv->mclk_fs); |
226 | if (multi) { | 330 | |
227 | np = of_get_next_child(node, np); | 331 | dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ? |
228 | if (!np) | 332 | priv->snd_card.name : ""); |
229 | break; | 333 | |
334 | if (multi) { | ||
335 | struct device_node *np = NULL; | ||
336 | int i; | ||
337 | for (i = 0; (np = of_get_next_child(node, np)); i++) { | ||
338 | dev_dbg(dev, "\tlink %d:\n", i); | ||
339 | ret = simple_card_dai_link_of(np, dev, dai_link + i, | ||
340 | dai_props + i, false); | ||
341 | if (ret < 0) { | ||
342 | of_node_put(np); | ||
343 | return ret; | ||
344 | } | ||
230 | } | 345 | } |
231 | 346 | } else { | |
232 | ret = simple_card_cpu_codec_of(multi ? np : node, | 347 | ret = simple_card_dai_link_of(node, dev, dai_link, dai_props, |
233 | daifmt, dai_link, dai_props); | 348 | true); |
234 | if (ret < 0) | 349 | if (ret < 0) |
235 | goto err; | 350 | return ret; |
236 | |||
237 | /* | ||
238 | * overwrite cpu_dai->fmt as its DAIFMT_MASTER bit is based on CODEC | ||
239 | * while the other bits should be identical unless buggy SW/HW design. | ||
240 | */ | ||
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; | ||
250 | |||
251 | name = devm_kzalloc(dev, | ||
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 | } | 351 | } |
265 | 352 | ||
266 | /* card name is created from CPU/CODEC dai name */ | ||
267 | dai_link = priv->snd_card.dai_link; | ||
268 | if (!priv->snd_card.name) | 353 | if (!priv->snd_card.name) |
269 | priv->snd_card.name = dai_link->name; | 354 | priv->snd_card.name = priv->snd_card.dai_link->name; |
270 | |||
271 | dev_dbg(dev, "card-name : %s\n", priv->snd_card.name); | ||
272 | dev_dbg(dev, "platform : %04x\n", daifmt); | ||
273 | dai_props = priv->dai_props; | ||
274 | dev_dbg(dev, "cpu : %s / %04x / %d\n", | ||
275 | dai_link->cpu_dai_name, | ||
276 | dai_props->cpu_dai.fmt, | ||
277 | dai_props->cpu_dai.sysclk); | ||
278 | dev_dbg(dev, "codec : %s / %04x / %d\n", | ||
279 | dai_link->codec_dai_name, | ||
280 | dai_props->codec_dai.fmt, | ||
281 | dai_props->codec_dai.sysclk); | ||
282 | 355 | ||
283 | return 0; | 356 | return 0; |
284 | |||
285 | err: | ||
286 | of_node_put(np); | ||
287 | return ret; | ||
288 | } | 357 | } |
289 | 358 | ||
290 | /* update the reference count of the devices nodes at end of probe */ | 359 | /* update the reference count of the devices nodes at end of probe */ |
@@ -378,10 +447,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) | |||
378 | return -EINVAL; | 447 | return -EINVAL; |
379 | } | 448 | } |
380 | 449 | ||
381 | if (!cinfo->name || | 450 | if (!cinfo->name || |
382 | !cinfo->codec_dai.name || | 451 | !cinfo->codec_dai.name || |
383 | !cinfo->codec || | 452 | !cinfo->codec || |
384 | !cinfo->platform || | 453 | !cinfo->platform || |
385 | !cinfo->cpu_dai.name) { | 454 | !cinfo->cpu_dai.name) { |
386 | dev_err(dev, "insufficient asoc_simple_card_info settings\n"); | 455 | dev_err(dev, "insufficient asoc_simple_card_info settings\n"); |
387 | return -EINVAL; | 456 | return -EINVAL; |
@@ -425,11 +494,11 @@ MODULE_DEVICE_TABLE(of, asoc_simple_of_match); | |||
425 | 494 | ||
426 | static struct platform_driver asoc_simple_card = { | 495 | static struct platform_driver asoc_simple_card = { |
427 | .driver = { | 496 | .driver = { |
428 | .name = "asoc-simple-card", | 497 | .name = "asoc-simple-card", |
429 | .owner = THIS_MODULE, | 498 | .owner = THIS_MODULE, |
430 | .of_match_table = asoc_simple_of_match, | 499 | .of_match_table = asoc_simple_of_match, |
431 | }, | 500 | }, |
432 | .probe = asoc_simple_card_probe, | 501 | .probe = asoc_simple_card_probe, |
433 | }; | 502 | }; |
434 | 503 | ||
435 | module_platform_driver(asoc_simple_card); | 504 | module_platform_driver(asoc_simple_card); |