1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Copyright 2017-2021 NXP |
3 | |
4 | #include <linux/module.h> |
5 | #include <linux/init.h> |
6 | #include <linux/slab.h> |
7 | #include <linux/gpio/consumer.h> |
8 | #include <linux/of.h> |
9 | #include <linux/i2c.h> |
10 | #include <linux/clk.h> |
11 | #include <sound/soc.h> |
12 | #include <sound/pcm_params.h> |
13 | #include <sound/pcm.h> |
14 | #include <sound/soc-dapm.h> |
15 | #include <sound/simple_card_utils.h> |
16 | |
17 | #include "fsl_sai.h" |
18 | |
19 | #define IMX_CARD_MCLK_22P5792MHZ 22579200 |
20 | #define IMX_CARD_MCLK_24P576MHZ 24576000 |
21 | |
22 | enum codec_type { |
23 | CODEC_DUMMY = 0, |
24 | CODEC_AK5558 = 1, |
25 | CODEC_AK4458, |
26 | CODEC_AK4497, |
27 | CODEC_AK5552, |
28 | }; |
29 | |
30 | /* |
31 | * Mapping LRCK fs and frame width, table 3 & 4 in datasheet |
32 | * @rmin: min rate |
33 | * @rmax: max rate |
34 | * @wmin: min frame ratio |
35 | * @wmax: max frame ratio |
36 | */ |
37 | struct imx_akcodec_fs_mul { |
38 | unsigned int rmin; |
39 | unsigned int rmax; |
40 | unsigned int wmin; |
41 | unsigned int wmax; |
42 | }; |
43 | |
44 | /* |
45 | * Mapping TDM mode and frame width |
46 | */ |
47 | struct imx_akcodec_tdm_fs_mul { |
48 | unsigned int min; |
49 | unsigned int max; |
50 | unsigned int mul; |
51 | }; |
52 | |
53 | /* |
54 | * struct imx_card_plat_data - specific info for codecs |
55 | * |
56 | * @fs_mul: ratio of mclk/fs for normal mode |
57 | * @tdm_fs_mul: ratio of mclk/fs for tdm mode |
58 | * @support_rates: supported sample rate |
59 | * @support_tdm_rates: supported sample rate for tdm mode |
60 | * @support_channels: supported channels |
61 | * @support_tdm_channels: supported channels for tdm mode |
62 | * @num_fs_mul: ARRAY_SIZE of fs_mul |
63 | * @num_tdm_fs_mul: ARRAY_SIZE of tdm_fs_mul |
64 | * @num_rates: ARRAY_SIZE of support_rates |
65 | * @num_tdm_rates: ARRAY_SIZE of support_tdm_rates |
66 | * @num_channels: ARRAY_SIZE of support_channels |
67 | * @num_tdm_channels: ARRAY_SIZE of support_tdm_channels |
68 | * @type: codec type |
69 | */ |
70 | struct imx_card_plat_data { |
71 | struct imx_akcodec_fs_mul *fs_mul; |
72 | struct imx_akcodec_tdm_fs_mul *tdm_fs_mul; |
73 | const u32 *support_rates; |
74 | const u32 *support_tdm_rates; |
75 | const u32 *support_channels; |
76 | const u32 *support_tdm_channels; |
77 | unsigned int num_fs_mul; |
78 | unsigned int num_tdm_fs_mul; |
79 | unsigned int num_rates; |
80 | unsigned int num_tdm_rates; |
81 | unsigned int num_channels; |
82 | unsigned int num_tdm_channels; |
83 | unsigned int num_codecs; |
84 | enum codec_type type; |
85 | }; |
86 | |
87 | /* |
88 | * struct dai_link_data - specific info for dai link |
89 | * |
90 | * @slots: slot number |
91 | * @slot_width: slot width value |
92 | * @cpu_sysclk_id: sysclk id for cpu dai |
93 | * @one2one_ratio: true if mclk equal to bclk |
94 | */ |
95 | struct dai_link_data { |
96 | unsigned int slots; |
97 | unsigned int slot_width; |
98 | unsigned int cpu_sysclk_id; |
99 | bool one2one_ratio; |
100 | }; |
101 | |
102 | /* |
103 | * struct imx_card_data - platform device data |
104 | * |
105 | * @plat_data: pointer of imx_card_plat_data |
106 | * @dapm_routes: pointer of dapm_routes |
107 | * @link_data: private data for dai link |
108 | * @card: card instance |
109 | * @num_dapm_routes: number of dapm_routes |
110 | * @asrc_rate: asrc rates |
111 | * @asrc_format: asrc format |
112 | */ |
113 | struct imx_card_data { |
114 | struct imx_card_plat_data *plat_data; |
115 | struct snd_soc_dapm_route *dapm_routes; |
116 | struct dai_link_data *link_data; |
117 | struct snd_soc_card card; |
118 | int num_dapm_routes; |
119 | u32 asrc_rate; |
120 | snd_pcm_format_t asrc_format; |
121 | }; |
122 | |
123 | static struct imx_akcodec_fs_mul ak4458_fs_mul[] = { |
124 | /* Normal, < 32kHz */ |
125 | { .rmin = 8000, .rmax = 24000, .wmin = 256, .wmax = 1024, }, |
126 | /* Normal, 32kHz */ |
127 | { .rmin = 32000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, |
128 | /* Normal */ |
129 | { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 768, }, |
130 | /* Double */ |
131 | { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 512, }, |
132 | /* Quad */ |
133 | { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 256, }, |
134 | /* Oct */ |
135 | { .rmin = 352800, .rmax = 384000, .wmin = 32, .wmax = 128, }, |
136 | /* Hex */ |
137 | { .rmin = 705600, .rmax = 768000, .wmin = 16, .wmax = 64, }, |
138 | }; |
139 | |
140 | static struct imx_akcodec_tdm_fs_mul ak4458_tdm_fs_mul[] = { |
141 | /* |
142 | * Table 13 - Audio Interface Format |
143 | * For TDM mode, MCLK should is set to |
144 | * obtained from 2 * slots * slot_width |
145 | */ |
146 | { .min = 128, .max = 128, .mul = 256 }, /* TDM128 */ |
147 | { .min = 256, .max = 256, .mul = 512 }, /* TDM256 */ |
148 | { .min = 512, .max = 512, .mul = 1024 }, /* TDM512 */ |
149 | }; |
150 | |
151 | static struct imx_akcodec_fs_mul ak4497_fs_mul[] = { |
152 | /** |
153 | * Table 7 - mapping multiplier and speed mode |
154 | * Tables 8 & 9 - mapping speed mode and LRCK fs |
155 | */ |
156 | { .rmin = 8000, .rmax = 32000, .wmin = 256, .wmax = 1024, }, /* Normal, <= 32kHz */ |
157 | { .rmin = 44100, .rmax = 48000, .wmin = 256, .wmax = 512, }, /* Normal */ |
158 | { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, /* Double */ |
159 | { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, /* Quad */ |
160 | { .rmin = 352800, .rmax = 384000, .wmin = 128, .wmax = 128, }, /* Oct */ |
161 | { .rmin = 705600, .rmax = 768000, .wmin = 64, .wmax = 64, }, /* Hex */ |
162 | }; |
163 | |
164 | /* |
165 | * Auto MCLK selection based on LRCK for Normal Mode |
166 | * (Table 4 from datasheet) |
167 | */ |
168 | static struct imx_akcodec_fs_mul ak5558_fs_mul[] = { |
169 | { .rmin = 8000, .rmax = 32000, .wmin = 512, .wmax = 1024, }, |
170 | { .rmin = 44100, .rmax = 48000, .wmin = 512, .wmax = 512, }, |
171 | { .rmin = 88200, .rmax = 96000, .wmin = 256, .wmax = 256, }, |
172 | { .rmin = 176400, .rmax = 192000, .wmin = 128, .wmax = 128, }, |
173 | { .rmin = 352800, .rmax = 384000, .wmin = 64, .wmax = 64, }, |
174 | { .rmin = 705600, .rmax = 768000, .wmin = 32, .wmax = 32, }, |
175 | }; |
176 | |
177 | /* |
178 | * MCLK and BCLK selection based on TDM mode |
179 | * because of SAI we also add the restriction: MCLK >= 2 * BCLK |
180 | * (Table 9 from datasheet) |
181 | */ |
182 | static struct imx_akcodec_tdm_fs_mul ak5558_tdm_fs_mul[] = { |
183 | { .min = 128, .max = 128, .mul = 256 }, |
184 | { .min = 256, .max = 256, .mul = 512 }, |
185 | { .min = 512, .max = 512, .mul = 1024 }, |
186 | }; |
187 | |
188 | static const u32 akcodec_rates[] = { |
189 | 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, |
190 | 96000, 176400, 192000, 352800, 384000, 705600, 768000, |
191 | }; |
192 | |
193 | static const u32 akcodec_tdm_rates[] = { |
194 | 8000, 16000, 32000, 48000, 96000, |
195 | }; |
196 | |
197 | static const u32 ak4458_channels[] = { |
198 | 1, 2, 4, 6, 8, 10, 12, 14, 16, |
199 | }; |
200 | |
201 | static const u32 ak4458_tdm_channels[] = { |
202 | 1, 2, 3, 4, 5, 6, 7, 8, 16, |
203 | }; |
204 | |
205 | static const u32 ak5558_channels[] = { |
206 | 1, 2, 4, 6, 8, |
207 | }; |
208 | |
209 | static const u32 ak5558_tdm_channels[] = { |
210 | 1, 2, 3, 4, 5, 6, 7, 8, |
211 | }; |
212 | |
213 | static bool format_is_dsd(struct snd_pcm_hw_params *params) |
214 | { |
215 | snd_pcm_format_t format = params_format(p: params); |
216 | |
217 | switch (format) { |
218 | case SNDRV_PCM_FORMAT_DSD_U8: |
219 | case SNDRV_PCM_FORMAT_DSD_U16_LE: |
220 | case SNDRV_PCM_FORMAT_DSD_U16_BE: |
221 | case SNDRV_PCM_FORMAT_DSD_U32_LE: |
222 | case SNDRV_PCM_FORMAT_DSD_U32_BE: |
223 | return true; |
224 | default: |
225 | return false; |
226 | } |
227 | } |
228 | |
229 | static bool format_is_tdm(struct dai_link_data *link_data) |
230 | { |
231 | if (link_data->slots > 2) |
232 | return true; |
233 | else |
234 | return false; |
235 | } |
236 | |
237 | static bool codec_is_akcodec(unsigned int type) |
238 | { |
239 | switch (type) { |
240 | case CODEC_AK4458: |
241 | case CODEC_AK4497: |
242 | case CODEC_AK5558: |
243 | case CODEC_AK5552: |
244 | return true; |
245 | default: |
246 | break; |
247 | } |
248 | return false; |
249 | } |
250 | |
251 | static unsigned long akcodec_get_mclk_rate(struct snd_pcm_substream *substream, |
252 | struct snd_pcm_hw_params *params, |
253 | int slots, int slot_width) |
254 | { |
255 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
256 | struct imx_card_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
257 | const struct imx_card_plat_data *plat_data = data->plat_data; |
258 | struct dai_link_data *link_data = &data->link_data[rtd->num]; |
259 | unsigned int width = slots * slot_width; |
260 | unsigned int rate = params_rate(p: params); |
261 | int i; |
262 | |
263 | if (format_is_tdm(link_data)) { |
264 | for (i = 0; i < plat_data->num_tdm_fs_mul; i++) { |
265 | /* min = max = slots * slots_width */ |
266 | if (width != plat_data->tdm_fs_mul[i].min) |
267 | continue; |
268 | return rate * plat_data->tdm_fs_mul[i].mul; |
269 | } |
270 | } else { |
271 | for (i = 0; i < plat_data->num_fs_mul; i++) { |
272 | if (rate >= plat_data->fs_mul[i].rmin && |
273 | rate <= plat_data->fs_mul[i].rmax) { |
274 | width = max(width, plat_data->fs_mul[i].wmin); |
275 | width = min(width, plat_data->fs_mul[i].wmax); |
276 | |
277 | /* Adjust SAI bclk:mclk ratio */ |
278 | width *= link_data->one2one_ratio ? 1 : 2; |
279 | |
280 | return rate * width; |
281 | } |
282 | } |
283 | } |
284 | |
285 | /* Let DAI manage clk frequency by default */ |
286 | return 0; |
287 | } |
288 | |
289 | static int imx_aif_hw_params(struct snd_pcm_substream *substream, |
290 | struct snd_pcm_hw_params *params) |
291 | { |
292 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
293 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
294 | struct snd_soc_card *card = rtd->card; |
295 | struct imx_card_data *data = snd_soc_card_get_drvdata(card); |
296 | struct dai_link_data *link_data = &data->link_data[rtd->num]; |
297 | struct imx_card_plat_data *plat_data = data->plat_data; |
298 | struct device *dev = card->dev; |
299 | struct snd_soc_dai *codec_dai; |
300 | unsigned long mclk_freq; |
301 | unsigned int fmt = rtd->dai_link->dai_fmt; |
302 | unsigned int slots, slot_width; |
303 | int ret, i; |
304 | |
305 | slots = link_data->slots; |
306 | slot_width = link_data->slot_width; |
307 | |
308 | if (!format_is_tdm(link_data)) { |
309 | if (format_is_dsd(params)) { |
310 | slots = 1; |
311 | slot_width = params_width(p: params); |
312 | fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | |
313 | SND_SOC_DAIFMT_PDM; |
314 | } else { |
315 | slots = 2; |
316 | slot_width = params_physical_width(p: params); |
317 | fmt = (rtd->dai_link->dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | |
318 | SND_SOC_DAIFMT_I2S; |
319 | } |
320 | } |
321 | |
322 | ret = snd_soc_dai_set_fmt(dai: cpu_dai, fmt: snd_soc_daifmt_clock_provider_flipped(dai_fmt: fmt)); |
323 | if (ret && ret != -ENOTSUPP) { |
324 | dev_err(dev, "failed to set cpu dai fmt: %d\n" , ret); |
325 | return ret; |
326 | } |
327 | ret = snd_soc_dai_set_tdm_slot(dai: cpu_dai, |
328 | BIT(slots) - 1, |
329 | BIT(slots) - 1, |
330 | slots, slot_width); |
331 | if (ret && ret != -ENOTSUPP) { |
332 | dev_err(dev, "failed to set cpu dai tdm slot: %d\n" , ret); |
333 | return ret; |
334 | } |
335 | |
336 | for_each_rtd_codec_dais(rtd, i, codec_dai) { |
337 | ret = snd_soc_dai_set_fmt(dai: codec_dai, fmt); |
338 | if (ret && ret != -ENOTSUPP) { |
339 | dev_err(dev, "failed to set codec dai[%d] fmt: %d\n" , i, ret); |
340 | return ret; |
341 | } |
342 | |
343 | ret = snd_soc_dai_set_tdm_slot(dai: codec_dai, |
344 | BIT(slots) - 1, |
345 | BIT(slots) - 1, |
346 | slots, slot_width); |
347 | if (ret && ret != -ENOTSUPP) { |
348 | dev_err(dev, "failed to set codec dai[%d] tdm slot: %d\n" , i, ret); |
349 | return ret; |
350 | } |
351 | } |
352 | |
353 | /* Set MCLK freq */ |
354 | if (codec_is_akcodec(type: plat_data->type)) |
355 | mclk_freq = akcodec_get_mclk_rate(substream, params, slots, slot_width); |
356 | else |
357 | mclk_freq = params_rate(p: params) * slots * slot_width; |
358 | |
359 | if (format_is_dsd(params)) { |
360 | /* Use the maximum freq from DSD512 (512*44100 = 22579200) */ |
361 | if (!(params_rate(p: params) % 11025)) |
362 | mclk_freq = IMX_CARD_MCLK_22P5792MHZ; |
363 | else |
364 | mclk_freq = IMX_CARD_MCLK_24P576MHZ; |
365 | } |
366 | |
367 | ret = snd_soc_dai_set_sysclk(dai: cpu_dai, clk_id: link_data->cpu_sysclk_id, freq: mclk_freq, |
368 | SND_SOC_CLOCK_OUT); |
369 | if (ret && ret != -ENOTSUPP) { |
370 | dev_err(dev, "failed to set cpui dai mclk1 rate (%lu): %d\n" , mclk_freq, ret); |
371 | return ret; |
372 | } |
373 | |
374 | return 0; |
375 | } |
376 | |
377 | static int ak5558_hw_rule_rate(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *r) |
378 | { |
379 | struct dai_link_data *link_data = r->private; |
380 | struct snd_interval t = { .min = 8000, .max = 8000, }; |
381 | unsigned long mclk_freq; |
382 | unsigned int fs; |
383 | int i; |
384 | |
385 | fs = hw_param_interval(params: p, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; |
386 | fs *= link_data->slots; |
387 | |
388 | /* Identify maximum supported rate */ |
389 | for (i = 0; i < ARRAY_SIZE(akcodec_rates); i++) { |
390 | mclk_freq = fs * akcodec_rates[i]; |
391 | /* Adjust SAI bclk:mclk ratio */ |
392 | mclk_freq *= link_data->one2one_ratio ? 1 : 2; |
393 | |
394 | /* Skip rates for which MCLK is beyond supported value */ |
395 | if (mclk_freq > 36864000) |
396 | continue; |
397 | |
398 | if (t.max < akcodec_rates[i]) |
399 | t.max = akcodec_rates[i]; |
400 | } |
401 | |
402 | return snd_interval_refine(i: hw_param_interval(params: p, var: r->var), v: &t); |
403 | } |
404 | |
405 | static int imx_aif_startup(struct snd_pcm_substream *substream) |
406 | { |
407 | struct snd_pcm_runtime *runtime = substream->runtime; |
408 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
409 | struct snd_soc_card *card = rtd->card; |
410 | struct imx_card_data *data = snd_soc_card_get_drvdata(card); |
411 | struct dai_link_data *link_data = &data->link_data[rtd->num]; |
412 | static struct snd_pcm_hw_constraint_list constraint_rates; |
413 | static struct snd_pcm_hw_constraint_list constraint_channels; |
414 | int ret = 0; |
415 | |
416 | if (format_is_tdm(link_data)) { |
417 | constraint_channels.list = data->plat_data->support_tdm_channels; |
418 | constraint_channels.count = data->plat_data->num_tdm_channels; |
419 | constraint_rates.list = data->plat_data->support_tdm_rates; |
420 | constraint_rates.count = data->plat_data->num_tdm_rates; |
421 | } else { |
422 | constraint_channels.list = data->plat_data->support_channels; |
423 | constraint_channels.count = data->plat_data->num_channels; |
424 | constraint_rates.list = data->plat_data->support_rates; |
425 | constraint_rates.count = data->plat_data->num_rates; |
426 | } |
427 | |
428 | if (constraint_channels.count) { |
429 | ret = snd_pcm_hw_constraint_list(runtime, cond: 0, |
430 | SNDRV_PCM_HW_PARAM_CHANNELS, |
431 | l: &constraint_channels); |
432 | if (ret) |
433 | return ret; |
434 | } |
435 | |
436 | if (constraint_rates.count) { |
437 | ret = snd_pcm_hw_constraint_list(runtime, cond: 0, |
438 | SNDRV_PCM_HW_PARAM_RATE, |
439 | l: &constraint_rates); |
440 | if (ret) |
441 | return ret; |
442 | } |
443 | |
444 | if (data->plat_data->type == CODEC_AK5558) |
445 | ret = snd_pcm_hw_rule_add(runtime: substream->runtime, cond: 0, |
446 | SNDRV_PCM_HW_PARAM_RATE, |
447 | func: ak5558_hw_rule_rate, private: link_data, |
448 | SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); |
449 | |
450 | return ret; |
451 | } |
452 | |
453 | static const struct snd_soc_ops imx_aif_ops = { |
454 | .hw_params = imx_aif_hw_params, |
455 | .startup = imx_aif_startup, |
456 | }; |
457 | |
458 | static const struct snd_soc_ops imx_aif_ops_be = { |
459 | .hw_params = imx_aif_hw_params, |
460 | }; |
461 | |
462 | static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, |
463 | struct snd_pcm_hw_params *params) |
464 | { |
465 | struct snd_soc_card *card = rtd->card; |
466 | struct imx_card_data *data = snd_soc_card_get_drvdata(card); |
467 | struct snd_interval *rate; |
468 | struct snd_mask *mask; |
469 | |
470 | rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); |
471 | rate->max = data->asrc_rate; |
472 | rate->min = data->asrc_rate; |
473 | |
474 | mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); |
475 | snd_mask_none(mask); |
476 | snd_mask_set(mask, val: (__force unsigned int)data->asrc_format); |
477 | |
478 | return 0; |
479 | } |
480 | |
481 | static int imx_card_parse_of(struct imx_card_data *data) |
482 | { |
483 | struct imx_card_plat_data *plat_data = data->plat_data; |
484 | struct snd_soc_card *card = &data->card; |
485 | struct snd_soc_dai_link_component *dlc; |
486 | struct device_node *platform = NULL; |
487 | struct device_node *codec = NULL; |
488 | struct device_node *cpu = NULL; |
489 | struct device_node *np; |
490 | struct device *dev = card->dev; |
491 | struct snd_soc_dai_link *link; |
492 | struct dai_link_data *link_data; |
493 | struct of_phandle_args args; |
494 | int ret, num_links; |
495 | u32 asrc_fmt = 0; |
496 | u32 width; |
497 | |
498 | ret = snd_soc_of_parse_card_name(card, propname: "model" ); |
499 | if (ret) { |
500 | dev_err(dev, "Error parsing card name: %d\n" , ret); |
501 | return ret; |
502 | } |
503 | |
504 | /* DAPM routes */ |
505 | if (of_property_read_bool(np: dev->of_node, propname: "audio-routing" )) { |
506 | ret = snd_soc_of_parse_audio_routing(card, propname: "audio-routing" ); |
507 | if (ret) |
508 | return ret; |
509 | } |
510 | |
511 | /* Populate links */ |
512 | num_links = of_get_child_count(np: dev->of_node); |
513 | |
514 | /* Allocate the DAI link array */ |
515 | card->dai_link = devm_kcalloc(dev, n: num_links, size: sizeof(*link), GFP_KERNEL); |
516 | if (!card->dai_link) |
517 | return -ENOMEM; |
518 | |
519 | data->link_data = devm_kcalloc(dev, n: num_links, size: sizeof(*link), GFP_KERNEL); |
520 | if (!data->link_data) |
521 | return -ENOMEM; |
522 | |
523 | card->num_links = num_links; |
524 | link = card->dai_link; |
525 | link_data = data->link_data; |
526 | |
527 | for_each_child_of_node(dev->of_node, np) { |
528 | dlc = devm_kzalloc(dev, size: 2 * sizeof(*dlc), GFP_KERNEL); |
529 | if (!dlc) { |
530 | ret = -ENOMEM; |
531 | goto err_put_np; |
532 | } |
533 | |
534 | link->cpus = &dlc[0]; |
535 | link->platforms = &dlc[1]; |
536 | |
537 | link->num_cpus = 1; |
538 | link->num_platforms = 1; |
539 | |
540 | ret = of_property_read_string(np, propname: "link-name" , out_string: &link->name); |
541 | if (ret) { |
542 | dev_err(card->dev, "error getting codec dai_link name\n" ); |
543 | goto err_put_np; |
544 | } |
545 | |
546 | cpu = of_get_child_by_name(node: np, name: "cpu" ); |
547 | if (!cpu) { |
548 | dev_err(dev, "%s: Can't find cpu DT node\n" , link->name); |
549 | ret = -EINVAL; |
550 | goto err; |
551 | } |
552 | |
553 | ret = snd_soc_of_get_dlc(of_node: cpu, args: &args, dlc: link->cpus, index: 0); |
554 | if (ret) { |
555 | dev_err_probe(dev: card->dev, err: ret, |
556 | fmt: "%s: error getting cpu dai info\n" , link->name); |
557 | goto err; |
558 | } |
559 | |
560 | if (of_node_name_eq(np: args.np, name: "sai" )) { |
561 | /* sai sysclk id */ |
562 | link_data->cpu_sysclk_id = FSL_SAI_CLK_MAST1; |
563 | |
564 | /* sai may support mclk/bclk = 1 */ |
565 | if (of_property_read_bool(np, propname: "fsl,mclk-equal-bclk" )) { |
566 | link_data->one2one_ratio = true; |
567 | } else { |
568 | int i; |
569 | |
570 | /* |
571 | * i.MX8MQ don't support one2one ratio, then |
572 | * with ak4497 only 16bit case is supported. |
573 | */ |
574 | for (i = 0; i < ARRAY_SIZE(ak4497_fs_mul); i++) { |
575 | if (ak4497_fs_mul[i].rmin == 705600 && |
576 | ak4497_fs_mul[i].rmax == 768000) { |
577 | ak4497_fs_mul[i].wmin = 32; |
578 | ak4497_fs_mul[i].wmax = 32; |
579 | } |
580 | } |
581 | } |
582 | } |
583 | |
584 | link->platforms->of_node = link->cpus->of_node; |
585 | link->id = args.args[0]; |
586 | |
587 | codec = of_get_child_by_name(node: np, name: "codec" ); |
588 | if (codec) { |
589 | ret = snd_soc_of_get_dai_link_codecs(dev, of_node: codec, dai_link: link); |
590 | if (ret < 0) { |
591 | dev_err_probe(dev, err: ret, fmt: "%s: codec dai not found\n" , |
592 | link->name); |
593 | goto err; |
594 | } |
595 | |
596 | plat_data->num_codecs = link->num_codecs; |
597 | |
598 | /* Check the akcodec type */ |
599 | if (!strcmp(link->codecs->dai_name, "ak4458-aif" )) |
600 | plat_data->type = CODEC_AK4458; |
601 | else if (!strcmp(link->codecs->dai_name, "ak4497-aif" )) |
602 | plat_data->type = CODEC_AK4497; |
603 | else if (!strcmp(link->codecs->dai_name, "ak5558-aif" )) |
604 | plat_data->type = CODEC_AK5558; |
605 | else if (!strcmp(link->codecs->dai_name, "ak5552-aif" )) |
606 | plat_data->type = CODEC_AK5552; |
607 | |
608 | } else { |
609 | link->codecs = &snd_soc_dummy_dlc; |
610 | link->num_codecs = 1; |
611 | } |
612 | |
613 | if (!strncmp(link->name, "HiFi-ASRC-FE" , 12)) { |
614 | /* DPCM frontend */ |
615 | link->dynamic = 1; |
616 | link->dpcm_merged_chan = 1; |
617 | |
618 | ret = of_property_read_u32(np: args.np, propname: "fsl,asrc-rate" , out_value: &data->asrc_rate); |
619 | if (ret) { |
620 | dev_err(dev, "failed to get output rate\n" ); |
621 | ret = -EINVAL; |
622 | goto err; |
623 | } |
624 | |
625 | ret = of_property_read_u32(np: args.np, propname: "fsl,asrc-format" , out_value: &asrc_fmt); |
626 | data->asrc_format = (__force snd_pcm_format_t)asrc_fmt; |
627 | if (ret) { |
628 | /* Fallback to old binding; translate to asrc_format */ |
629 | ret = of_property_read_u32(np: args.np, propname: "fsl,asrc-width" , out_value: &width); |
630 | if (ret) { |
631 | dev_err(dev, |
632 | "failed to decide output format\n" ); |
633 | goto err; |
634 | } |
635 | |
636 | if (width == 24) |
637 | data->asrc_format = SNDRV_PCM_FORMAT_S24_LE; |
638 | else |
639 | data->asrc_format = SNDRV_PCM_FORMAT_S16_LE; |
640 | } |
641 | } else if (!strncmp(link->name, "HiFi-ASRC-BE" , 12)) { |
642 | /* DPCM backend */ |
643 | link->no_pcm = 1; |
644 | link->platforms->of_node = NULL; |
645 | link->platforms->name = "snd-soc-dummy" ; |
646 | |
647 | link->be_hw_params_fixup = be_hw_params_fixup; |
648 | link->ops = &imx_aif_ops_be; |
649 | } else { |
650 | link->ops = &imx_aif_ops; |
651 | } |
652 | |
653 | if (link->no_pcm || link->dynamic) |
654 | snd_soc_dai_link_set_capabilities(dai_link: link); |
655 | |
656 | /* Get dai fmt */ |
657 | ret = simple_util_parse_daifmt(dev, node: np, codec, |
658 | NULL, retfmt: &link->dai_fmt); |
659 | if (ret) |
660 | link->dai_fmt = SND_SOC_DAIFMT_NB_NF | |
661 | SND_SOC_DAIFMT_CBC_CFC | |
662 | SND_SOC_DAIFMT_I2S; |
663 | |
664 | /* Get tdm slot */ |
665 | snd_soc_of_parse_tdm_slot(np, NULL, NULL, |
666 | slots: &link_data->slots, |
667 | slot_width: &link_data->slot_width); |
668 | /* default value */ |
669 | if (!link_data->slots) |
670 | link_data->slots = 2; |
671 | |
672 | if (!link_data->slot_width) |
673 | link_data->slot_width = 32; |
674 | |
675 | link->ignore_pmdown_time = 1; |
676 | link->stream_name = link->name; |
677 | link++; |
678 | link_data++; |
679 | |
680 | of_node_put(node: cpu); |
681 | of_node_put(node: codec); |
682 | of_node_put(node: platform); |
683 | |
684 | cpu = NULL; |
685 | codec = NULL; |
686 | platform = NULL; |
687 | } |
688 | |
689 | return 0; |
690 | err: |
691 | of_node_put(node: cpu); |
692 | of_node_put(node: codec); |
693 | of_node_put(node: platform); |
694 | err_put_np: |
695 | of_node_put(node: np); |
696 | return ret; |
697 | } |
698 | |
699 | static int imx_card_probe(struct platform_device *pdev) |
700 | { |
701 | struct snd_soc_dai_link *link_be = NULL, *link; |
702 | struct imx_card_plat_data *plat_data; |
703 | struct imx_card_data *data; |
704 | int ret, i; |
705 | |
706 | data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*data), GFP_KERNEL); |
707 | if (!data) |
708 | return -ENOMEM; |
709 | |
710 | plat_data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*plat_data), GFP_KERNEL); |
711 | if (!plat_data) |
712 | return -ENOMEM; |
713 | |
714 | data->plat_data = plat_data; |
715 | data->card.dev = &pdev->dev; |
716 | |
717 | dev_set_drvdata(dev: &pdev->dev, data: &data->card); |
718 | snd_soc_card_set_drvdata(card: &data->card, data); |
719 | ret = imx_card_parse_of(data); |
720 | if (ret) |
721 | return ret; |
722 | |
723 | data->num_dapm_routes = plat_data->num_codecs + 1; |
724 | data->dapm_routes = devm_kcalloc(dev: &pdev->dev, n: data->num_dapm_routes, |
725 | size: sizeof(struct snd_soc_dapm_route), |
726 | GFP_KERNEL); |
727 | if (!data->dapm_routes) |
728 | return -ENOMEM; |
729 | |
730 | /* configure the dapm routes */ |
731 | switch (plat_data->type) { |
732 | case CODEC_AK4458: |
733 | case CODEC_AK4497: |
734 | if (plat_data->num_codecs == 1) { |
735 | data->dapm_routes[0].sink = "Playback" ; |
736 | data->dapm_routes[0].source = "CPU-Playback" ; |
737 | i = 1; |
738 | } else { |
739 | for (i = 0; i < plat_data->num_codecs; i++) { |
740 | data->dapm_routes[i].sink = |
741 | devm_kasprintf(dev: &pdev->dev, GFP_KERNEL, fmt: "%d %s" , |
742 | i + 1, "Playback" ); |
743 | data->dapm_routes[i].source = "CPU-Playback" ; |
744 | } |
745 | } |
746 | data->dapm_routes[i].sink = "CPU-Playback" ; |
747 | data->dapm_routes[i].source = "ASRC-Playback" ; |
748 | break; |
749 | case CODEC_AK5558: |
750 | case CODEC_AK5552: |
751 | if (plat_data->num_codecs == 1) { |
752 | data->dapm_routes[0].sink = "CPU-Capture" ; |
753 | data->dapm_routes[0].source = "Capture" ; |
754 | i = 1; |
755 | } else { |
756 | for (i = 0; i < plat_data->num_codecs; i++) { |
757 | data->dapm_routes[i].source = |
758 | devm_kasprintf(dev: &pdev->dev, GFP_KERNEL, fmt: "%d %s" , |
759 | i + 1, "Capture" ); |
760 | data->dapm_routes[i].sink = "CPU-Capture" ; |
761 | } |
762 | } |
763 | data->dapm_routes[i].sink = "ASRC-Capture" ; |
764 | data->dapm_routes[i].source = "CPU-Capture" ; |
765 | break; |
766 | default: |
767 | break; |
768 | } |
769 | |
770 | /* default platform data for akcodecs */ |
771 | if (codec_is_akcodec(type: plat_data->type)) { |
772 | plat_data->support_rates = akcodec_rates; |
773 | plat_data->num_rates = ARRAY_SIZE(akcodec_rates); |
774 | plat_data->support_tdm_rates = akcodec_tdm_rates; |
775 | plat_data->num_tdm_rates = ARRAY_SIZE(akcodec_tdm_rates); |
776 | |
777 | switch (plat_data->type) { |
778 | case CODEC_AK4458: |
779 | plat_data->fs_mul = ak4458_fs_mul; |
780 | plat_data->num_fs_mul = ARRAY_SIZE(ak4458_fs_mul); |
781 | plat_data->tdm_fs_mul = ak4458_tdm_fs_mul; |
782 | plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak4458_tdm_fs_mul); |
783 | plat_data->support_channels = ak4458_channels; |
784 | plat_data->num_channels = ARRAY_SIZE(ak4458_channels); |
785 | plat_data->support_tdm_channels = ak4458_tdm_channels; |
786 | plat_data->num_tdm_channels = ARRAY_SIZE(ak4458_tdm_channels); |
787 | break; |
788 | case CODEC_AK4497: |
789 | plat_data->fs_mul = ak4497_fs_mul; |
790 | plat_data->num_fs_mul = ARRAY_SIZE(ak4497_fs_mul); |
791 | plat_data->support_channels = ak4458_channels; |
792 | plat_data->num_channels = ARRAY_SIZE(ak4458_channels); |
793 | break; |
794 | case CODEC_AK5558: |
795 | case CODEC_AK5552: |
796 | plat_data->fs_mul = ak5558_fs_mul; |
797 | plat_data->num_fs_mul = ARRAY_SIZE(ak5558_fs_mul); |
798 | plat_data->tdm_fs_mul = ak5558_tdm_fs_mul; |
799 | plat_data->num_tdm_fs_mul = ARRAY_SIZE(ak5558_tdm_fs_mul); |
800 | plat_data->support_channels = ak5558_channels; |
801 | plat_data->num_channels = ARRAY_SIZE(ak5558_channels); |
802 | plat_data->support_tdm_channels = ak5558_tdm_channels; |
803 | plat_data->num_tdm_channels = ARRAY_SIZE(ak5558_tdm_channels); |
804 | break; |
805 | default: |
806 | break; |
807 | } |
808 | } |
809 | |
810 | /* with asrc as front end */ |
811 | if (data->card.num_links == 3) { |
812 | data->card.dapm_routes = data->dapm_routes; |
813 | data->card.num_dapm_routes = data->num_dapm_routes; |
814 | for_each_card_prelinks(&data->card, i, link) { |
815 | if (link->no_pcm == 1) |
816 | link_be = link; |
817 | } |
818 | for_each_card_prelinks(&data->card, i, link) { |
819 | if (link->dynamic == 1 && link_be) { |
820 | link->dpcm_playback = link_be->dpcm_playback; |
821 | link->dpcm_capture = link_be->dpcm_capture; |
822 | } |
823 | } |
824 | } |
825 | |
826 | ret = devm_snd_soc_register_card(dev: &pdev->dev, card: &data->card); |
827 | if (ret) |
828 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "snd_soc_register_card failed\n" ); |
829 | |
830 | return 0; |
831 | } |
832 | |
833 | static const struct of_device_id imx_card_dt_ids[] = { |
834 | { .compatible = "fsl,imx-audio-card" , }, |
835 | { }, |
836 | }; |
837 | MODULE_DEVICE_TABLE(of, imx_card_dt_ids); |
838 | |
839 | static struct platform_driver imx_card_driver = { |
840 | .driver = { |
841 | .name = "imx-card" , |
842 | .pm = &snd_soc_pm_ops, |
843 | .of_match_table = imx_card_dt_ids, |
844 | }, |
845 | .probe = imx_card_probe, |
846 | }; |
847 | module_platform_driver(imx_card_driver); |
848 | |
849 | MODULE_DESCRIPTION("Freescale i.MX ASoC Machine Driver" ); |
850 | MODULE_LICENSE("GPL v2" ); |
851 | MODULE_ALIAS("platform:imx-card" ); |
852 | |