aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/generic/simple-card.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/generic/simple-card.c')
-rw-r--r--sound/soc/generic/simple-card.c281
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
31static 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
49static struct snd_soc_ops asoc_simple_card_ops = {
50 .hw_params = asoc_simple_card_hw_params,
51};
52
30static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, 53static 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
67static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) 90static 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
89static int 111static int
90asoc_simple_card_sub_parse_of(struct device_node *np, 112asoc_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
154static int simple_card_cpu_codec_of(struct device_node *node, 167static 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
289dai_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
285err:
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
426static struct platform_driver asoc_simple_card = { 495static 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
435module_platform_driver(asoc_simple_card); 504module_platform_driver(asoc_simple_card);