Merge tag 'sunxi-fixes-for-4.3' of https://git.kernel.org/pub/scm/linux/kernel/git...
[linux-drm-fsl-dcu.git] / sound / soc / fsl / fsl-asoc-card.c
1 /*
2  * Freescale Generic ASoC Sound Card driver with ASRC
3  *
4  * Copyright (C) 2014 Freescale Semiconductor, Inc.
5  *
6  * Author: Nicolin Chen <nicoleotsuka@gmail.com>
7  *
8  * This file is licensed under the terms of the GNU General Public License
9  * version 2. This program is licensed "as is" without any warranty of any
10  * kind, whether express or implied.
11  */
12
13 #include <linux/clk.h>
14 #include <linux/i2c.h>
15 #include <linux/module.h>
16 #include <linux/of_platform.h>
17 #include <sound/pcm_params.h>
18 #include <sound/soc.h>
19
20 #include "fsl_esai.h"
21 #include "fsl_sai.h"
22 #include "imx-audmux.h"
23
24 #include "../codecs/sgtl5000.h"
25 #include "../codecs/wm8962.h"
26 #include "../codecs/wm8960.h"
27
28 #define RX 0
29 #define TX 1
30
31 /* Default DAI format without Master and Slave flag */
32 #define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
33
34 /**
35  * CODEC private data
36  *
37  * @mclk_freq: Clock rate of MCLK
38  * @mclk_id: MCLK (or main clock) id for set_sysclk()
39  * @fll_id: FLL (or secordary clock) id for set_sysclk()
40  * @pll_id: PLL id for set_pll()
41  */
42 struct codec_priv {
43         unsigned long mclk_freq;
44         u32 mclk_id;
45         u32 fll_id;
46         u32 pll_id;
47 };
48
49 /**
50  * CPU private data
51  *
52  * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
53  * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
54  * @sysclk_id[2]: SYSCLK ids for set_sysclk()
55  * @slot_width: Slot width of each frame
56  *
57  * Note: [1] for tx and [0] for rx
58  */
59 struct cpu_priv {
60         unsigned long sysclk_freq[2];
61         u32 sysclk_dir[2];
62         u32 sysclk_id[2];
63         u32 slot_width;
64 };
65
66 /**
67  * Freescale Generic ASOC card private data
68  *
69  * @dai_link[3]: DAI link structure including normal one and DPCM link
70  * @pdev: platform device pointer
71  * @codec_priv: CODEC private data
72  * @cpu_priv: CPU private data
73  * @card: ASoC card structure
74  * @sample_rate: Current sample rate
75  * @sample_format: Current sample format
76  * @asrc_rate: ASRC sample rate used by Back-Ends
77  * @asrc_format: ASRC sample format used by Back-Ends
78  * @dai_fmt: DAI format between CPU and CODEC
79  * @name: Card name
80  */
81
82 struct fsl_asoc_card_priv {
83         struct snd_soc_dai_link dai_link[3];
84         struct platform_device *pdev;
85         struct codec_priv codec_priv;
86         struct cpu_priv cpu_priv;
87         struct snd_soc_card card;
88         u32 sample_rate;
89         u32 sample_format;
90         u32 asrc_rate;
91         u32 asrc_format;
92         u32 dai_fmt;
93         char name[32];
94 };
95
96 /**
97  * This dapm route map exsits for DPCM link only.
98  * The other routes shall go through Device Tree.
99  */
100 static const struct snd_soc_dapm_route audio_map[] = {
101         {"CPU-Playback",  NULL, "ASRC-Playback"},
102         {"Playback",  NULL, "CPU-Playback"},
103         {"ASRC-Capture",  NULL, "CPU-Capture"},
104         {"CPU-Capture",  NULL, "Capture"},
105 };
106
107 /* Add all possible widgets into here without being redundant */
108 static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
109         SND_SOC_DAPM_LINE("Line Out Jack", NULL),
110         SND_SOC_DAPM_LINE("Line In Jack", NULL),
111         SND_SOC_DAPM_HP("Headphone Jack", NULL),
112         SND_SOC_DAPM_SPK("Ext Spk", NULL),
113         SND_SOC_DAPM_MIC("Mic Jack", NULL),
114         SND_SOC_DAPM_MIC("AMIC", NULL),
115         SND_SOC_DAPM_MIC("DMIC", NULL),
116 };
117
118 static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
119                                    struct snd_pcm_hw_params *params)
120 {
121         struct snd_soc_pcm_runtime *rtd = substream->private_data;
122         struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
123         bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
124         struct cpu_priv *cpu_priv = &priv->cpu_priv;
125         struct device *dev = rtd->card->dev;
126         int ret;
127
128         priv->sample_rate = params_rate(params);
129         priv->sample_format = params_format(params);
130
131         /*
132          * If codec-dai is DAI Master and all configurations are already in the
133          * set_bias_level(), bypass the remaining settings in hw_params().
134          * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
135          */
136         if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
137                 return 0;
138
139         /* Specific configurations of DAIs starts from here */
140         ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx],
141                                      cpu_priv->sysclk_freq[tx],
142                                      cpu_priv->sysclk_dir[tx]);
143         if (ret) {
144                 dev_err(dev, "failed to set sysclk for cpu dai\n");
145                 return ret;
146         }
147
148         if (cpu_priv->slot_width) {
149                 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2,
150                                                cpu_priv->slot_width);
151                 if (ret) {
152                         dev_err(dev, "failed to set TDM slot for cpu dai\n");
153                         return ret;
154                 }
155         }
156
157         return 0;
158 }
159
160 static struct snd_soc_ops fsl_asoc_card_ops = {
161         .hw_params = fsl_asoc_card_hw_params,
162 };
163
164 static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
165                               struct snd_pcm_hw_params *params)
166 {
167         struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
168         struct snd_interval *rate;
169         struct snd_mask *mask;
170
171         rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
172         rate->max = rate->min = priv->asrc_rate;
173
174         mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
175         snd_mask_none(mask);
176         snd_mask_set(mask, priv->asrc_format);
177
178         return 0;
179 }
180
181 static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
182         /* Default ASoC DAI Link*/
183         {
184                 .name = "HiFi",
185                 .stream_name = "HiFi",
186                 .ops = &fsl_asoc_card_ops,
187         },
188         /* DPCM Link between Front-End and Back-End (Optional) */
189         {
190                 .name = "HiFi-ASRC-FE",
191                 .stream_name = "HiFi-ASRC-FE",
192                 .codec_name = "snd-soc-dummy",
193                 .codec_dai_name = "snd-soc-dummy-dai",
194                 .dpcm_playback = 1,
195                 .dpcm_capture = 1,
196                 .dynamic = 1,
197         },
198         {
199                 .name = "HiFi-ASRC-BE",
200                 .stream_name = "HiFi-ASRC-BE",
201                 .platform_name = "snd-soc-dummy",
202                 .be_hw_params_fixup = be_hw_params_fixup,
203                 .ops = &fsl_asoc_card_ops,
204                 .dpcm_playback = 1,
205                 .dpcm_capture = 1,
206                 .no_pcm = 1,
207         },
208 };
209
210 static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
211                                         struct snd_soc_dapm_context *dapm,
212                                         enum snd_soc_bias_level level)
213 {
214         struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
215         struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
216         struct codec_priv *codec_priv = &priv->codec_priv;
217         struct device *dev = card->dev;
218         unsigned int pll_out;
219         int ret;
220
221         if (dapm->dev != codec_dai->dev)
222                 return 0;
223
224         switch (level) {
225         case SND_SOC_BIAS_PREPARE:
226                 if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
227                         break;
228
229                 if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
230                         pll_out = priv->sample_rate * 384;
231                 else
232                         pll_out = priv->sample_rate * 256;
233
234                 ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id,
235                                           codec_priv->mclk_id,
236                                           codec_priv->mclk_freq, pll_out);
237                 if (ret) {
238                         dev_err(dev, "failed to start FLL: %d\n", ret);
239                         return ret;
240                 }
241
242                 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id,
243                                              pll_out, SND_SOC_CLOCK_IN);
244                 if (ret) {
245                         dev_err(dev, "failed to set SYSCLK: %d\n", ret);
246                         return ret;
247                 }
248                 break;
249
250         case SND_SOC_BIAS_STANDBY:
251                 if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
252                         break;
253
254                 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
255                                              codec_priv->mclk_freq,
256                                              SND_SOC_CLOCK_IN);
257                 if (ret) {
258                         dev_err(dev, "failed to switch away from FLL: %d\n", ret);
259                         return ret;
260                 }
261
262                 ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0);
263                 if (ret) {
264                         dev_err(dev, "failed to stop FLL: %d\n", ret);
265                         return ret;
266                 }
267                 break;
268
269         default:
270                 break;
271         }
272
273         return 0;
274 }
275
276 static int fsl_asoc_card_audmux_init(struct device_node *np,
277                                      struct fsl_asoc_card_priv *priv)
278 {
279         struct device *dev = &priv->pdev->dev;
280         u32 int_ptcr = 0, ext_ptcr = 0;
281         int int_port, ext_port;
282         int ret;
283
284         ret = of_property_read_u32(np, "mux-int-port", &int_port);
285         if (ret) {
286                 dev_err(dev, "mux-int-port missing or invalid\n");
287                 return ret;
288         }
289         ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
290         if (ret) {
291                 dev_err(dev, "mux-ext-port missing or invalid\n");
292                 return ret;
293         }
294
295         /*
296          * The port numbering in the hardware manual starts at 1, while
297          * the AUDMUX API expects it starts at 0.
298          */
299         int_port--;
300         ext_port--;
301
302         /*
303          * Use asynchronous mode (6 wires) for all cases.
304          * If only 4 wires are needed, just set SSI into
305          * synchronous mode and enable 4 PADs in IOMUX.
306          */
307         switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
308         case SND_SOC_DAIFMT_CBM_CFM:
309                 int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
310                            IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
311                            IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
312                            IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
313                            IMX_AUDMUX_V2_PTCR_RFSDIR |
314                            IMX_AUDMUX_V2_PTCR_RCLKDIR |
315                            IMX_AUDMUX_V2_PTCR_TFSDIR |
316                            IMX_AUDMUX_V2_PTCR_TCLKDIR;
317                 break;
318         case SND_SOC_DAIFMT_CBM_CFS:
319                 int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
320                            IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
321                            IMX_AUDMUX_V2_PTCR_RCLKDIR |
322                            IMX_AUDMUX_V2_PTCR_TCLKDIR;
323                 ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
324                            IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
325                            IMX_AUDMUX_V2_PTCR_RFSDIR |
326                            IMX_AUDMUX_V2_PTCR_TFSDIR;
327                 break;
328         case SND_SOC_DAIFMT_CBS_CFM:
329                 int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
330                            IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
331                            IMX_AUDMUX_V2_PTCR_RFSDIR |
332                            IMX_AUDMUX_V2_PTCR_TFSDIR;
333                 ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
334                            IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
335                            IMX_AUDMUX_V2_PTCR_RCLKDIR |
336                            IMX_AUDMUX_V2_PTCR_TCLKDIR;
337                 break;
338         case SND_SOC_DAIFMT_CBS_CFS:
339                 ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
340                            IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
341                            IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
342                            IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
343                            IMX_AUDMUX_V2_PTCR_RFSDIR |
344                            IMX_AUDMUX_V2_PTCR_RCLKDIR |
345                            IMX_AUDMUX_V2_PTCR_TFSDIR |
346                            IMX_AUDMUX_V2_PTCR_TCLKDIR;
347                 break;
348         default:
349                 return -EINVAL;
350         }
351
352         /* Asynchronous mode can not be set along with RCLKDIR */
353         ret = imx_audmux_v2_configure_port(int_port, 0,
354                                            IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
355         if (ret) {
356                 dev_err(dev, "audmux internal port setup failed\n");
357                 return ret;
358         }
359
360         ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
361                                            IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
362         if (ret) {
363                 dev_err(dev, "audmux internal port setup failed\n");
364                 return ret;
365         }
366
367         ret = imx_audmux_v2_configure_port(ext_port, 0,
368                                            IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
369         if (ret) {
370                 dev_err(dev, "audmux external port setup failed\n");
371                 return ret;
372         }
373
374         ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
375                                            IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
376         if (ret) {
377                 dev_err(dev, "audmux external port setup failed\n");
378                 return ret;
379         }
380
381         return 0;
382 }
383
384 static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
385 {
386         struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
387         struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
388         struct codec_priv *codec_priv = &priv->codec_priv;
389         struct device *dev = card->dev;
390         int ret;
391
392         ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
393                                      codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
394         if (ret) {
395                 dev_err(dev, "failed to set sysclk in %s\n", __func__);
396                 return ret;
397         }
398
399         return 0;
400 }
401
402 static int fsl_asoc_card_probe(struct platform_device *pdev)
403 {
404         struct device_node *cpu_np, *codec_np, *asrc_np;
405         struct device_node *np = pdev->dev.of_node;
406         struct platform_device *asrc_pdev = NULL;
407         struct platform_device *cpu_pdev;
408         struct fsl_asoc_card_priv *priv;
409         struct i2c_client *codec_dev;
410         struct clk *codec_clk;
411         const char *codec_dai_name;
412         u32 width;
413         int ret;
414
415         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
416         if (!priv)
417                 return -ENOMEM;
418
419         cpu_np = of_parse_phandle(np, "audio-cpu", 0);
420         /* Give a chance to old DT binding */
421         if (!cpu_np)
422                 cpu_np = of_parse_phandle(np, "ssi-controller", 0);
423         codec_np = of_parse_phandle(np, "audio-codec", 0);
424         if (!cpu_np || !codec_np) {
425                 dev_err(&pdev->dev, "phandle missing or invalid\n");
426                 ret = -EINVAL;
427                 goto fail;
428         }
429
430         cpu_pdev = of_find_device_by_node(cpu_np);
431         if (!cpu_pdev) {
432                 dev_err(&pdev->dev, "failed to find CPU DAI device\n");
433                 ret = -EINVAL;
434                 goto fail;
435         }
436
437         codec_dev = of_find_i2c_device_by_node(codec_np);
438         if (!codec_dev) {
439                 dev_err(&pdev->dev, "failed to find codec platform device\n");
440                 ret = -EINVAL;
441                 goto fail;
442         }
443
444         asrc_np = of_parse_phandle(np, "audio-asrc", 0);
445         if (asrc_np)
446                 asrc_pdev = of_find_device_by_node(asrc_np);
447
448         /* Get the MCLK rate only, and leave it controlled by CODEC drivers */
449         codec_clk = clk_get(&codec_dev->dev, NULL);
450         if (!IS_ERR(codec_clk)) {
451                 priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
452                 clk_put(codec_clk);
453         }
454
455         /* Default sample rate and format, will be updated in hw_params() */
456         priv->sample_rate = 44100;
457         priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
458
459         /* Assign a default DAI format, and allow each card to overwrite it */
460         priv->dai_fmt = DAI_FMT_BASE;
461
462         /* Diversify the card configurations */
463         if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
464                 codec_dai_name = "cs42888";
465                 priv->card.set_bias_level = NULL;
466                 priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
467                 priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
468                 priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
469                 priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
470                 priv->cpu_priv.slot_width = 32;
471                 priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
472         } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
473                 codec_dai_name = "sgtl5000";
474                 priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
475                 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
476         } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
477                 codec_dai_name = "wm8962";
478                 priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
479                 priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
480                 priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
481                 priv->codec_priv.pll_id = WM8962_FLL;
482                 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
483         } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
484                 codec_dai_name = "wm8960-hifi";
485                 priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
486                 priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
487                 priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
488                 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
489         } else {
490                 dev_err(&pdev->dev, "unknown Device Tree compatible\n");
491                 ret = -EINVAL;
492                 goto asrc_fail;
493         }
494
495         /* Common settings for corresponding Freescale CPU DAI driver */
496         if (strstr(cpu_np->name, "ssi")) {
497                 /* Only SSI needs to configure AUDMUX */
498                 ret = fsl_asoc_card_audmux_init(np, priv);
499                 if (ret) {
500                         dev_err(&pdev->dev, "failed to init audmux\n");
501                         goto asrc_fail;
502                 }
503         } else if (strstr(cpu_np->name, "esai")) {
504                 priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL;
505                 priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL;
506         } else if (strstr(cpu_np->name, "sai")) {
507                 priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
508                 priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
509         }
510
511         sprintf(priv->name, "%s-audio", codec_dev->name);
512
513         /* Initialize sound card */
514         priv->pdev = pdev;
515         priv->card.dev = &pdev->dev;
516         priv->card.name = priv->name;
517         priv->card.dai_link = priv->dai_link;
518         priv->card.dapm_routes = audio_map;
519         priv->card.late_probe = fsl_asoc_card_late_probe;
520         priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
521         priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
522         priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
523
524         memcpy(priv->dai_link, fsl_asoc_card_dai,
525                sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
526
527         ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing");
528         if (ret) {
529                 dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
530                 goto asrc_fail;
531         }
532
533         /* Normal DAI Link */
534         priv->dai_link[0].cpu_of_node = cpu_np;
535         priv->dai_link[0].codec_of_node = codec_np;
536         priv->dai_link[0].codec_dai_name = codec_dai_name;
537         priv->dai_link[0].platform_of_node = cpu_np;
538         priv->dai_link[0].dai_fmt = priv->dai_fmt;
539         priv->card.num_links = 1;
540
541         if (asrc_pdev) {
542                 /* DPCM DAI Links only if ASRC exsits */
543                 priv->dai_link[1].cpu_of_node = asrc_np;
544                 priv->dai_link[1].platform_of_node = asrc_np;
545                 priv->dai_link[2].codec_dai_name = codec_dai_name;
546                 priv->dai_link[2].codec_of_node = codec_np;
547                 priv->dai_link[2].cpu_of_node = cpu_np;
548                 priv->dai_link[2].dai_fmt = priv->dai_fmt;
549                 priv->card.num_links = 3;
550
551                 ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
552                                            &priv->asrc_rate);
553                 if (ret) {
554                         dev_err(&pdev->dev, "failed to get output rate\n");
555                         ret = -EINVAL;
556                         goto asrc_fail;
557                 }
558
559                 ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
560                 if (ret) {
561                         dev_err(&pdev->dev, "failed to get output rate\n");
562                         ret = -EINVAL;
563                         goto asrc_fail;
564                 }
565
566                 if (width == 24)
567                         priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
568                 else
569                         priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
570         }
571
572         /* Finish card registering */
573         platform_set_drvdata(pdev, priv);
574         snd_soc_card_set_drvdata(&priv->card, priv);
575
576         ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
577         if (ret)
578                 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
579
580 asrc_fail:
581         of_node_put(asrc_np);
582 fail:
583         of_node_put(codec_np);
584         of_node_put(cpu_np);
585
586         return ret;
587 }
588
589 static const struct of_device_id fsl_asoc_card_dt_ids[] = {
590         { .compatible = "fsl,imx-audio-cs42888", },
591         { .compatible = "fsl,imx-audio-sgtl5000", },
592         { .compatible = "fsl,imx-audio-wm8962", },
593         { .compatible = "fsl,imx-audio-wm8960", },
594         {}
595 };
596
597 static struct platform_driver fsl_asoc_card_driver = {
598         .probe = fsl_asoc_card_probe,
599         .driver = {
600                 .name = "fsl-asoc-card",
601                 .pm = &snd_soc_pm_ops,
602                 .of_match_table = fsl_asoc_card_dt_ids,
603         },
604 };
605 module_platform_driver(fsl_asoc_card_driver);
606
607 MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
608 MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
609 MODULE_ALIAS("platform:fsl-asoc-card");
610 MODULE_LICENSE("GPL");