Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
bells.c
Go to the documentation of this file.
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 /*
22  * 44.1kHz based clocks for the SYSCLK domain, use a very high clock
23  * to allow all the DSP functionality to be enabled if desired.
24  */
25 #define SYSCLK_RATE (44100 * 1024)
26 
27 /* 48kHz based clocks for the ASYNC domain */
28 #define ASYNCCLK_RATE (48000 * 512)
29 
30 /* BCLK2 is fixed at this currently */
31 #define BCLK2_RATE (64 * 8000)
32 
33 /*
34  * Expect a 24.576MHz crystal if one is fitted (the driver will function
35  * if this is not fitted).
36  */
37 #define MCLK_RATE 24576000
38 
39 #define WM9081_AUDIO_RATE 44100
40 #define WM9081_MCLK_RATE (WM9081_AUDIO_RATE * 256)
41 
42 static int bells_set_bias_level(struct snd_soc_card *card,
43  struct snd_soc_dapm_context *dapm,
45 {
46  struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
47  struct snd_soc_codec *codec = codec_dai->codec;
48  int ret;
49 
50  if (dapm->dev != codec_dai->dev)
51  return 0;
52 
53  switch (level) {
55  if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
58  MCLK_RATE,
59  SYSCLK_RATE);
60  if (ret < 0)
61  pr_err("Failed to start FLL: %d\n", ret);
62 
65  BCLK2_RATE,
67  if (ret < 0)
68  pr_err("Failed to start FLL: %d\n", ret);
69  }
70  break;
71 
72  default:
73  break;
74  }
75 
76  return 0;
77 }
78 
79 static int bells_set_bias_level_post(struct snd_soc_card *card,
80  struct snd_soc_dapm_context *dapm,
82 {
83  struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
84  struct snd_soc_codec *codec = codec_dai->codec;
85  int ret;
86 
87  if (dapm->dev != codec_dai->dev)
88  return 0;
89 
90  switch (level) {
92  ret = snd_soc_codec_set_pll(codec, WM5102_FLL1, 0, 0, 0);
93  if (ret < 0) {
94  pr_err("Failed to stop FLL: %d\n", ret);
95  return ret;
96  }
97 
98  ret = snd_soc_codec_set_pll(codec, WM5102_FLL2, 0, 0, 0);
99  if (ret < 0) {
100  pr_err("Failed to stop FLL: %d\n", ret);
101  return ret;
102  }
103  break;
104 
105  default:
106  break;
107  }
108 
109  dapm->bias_level = level;
110 
111  return 0;
112 }
113 
114 static int bells_late_probe(struct snd_soc_card *card)
115 {
116  struct snd_soc_codec *codec = card->rtd[0].codec;
117  struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai;
118  struct snd_soc_dai *aif2_dai = card->rtd[1].cpu_dai;
119  struct snd_soc_dai *aif3_dai = card->rtd[2].cpu_dai;
120  struct snd_soc_dai *wm9081_dai = card->rtd[2].codec_dai;
121  int ret;
122 
123  ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
124  if (ret != 0) {
125  dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
126  return ret;
127  }
128 
129  ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
130  if (ret != 0) {
131  dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
132  return ret;
133  }
134 
135  ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
136  if (ret != 0) {
137  dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
138  return ret;
139  }
140 
144  if (ret != 0) {
145  dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
146  return ret;
147  }
148 
151  if (ret != 0) {
152  dev_err(codec->dev, "Failed to set OPCLK: %d\n", ret);
153  return ret;
154  }
155 
159  if (ret != 0) {
160  dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
161  return ret;
162  }
163 
165  0, WM9081_MCLK_RATE, 0);
166  if (ret != 0) {
167  dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
168  return ret;
169  }
170 
171  return 0;
172 }
173 
174 static const struct snd_soc_pcm_stream baseband_params = {
175  .formats = SNDRV_PCM_FMTBIT_S32_LE,
176  .rate_min = 8000,
177  .rate_max = 8000,
178  .channels_min = 2,
179  .channels_max = 2,
180 };
181 
182 static const struct snd_soc_pcm_stream sub_params = {
183  .formats = SNDRV_PCM_FMTBIT_S32_LE,
184  .rate_min = WM9081_AUDIO_RATE,
185  .rate_max = WM9081_AUDIO_RATE,
186  .channels_min = 2,
187  .channels_max = 2,
188 };
189 
190 static struct snd_soc_dai_link bells_dai_wm5102[] = {
191  {
192  .name = "CPU",
193  .stream_name = "CPU",
194  .cpu_dai_name = "samsung-i2s.0",
195  .codec_dai_name = "wm5102-aif1",
196  .platform_name = "samsung-audio",
197  .codec_name = "wm5102-codec",
200  },
201  {
202  .name = "Baseband",
203  .stream_name = "Baseband",
204  .cpu_dai_name = "wm5102-aif2",
205  .codec_dai_name = "wm1250-ev1",
206  .codec_name = "wm1250-ev1.1-0027",
209  .ignore_suspend = 1,
210  .params = &baseband_params,
211  },
212  {
213  .name = "Sub",
214  .stream_name = "Sub",
215  .cpu_dai_name = "wm5102-aif3",
216  .codec_dai_name = "wm9081-hifi",
217  .codec_name = "wm9081.1-006c",
220  .ignore_suspend = 1,
221  .params = &sub_params,
222  },
223 };
224 
225 static struct snd_soc_dai_link bells_dai_wm5110[] = {
226  {
227  .name = "CPU",
228  .stream_name = "CPU",
229  .cpu_dai_name = "samsung-i2s.0",
230  .codec_dai_name = "wm5110-aif1",
231  .platform_name = "samsung-audio",
232  .codec_name = "wm5110-codec",
235  },
236  {
237  .name = "Baseband",
238  .stream_name = "Baseband",
239  .cpu_dai_name = "wm5110-aif2",
240  .codec_dai_name = "wm1250-ev1",
241  .codec_name = "wm1250-ev1.1-0027",
244  .ignore_suspend = 1,
245  .params = &baseband_params,
246  },
247  {
248  .name = "Sub",
249  .stream_name = "Sub",
250  .cpu_dai_name = "wm5110-aif3",
251  .codec_dai_name = "wm9081-hifi",
252  .codec_name = "wm9081.1-006c",
255  .ignore_suspend = 1,
256  .params = &sub_params,
257  },
258 };
259 
260 static struct snd_soc_codec_conf bells_codec_conf[] = {
261  {
262  .dev_name = "wm9081.1-006c",
263  .name_prefix = "Sub",
264  },
265 };
266 
267 static struct snd_soc_dapm_route bells_routes[] = {
268  { "Sub CLK_SYS", NULL, "OPCLK" },
269 };
270 
271 static struct snd_soc_card bells_cards[] = {
272  {
273  .name = "Bells WM5102",
274  .owner = THIS_MODULE,
275  .dai_link = bells_dai_wm5102,
276  .num_links = ARRAY_SIZE(bells_dai_wm5102),
277  .codec_conf = bells_codec_conf,
278  .num_configs = ARRAY_SIZE(bells_codec_conf),
279 
280  .late_probe = bells_late_probe,
281 
282  .dapm_routes = bells_routes,
283  .num_dapm_routes = ARRAY_SIZE(bells_routes),
284 
285  .set_bias_level = bells_set_bias_level,
286  .set_bias_level_post = bells_set_bias_level_post,
287  },
288  {
289  .name = "Bells WM5110",
290  .owner = THIS_MODULE,
291  .dai_link = bells_dai_wm5110,
292  .num_links = ARRAY_SIZE(bells_dai_wm5110),
293  .codec_conf = bells_codec_conf,
294  .num_configs = ARRAY_SIZE(bells_codec_conf),
295 
296  .late_probe = bells_late_probe,
297 
298  .dapm_routes = bells_routes,
299  .num_dapm_routes = ARRAY_SIZE(bells_routes),
300 
301  .set_bias_level = bells_set_bias_level,
302  .set_bias_level_post = bells_set_bias_level_post,
303  },
304 };
305 
306 
307 static __devinit int bells_probe(struct platform_device *pdev)
308 {
309  int ret;
310 
311  bells_cards[pdev->id].dev = &pdev->dev;
312 
313  ret = snd_soc_register_card(&bells_cards[pdev->id]);
314  if (ret) {
315  dev_err(&pdev->dev,
316  "snd_soc_register_card(%s) failed: %d\n",
317  bells_cards[pdev->id].name, ret);
318  return ret;
319  }
320 
321  return 0;
322 }
323 
324 static int __devexit bells_remove(struct platform_device *pdev)
325 {
326  snd_soc_unregister_card(&bells_cards[pdev->id]);
327 
328  return 0;
329 }
330 
331 static struct platform_driver bells_driver = {
332  .driver = {
333  .name = "bells",
334  .owner = THIS_MODULE,
335  .pm = &snd_soc_pm_ops,
336  },
337  .probe = bells_probe,
338  .remove = __devexit_p(bells_remove),
339 };
340 
341 module_platform_driver(bells_driver);
342 
343 MODULE_DESCRIPTION("Bells audio support");
344 MODULE_AUTHOR("Mark Brown <[email protected]>");
345 MODULE_LICENSE("GPL");
346 MODULE_ALIAS("platform:bells");