Version:  2.0.40 2.2.26 2.4.37 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 4.0 4.1 4.2 4.3 4.4 4.5 4.6

Linux/sound/soc/samsung/bells.c

  1 /*
  2  * Bells audio support
  3  *
  4  * Copyright 2012 Wolfson Microelectronics
  5  *
  6  * This program is free software; you can redistribute  it and/or modify it
  7  * under  the terms of  the GNU General  Public License as published by the
  8  * Free Software Foundation;  either version 2 of the  License, or (at your
  9  * option) any later version.
 10  */
 11 
 12 #include <sound/soc.h>
 13 #include <sound/soc-dapm.h>
 14 #include <sound/jack.h>
 15 #include <linux/gpio.h>
 16 #include <linux/module.h>
 17 
 18 #include "../codecs/wm5102.h"
 19 #include "../codecs/wm9081.h"
 20 
 21 /* BCLK2 is fixed at this currently */
 22 #define BCLK2_RATE (64 * 8000)
 23 
 24 /*
 25  * Expect a 24.576MHz crystal if one is fitted (the driver will function
 26  * if this is not fitted).
 27  */
 28 #define MCLK_RATE 24576000
 29 
 30 #define SYS_AUDIO_RATE 44100
 31 #define SYS_MCLK_RATE  (SYS_AUDIO_RATE * 512)
 32 
 33 #define DAI_AP_DSP    0
 34 #define DAI_DSP_CODEC 1
 35 #define DAI_CODEC_CP  2
 36 #define DAI_CODEC_SUB 3
 37 
 38 struct bells_drvdata {
 39         int sysclk_rate;
 40         int asyncclk_rate;
 41 };
 42 
 43 static struct bells_drvdata wm2200_drvdata = {
 44         .sysclk_rate = 22579200,
 45 };
 46 
 47 static struct bells_drvdata wm5102_drvdata = {
 48         .sysclk_rate = 45158400,
 49         .asyncclk_rate = 49152000,
 50 };
 51 
 52 static struct bells_drvdata wm5110_drvdata = {
 53         .sysclk_rate = 135475200,
 54         .asyncclk_rate = 147456000,
 55 };
 56 
 57 static int bells_set_bias_level(struct snd_soc_card *card,
 58                                 struct snd_soc_dapm_context *dapm,
 59                                 enum snd_soc_bias_level level)
 60 {
 61         struct snd_soc_pcm_runtime *rtd;
 62         struct snd_soc_dai *codec_dai;
 63         struct snd_soc_codec *codec;
 64         struct bells_drvdata *bells = card->drvdata;
 65         int ret;
 66 
 67         rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
 68         codec_dai = rtd->codec_dai;
 69         codec = codec_dai->codec;
 70 
 71         if (dapm->dev != codec_dai->dev)
 72                 return 0;
 73 
 74         switch (level) {
 75         case SND_SOC_BIAS_PREPARE:
 76                 if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
 77                         break;
 78 
 79                 ret = snd_soc_codec_set_pll(codec, WM5102_FLL1,
 80                                             ARIZONA_FLL_SRC_MCLK1,
 81                                             MCLK_RATE,
 82                                             bells->sysclk_rate);
 83                 if (ret < 0)
 84                         pr_err("Failed to start FLL: %d\n", ret);
 85 
 86                 if (bells->asyncclk_rate) {
 87                         ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
 88                                                     ARIZONA_FLL_SRC_AIF2BCLK,
 89                                                     BCLK2_RATE,
 90                                                     bells->asyncclk_rate);
 91                         if (ret < 0)
 92                                 pr_err("Failed to start FLL: %d\n", ret);
 93                 }
 94                 break;
 95 
 96         default:
 97                 break;
 98         }
 99 
100         return 0;
101 }
102 
103 static int bells_set_bias_level_post(struct snd_soc_card *card,
104                                      struct snd_soc_dapm_context *dapm,
105                                      enum snd_soc_bias_level level)
106 {
107         struct snd_soc_pcm_runtime *rtd;
108         struct snd_soc_dai *codec_dai;
109         struct snd_soc_codec *codec;
110         struct bells_drvdata *bells = card->drvdata;
111         int ret;
112 
113         rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
114         codec_dai = rtd->codec_dai;
115         codec = codec_dai->codec;
116 
117         if (dapm->dev != codec_dai->dev)
118                 return 0;
119 
120         switch (level) {
121         case SND_SOC_BIAS_STANDBY:
122                 ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
123                 if (ret < 0) {
124                         pr_err("Failed to stop FLL: %d\n", ret);
125                         return ret;
126                 }
127 
128                 if (bells->asyncclk_rate) {
129                         ret = snd_soc_codec_set_pll(codec, WM5102_FLL2,
130                                                     0, 0, 0);
131                         if (ret < 0) {
132                                 pr_err("Failed to stop FLL: %d\n", ret);
133                                 return ret;
134                         }
135                 }
136                 break;
137 
138         default:
139                 break;
140         }
141 
142         dapm->bias_level = level;
143 
144         return 0;
145 }
146 
147 static int bells_late_probe(struct snd_soc_card *card)
148 {
149         struct bells_drvdata *bells = card->drvdata;
150         struct snd_soc_pcm_runtime *rtd;
151         struct snd_soc_codec *wm0010;
152         struct snd_soc_codec *codec;
153         struct snd_soc_dai *aif1_dai;
154         struct snd_soc_dai *aif2_dai;
155         struct snd_soc_dai *aif3_dai;
156         struct snd_soc_dai *wm9081_dai;
157         int ret;
158 
159         rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_AP_DSP].name);
160         wm0010 = rtd->codec;
161 
162         rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_DSP_CODEC].name);
163         codec = rtd->codec;
164         aif1_dai = rtd->codec_dai;
165 
166         ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_SYSCLK,
167                                        ARIZONA_CLK_SRC_FLL1,
168                                        bells->sysclk_rate,
169                                        SND_SOC_CLOCK_IN);
170         if (ret != 0) {
171                 dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
172                 return ret;
173         }
174 
175         ret = snd_soc_codec_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
176         if (ret != 0) {
177                 dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
178                 return ret;
179         }
180 
181         ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
182         if (ret != 0)
183                 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
184 
185         ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_OPCLK, 0,
186                                        SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
187         if (ret != 0)
188                 dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
189 
190         if (card->num_rtd == DAI_CODEC_CP)
191                 return 0;
192 
193         ret = snd_soc_codec_set_sysclk(codec, ARIZONA_CLK_ASYNCCLK,
194                                        ARIZONA_CLK_SRC_FLL2,
195                                        bells->asyncclk_rate,
196                                        SND_SOC_CLOCK_IN);
197         if (ret != 0) {
198                 dev_err(codec->dev, "Failed to set ASYNCCLK: %d\n", ret);
199                 return ret;
200         }
201 
202         rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_CP].name);
203         aif2_dai = rtd->cpu_dai;
204 
205         ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
206         if (ret != 0) {
207                 dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
208                 return ret;
209         }
210 
211         if (card->num_rtd == DAI_CODEC_SUB)
212                 return 0;
213 
214         rtd = snd_soc_get_pcm_runtime(card, card->dai_link[DAI_CODEC_SUB].name);
215         aif3_dai = rtd->cpu_dai;
216         wm9081_dai = rtd->codec_dai;
217 
218         ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
219         if (ret != 0) {
220                 dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
221                 return ret;
222         }
223 
224         ret = snd_soc_codec_set_sysclk(wm9081_dai->codec, WM9081_SYSCLK_MCLK,
225                                        0, SYS_MCLK_RATE, 0);
226         if (ret != 0) {
227                 dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
228                 return ret;
229         }
230 
231         return 0;
232 }
233 
234 static const struct snd_soc_pcm_stream baseband_params = {
235         .formats = SNDRV_PCM_FMTBIT_S32_LE,
236         .rate_min = 8000,
237         .rate_max = 8000,
238         .channels_min = 2,
239         .channels_max = 2,
240 };
241 
242 static const struct snd_soc_pcm_stream sub_params = {
243         .formats = SNDRV_PCM_FMTBIT_S32_LE,
244         .rate_min = SYS_AUDIO_RATE,
245         .rate_max = SYS_AUDIO_RATE,
246         .channels_min = 2,
247         .channels_max = 2,
248 };
249 
250 static struct snd_soc_dai_link bells_dai_wm2200[] = {
251         {
252                 .name = "CPU-DSP",
253                 .stream_name = "CPU-DSP",
254                 .cpu_dai_name = "samsung-i2s.0",
255                 .codec_dai_name = "wm0010-sdi1",
256                 .platform_name = "samsung-i2s.0",
257                 .codec_name = "spi0.0",
258                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
259                                 | SND_SOC_DAIFMT_CBM_CFM,
260         },
261         {
262                 .name = "DSP-CODEC",
263                 .stream_name = "DSP-CODEC",
264                 .cpu_dai_name = "wm0010-sdi2",
265                 .codec_dai_name = "wm2200",
266                 .codec_name = "wm2200.1-003a",
267                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
268                                 | SND_SOC_DAIFMT_CBM_CFM,
269                 .params = &sub_params,
270                 .ignore_suspend = 1,
271         },
272 };
273 
274 static struct snd_soc_dai_link bells_dai_wm5102[] = {
275         {
276                 .name = "CPU-DSP",
277                 .stream_name = "CPU-DSP",
278                 .cpu_dai_name = "samsung-i2s.0",
279                 .codec_dai_name = "wm0010-sdi1",
280                 .platform_name = "samsung-i2s.0",
281                 .codec_name = "spi0.0",
282                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
283                                 | SND_SOC_DAIFMT_CBM_CFM,
284         },
285         {
286                 .name = "DSP-CODEC",
287                 .stream_name = "DSP-CODEC",
288                 .cpu_dai_name = "wm0010-sdi2",
289                 .codec_dai_name = "wm5102-aif1",
290                 .codec_name = "wm5102-codec",
291                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
292                                 | SND_SOC_DAIFMT_CBM_CFM,
293                 .params = &sub_params,
294                 .ignore_suspend = 1,
295         },
296         {
297                 .name = "Baseband",
298                 .stream_name = "Baseband",
299                 .cpu_dai_name = "wm5102-aif2",
300                 .codec_dai_name = "wm1250-ev1",
301                 .codec_name = "wm1250-ev1.1-0027",
302                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
303                                 | SND_SOC_DAIFMT_CBM_CFM,
304                 .ignore_suspend = 1,
305                 .params = &baseband_params,
306         },
307         {
308                 .name = "Sub",
309                 .stream_name = "Sub",
310                 .cpu_dai_name = "wm5102-aif3",
311                 .codec_dai_name = "wm9081-hifi",
312                 .codec_name = "wm9081.1-006c",
313                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
314                                 | SND_SOC_DAIFMT_CBS_CFS,
315                 .ignore_suspend = 1,
316                 .params = &sub_params,
317         },
318 };
319 
320 static struct snd_soc_dai_link bells_dai_wm5110[] = {
321         {
322                 .name = "CPU-DSP",
323                 .stream_name = "CPU-DSP",
324                 .cpu_dai_name = "samsung-i2s.0",
325                 .codec_dai_name = "wm0010-sdi1",
326                 .platform_name = "samsung-i2s.0",
327                 .codec_name = "spi0.0",
328                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
329                                 | SND_SOC_DAIFMT_CBM_CFM,
330         },
331         {
332                 .name = "DSP-CODEC",
333                 .stream_name = "DSP-CODEC",
334                 .cpu_dai_name = "wm0010-sdi2",
335                 .codec_dai_name = "wm5110-aif1",
336                 .codec_name = "wm5110-codec",
337                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
338                                 | SND_SOC_DAIFMT_CBM_CFM,
339                 .params = &sub_params,
340                 .ignore_suspend = 1,
341         },
342         {
343                 .name = "Baseband",
344                 .stream_name = "Baseband",
345                 .cpu_dai_name = "wm5110-aif2",
346                 .codec_dai_name = "wm1250-ev1",
347                 .codec_name = "wm1250-ev1.1-0027",
348                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
349                                 | SND_SOC_DAIFMT_CBM_CFM,
350                 .ignore_suspend = 1,
351                 .params = &baseband_params,
352         },
353         {
354                 .name = "Sub",
355                 .stream_name = "Sub",
356                 .cpu_dai_name = "wm5110-aif3",
357                 .codec_dai_name = "wm9081-hifi",
358                 .codec_name = "wm9081.1-006c",
359                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
360                                 | SND_SOC_DAIFMT_CBS_CFS,
361                 .ignore_suspend = 1,
362                 .params = &sub_params,
363         },
364 };
365 
366 static struct snd_soc_codec_conf bells_codec_conf[] = {
367         {
368                 .dev_name = "wm9081.1-006c",
369                 .name_prefix = "Sub",
370         },
371 };
372 
373 static struct snd_soc_dapm_widget bells_widgets[] = {
374         SND_SOC_DAPM_MIC("DMIC", NULL),
375 };
376 
377 static struct snd_soc_dapm_route bells_routes[] = {
378         { "Sub CLK_SYS", NULL, "OPCLK" },
379         { "CLKIN", NULL, "OPCLK" },
380 
381         { "DMIC", NULL, "MICBIAS2" },
382         { "IN2L", NULL, "DMIC" },
383         { "IN2R", NULL, "DMIC" },
384 };
385 
386 static struct snd_soc_card bells_cards[] = {
387         {
388                 .name = "Bells WM2200",
389                 .owner = THIS_MODULE,
390                 .dai_link = bells_dai_wm2200,
391                 .num_links = ARRAY_SIZE(bells_dai_wm2200),
392                 .codec_conf = bells_codec_conf,
393                 .num_configs = ARRAY_SIZE(bells_codec_conf),
394 
395                 .late_probe = bells_late_probe,
396 
397                 .dapm_widgets = bells_widgets,
398                 .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
399                 .dapm_routes = bells_routes,
400                 .num_dapm_routes = ARRAY_SIZE(bells_routes),
401 
402                 .set_bias_level = bells_set_bias_level,
403                 .set_bias_level_post = bells_set_bias_level_post,
404 
405                 .drvdata = &wm2200_drvdata,
406         },
407         {
408                 .name = "Bells WM5102",
409                 .owner = THIS_MODULE,
410                 .dai_link = bells_dai_wm5102,
411                 .num_links = ARRAY_SIZE(bells_dai_wm5102),
412                 .codec_conf = bells_codec_conf,
413                 .num_configs = ARRAY_SIZE(bells_codec_conf),
414 
415                 .late_probe = bells_late_probe,
416 
417                 .dapm_widgets = bells_widgets,
418                 .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
419                 .dapm_routes = bells_routes,
420                 .num_dapm_routes = ARRAY_SIZE(bells_routes),
421 
422                 .set_bias_level = bells_set_bias_level,
423                 .set_bias_level_post = bells_set_bias_level_post,
424 
425                 .drvdata = &wm5102_drvdata,
426         },
427         {
428                 .name = "Bells WM5110",
429                 .owner = THIS_MODULE,
430                 .dai_link = bells_dai_wm5110,
431                 .num_links = ARRAY_SIZE(bells_dai_wm5110),
432                 .codec_conf = bells_codec_conf,
433                 .num_configs = ARRAY_SIZE(bells_codec_conf),
434 
435                 .late_probe = bells_late_probe,
436 
437                 .dapm_widgets = bells_widgets,
438                 .num_dapm_widgets = ARRAY_SIZE(bells_widgets),
439                 .dapm_routes = bells_routes,
440                 .num_dapm_routes = ARRAY_SIZE(bells_routes),
441 
442                 .set_bias_level = bells_set_bias_level,
443                 .set_bias_level_post = bells_set_bias_level_post,
444 
445                 .drvdata = &wm5110_drvdata,
446         },
447 };
448 
449 
450 static int bells_probe(struct platform_device *pdev)
451 {
452         int ret;
453 
454         bells_cards[pdev->id].dev = &pdev->dev;
455 
456         ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]);
457         if (ret)
458                 dev_err(&pdev->dev,
459                         "snd_soc_register_card(%s) failed: %d\n",
460                         bells_cards[pdev->id].name, ret);
461 
462         return ret;
463 }
464 
465 static struct platform_driver bells_driver = {
466         .driver = {
467                 .name = "bells",
468                 .pm = &snd_soc_pm_ops,
469         },
470         .probe = bells_probe,
471 };
472 
473 module_platform_driver(bells_driver);
474 
475 MODULE_DESCRIPTION("Bells audio support");
476 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
477 MODULE_LICENSE("GPL");
478 MODULE_ALIAS("platform:bells");
479 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us