Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[pandora-kernel.git] / sound / soc / atmel / playpaq_wm8510.c
1 /* sound/soc/at32/playpaq_wm8510.c
2  * ASoC machine driver for PlayPaq using WM8510 codec
3  *
4  * Copyright (C) 2008 Long Range Systems
5  *    Geoffrey Wossum <gwossum@acm.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  * This code is largely inspired by sound/soc/at91/eti_b1_wm8731.c
12  *
13  * NOTE: If you don't have the AT32 enhanced portmux configured (which
14  * isn't currently in the mainline or Atmel patched kernel), you will
15  * need to set the MCLK pin (PA30) to peripheral A in your board initialization
16  * code.  Something like:
17  *      at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
18  *
19  */
20
21 /* #define DEBUG */
22
23 #include <linux/module.h>
24 #include <linux/moduleparam.h>
25 #include <linux/kernel.h>
26 #include <linux/errno.h>
27 #include <linux/clk.h>
28 #include <linux/timer.h>
29 #include <linux/interrupt.h>
30 #include <linux/platform_device.h>
31
32 #include <sound/core.h>
33 #include <sound/pcm.h>
34 #include <sound/pcm_params.h>
35 #include <sound/soc.h>
36
37 #include <mach/at32ap700x.h>
38 #include <mach/portmux.h>
39
40 #include "../codecs/wm8510.h"
41 #include "atmel-pcm.h"
42 #include "atmel_ssc_dai.h"
43
44
45 /*-------------------------------------------------------------------------*\
46  * constants
47 \*-------------------------------------------------------------------------*/
48 #define MCLK_PIN                GPIO_PIN_PA(30)
49 #define MCLK_PERIPH             GPIO_PERIPH_A
50
51
52 /*-------------------------------------------------------------------------*\
53  * data types
54 \*-------------------------------------------------------------------------*/
55 /* SSC clocking data */
56 struct ssc_clock_data {
57         /* CMR div */
58         unsigned int cmr_div;
59
60         /* Frame period (as needed by xCMR.PERIOD) */
61         unsigned int period;
62
63         /* The SSC clock rate these settings where calculated for */
64         unsigned long ssc_rate;
65 };
66
67
68 /*-------------------------------------------------------------------------*\
69  * module data
70 \*-------------------------------------------------------------------------*/
71 static struct clk *_gclk0;
72 static struct clk *_pll0;
73
74 #define CODEC_CLK (_gclk0)
75
76
77 /*-------------------------------------------------------------------------*\
78  * Sound SOC operations
79 \*-------------------------------------------------------------------------*/
80 #if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
81 static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
82         struct snd_pcm_hw_params *params,
83         struct snd_soc_dai *cpu_dai)
84 {
85         struct at32_ssc_info *ssc_p = snd_soc_dai_get_drvdata(cpu_dai);
86         struct ssc_device *ssc = ssc_p->ssc;
87         struct ssc_clock_data cd;
88         unsigned int rate, width_bits, channels;
89         unsigned int bitrate, ssc_div;
90         unsigned actual_rate;
91
92
93         /*
94          * Figure out required bitrate
95          */
96         rate = params_rate(params);
97         channels = params_channels(params);
98         width_bits = snd_pcm_format_physical_width(params_format(params));
99         bitrate = rate * width_bits * channels;
100
101
102         /*
103          * Figure out required SSC divider and period for required bitrate
104          */
105         cd.ssc_rate = clk_get_rate(ssc->clk);
106         ssc_div = cd.ssc_rate / bitrate;
107         cd.cmr_div = ssc_div / 2;
108         if (ssc_div & 1) {
109                 /* round cmr_div up */
110                 cd.cmr_div++;
111         }
112         cd.period = width_bits - 1;
113
114
115         /*
116          * Find actual rate, compare to requested rate
117          */
118         actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
119         pr_debug("playpaq_wm8510: Request rate = %u, actual rate = %u\n",
120                  rate, actual_rate);
121
122
123         return cd;
124 }
125 #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
126
127
128
129 static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
130                                     struct snd_pcm_hw_params *params)
131 {
132         struct snd_soc_pcm_runtime *rtd = substream->private_data;
133         struct snd_soc_dai *codec_dai = rtd->codec_dai;
134         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
135         struct at32_ssc_info *ssc_p = snd_soc_dai_get_drvdata(cpu_dai);
136         struct ssc_device *ssc = ssc_p->ssc;
137         unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
138         int ret;
139
140
141         /* Due to difficulties with getting the correct clocks from the AT32's
142          * PLL0, we're going to let the CODEC be in charge of all the clocks
143          */
144 #if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
145         const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
146                                   SND_SOC_DAIFMT_NB_NF |
147                                   SND_SOC_DAIFMT_CBM_CFM);
148 #else
149         struct ssc_clock_data cd;
150         const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
151                                   SND_SOC_DAIFMT_NB_NF |
152                                   SND_SOC_DAIFMT_CBS_CFS);
153 #endif
154
155         if (ssc == NULL) {
156                 pr_warning("playpaq_wm8510_hw_params: ssc is NULL!\n");
157                 return -EINVAL;
158         }
159
160
161         /*
162          * Figure out PLL and BCLK dividers for WM8510
163          */
164         switch (params_rate(params)) {
165         case 48000:
166                 pll_out = 24576000;
167                 mclk_div = WM8510_MCLKDIV_2;
168                 bclk = WM8510_BCLKDIV_8;
169                 break;
170
171         case 44100:
172                 pll_out = 22579200;
173                 mclk_div = WM8510_MCLKDIV_2;
174                 bclk = WM8510_BCLKDIV_8;
175                 break;
176
177         case 22050:
178                 pll_out = 22579200;
179                 mclk_div = WM8510_MCLKDIV_4;
180                 bclk = WM8510_BCLKDIV_8;
181                 break;
182
183         case 16000:
184                 pll_out = 24576000;
185                 mclk_div = WM8510_MCLKDIV_6;
186                 bclk = WM8510_BCLKDIV_8;
187                 break;
188
189         case 11025:
190                 pll_out = 22579200;
191                 mclk_div = WM8510_MCLKDIV_8;
192                 bclk = WM8510_BCLKDIV_8;
193                 break;
194
195         case 8000:
196                 pll_out = 24576000;
197                 mclk_div = WM8510_MCLKDIV_12;
198                 bclk = WM8510_BCLKDIV_8;
199                 break;
200
201         default:
202                 pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
203                            params_rate(params));
204                 return -EINVAL;
205         }
206
207
208         /*
209          * set CPU and CODEC DAI configuration
210          */
211         ret = snd_soc_dai_set_fmt(codec_dai, fmt);
212         if (ret < 0) {
213                 pr_warning("playpaq_wm8510: "
214                            "Failed to set CODEC DAI format (%d)\n",
215                            ret);
216                 return ret;
217         }
218         ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
219         if (ret < 0) {
220                 pr_warning("playpaq_wm8510: "
221                            "Failed to set CPU DAI format (%d)\n",
222                            ret);
223                 return ret;
224         }
225
226
227         /*
228          * Set CPU clock configuration
229          */
230 #if defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
231         cd = playpaq_wm8510_calc_ssc_clock(params, cpu_dai);
232         pr_debug("playpaq_wm8510: cmr_div = %d, period = %d\n",
233                  cd.cmr_div, cd.period);
234         ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_CMR_DIV, cd.cmr_div);
235         if (ret < 0) {
236                 pr_warning("playpaq_wm8510: Failed to set CPU CMR_DIV (%d)\n",
237                            ret);
238                 return ret;
239         }
240         ret = snd_soc_dai_set_clkdiv(cpu_dai, AT32_SSC_TCMR_PERIOD,
241                                           cd.period);
242         if (ret < 0) {
243                 pr_warning("playpaq_wm8510: "
244                            "Failed to set CPU transmit period (%d)\n",
245                            ret);
246                 return ret;
247         }
248 #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
249
250
251         /*
252          * Set CODEC clock configuration
253          */
254         pr_debug("playpaq_wm8510: "
255                  "pll_in = %ld, pll_out = %u, bclk = %x, mclk = %x\n",
256                  clk_get_rate(CODEC_CLK), pll_out, bclk, mclk_div);
257
258
259 #if !defined CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE
260         ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_BCLKDIV, bclk);
261         if (ret < 0) {
262                 pr_warning
263                     ("playpaq_wm8510: Failed to set CODEC DAI BCLKDIV (%d)\n",
264                      ret);
265                 return ret;
266         }
267 #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
268
269
270         ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
271                                          clk_get_rate(CODEC_CLK), pll_out);
272         if (ret < 0) {
273                 pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
274                            ret);
275                 return ret;
276         }
277
278
279         ret = snd_soc_dai_set_clkdiv(codec_dai, WM8510_MCLKDIV, mclk_div);
280         if (ret < 0) {
281                 pr_warning("playpaq_wm8510: Failed to set CODEC MCLKDIV (%d)\n",
282                            ret);
283                 return ret;
284         }
285
286
287         return 0;
288 }
289
290
291
292 static struct snd_soc_ops playpaq_wm8510_ops = {
293         .hw_params = playpaq_wm8510_hw_params,
294 };
295
296
297
298 static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = {
299         SND_SOC_DAPM_MIC("Int Mic", NULL),
300         SND_SOC_DAPM_SPK("Ext Spk", NULL),
301 };
302
303
304
305 static const struct snd_soc_dapm_route intercon[] = {
306         /* speaker connected to SPKOUT */
307         {"Ext Spk", NULL, "SPKOUTP"},
308         {"Ext Spk", NULL, "SPKOUTN"},
309
310         {"Mic Bias", NULL, "Int Mic"},
311         {"MICN", NULL, "Mic Bias"},
312         {"MICP", NULL, "Mic Bias"},
313 };
314
315
316
317 static int playpaq_wm8510_init(struct snd_soc_pcm_runtime *rtd)
318 {
319         struct snd_soc_codec *codec = rtd->codec;
320         struct snd_soc_dapm_context *dapm = &codec->dapm;
321         int i;
322
323         /*
324          * Add DAPM widgets
325          */
326         for (i = 0; i < ARRAY_SIZE(playpaq_dapm_widgets); i++)
327                 snd_soc_dapm_new_control(dapm, &playpaq_dapm_widgets[i]);
328
329
330
331         /*
332          * Setup audio path interconnects
333          */
334         snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
335
336
337
338         /* always connected pins */
339         snd_soc_dapm_enable_pin(dapm, "Int Mic");
340         snd_soc_dapm_enable_pin(dapm, "Ext Spk");
341         snd_soc_dapm_sync(dapm);
342
343
344
345         /* Make CSB show PLL rate */
346         snd_soc_dai_set_clkdiv(rtd->codec_dai, WM8510_OPCLKDIV,
347                                        WM8510_OPCLKDIV_1 | 4);
348
349         return 0;
350 }
351
352
353
354 static struct snd_soc_dai_link playpaq_wm8510_dai = {
355         .name = "WM8510",
356         .stream_name = "WM8510 PCM",
357         .cpu_dai_name= "atmel-ssc-dai.0",
358         .platform_name = "atmel-pcm-audio",
359         .codec_name = "wm8510-codec.0-0x1a",
360         .codec_dai_name = "wm8510-hifi",
361         .init = playpaq_wm8510_init,
362         .ops = &playpaq_wm8510_ops,
363 };
364
365
366
367 static struct snd_soc_card snd_soc_playpaq = {
368         .name = "LRS_PlayPaq_WM8510",
369         .dai_link = &playpaq_wm8510_dai,
370         .num_links = 1,
371 };
372
373 static struct platform_device *playpaq_snd_device;
374
375
376 static int __init playpaq_asoc_init(void)
377 {
378         int ret = 0;
379
380         /*
381          * Configure MCLK for WM8510
382          */
383         _gclk0 = clk_get(NULL, "gclk0");
384         if (IS_ERR(_gclk0)) {
385                 _gclk0 = NULL;
386                 goto err_gclk0;
387         }
388         _pll0 = clk_get(NULL, "pll0");
389         if (IS_ERR(_pll0)) {
390                 _pll0 = NULL;
391                 goto err_pll0;
392         }
393         if (clk_set_parent(_gclk0, _pll0)) {
394                 pr_warning("snd-soc-playpaq: "
395                            "Failed to set PLL0 as parent for DAC clock\n");
396                 goto err_set_clk;
397         }
398         clk_set_rate(CODEC_CLK, 12000000);
399         clk_enable(CODEC_CLK);
400
401 #if defined CONFIG_AT32_ENHANCED_PORTMUX
402         at32_select_periph(MCLK_PIN, MCLK_PERIPH, 0);
403 #endif
404
405
406         /*
407          * Create and register platform device
408          */
409         playpaq_snd_device = platform_device_alloc("soc-audio", 0);
410         if (playpaq_snd_device == NULL) {
411                 ret = -ENOMEM;
412                 goto err_device_alloc;
413         }
414
415         platform_set_drvdata(playpaq_snd_device, &snd_soc_playpaq);
416
417         ret = platform_device_add(playpaq_snd_device);
418         if (ret) {
419                 pr_warning("playpaq_wm8510: platform_device_add failed (%d)\n",
420                            ret);
421                 goto err_device_add;
422         }
423
424         return 0;
425
426
427 err_device_add:
428         if (playpaq_snd_device != NULL) {
429                 platform_device_put(playpaq_snd_device);
430                 playpaq_snd_device = NULL;
431         }
432 err_device_alloc:
433 err_set_clk:
434         if (_pll0 != NULL) {
435                 clk_put(_pll0);
436                 _pll0 = NULL;
437         }
438 err_pll0:
439         if (_gclk0 != NULL) {
440                 clk_put(_gclk0);
441                 _gclk0 = NULL;
442         }
443         return ret;
444 }
445
446
447 static void __exit playpaq_asoc_exit(void)
448 {
449         if (_gclk0 != NULL) {
450                 clk_put(_gclk0);
451                 _gclk0 = NULL;
452         }
453         if (_pll0 != NULL) {
454                 clk_put(_pll0);
455                 _pll0 = NULL;
456         }
457
458 #if defined CONFIG_AT32_ENHANCED_PORTMUX
459         at32_free_pin(MCLK_PIN);
460 #endif
461
462         platform_device_unregister(playpaq_snd_device);
463         playpaq_snd_device = NULL;
464 }
465
466 module_init(playpaq_asoc_init);
467 module_exit(playpaq_asoc_exit);
468
469 MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
470 MODULE_DESCRIPTION("ASoC machine driver for LRS PlayPaq");
471 MODULE_LICENSE("GPL");