~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/atmel/playpaq_wm8510.c

Version: ~ [ linux-5.4-rc7 ] ~ [ linux-5.3.11 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.84 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.154 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.201 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.201 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.77 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp