aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig18
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/at91/Kconfig10
-rw-r--r--sound/soc/at91/Makefile4
-rw-r--r--sound/soc/at91/at91-ssc.c (renamed from sound/soc/at91/at91-i2s.c)259
-rw-r--r--sound/soc/at91/at91-ssc.h (renamed from sound/soc/at91/at91-i2s.h)14
-rw-r--r--sound/soc/at91/eti_b1_wm8731.c8
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ac97.c1
-rw-r--r--sound/soc/codecs/ac97.h1
-rw-r--r--sound/soc/codecs/wm8750.c2
-rw-r--r--sound/soc/codecs/wm8753.c1811
-rw-r--r--sound/soc/codecs/wm8753.h126
-rw-r--r--sound/soc/codecs/wm9712.c7
-rw-r--r--sound/soc/pxa/Kconfig4
-rw-r--r--sound/soc/s3c24xx/Kconfig10
-rw-r--r--sound/soc/s3c24xx/Makefile6
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c441
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.h37
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c468
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.h31
-rw-r--r--sound/soc/soc-dapm.c6
23 files changed, 3137 insertions, 135 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index dccaa4be679e..10cffc087181 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -2,31 +2,31 @@
2# SoC audio configuration 2# SoC audio configuration
3# 3#
4 4
5menu "SoC audio support" 5menu "System on Chip audio support"
6 depends on SND!=n 6 depends on SND!=n
7 7
8config SND_SOC_AC97_BUS 8config SND_SOC_AC97_BUS
9 bool 9 bool
10 10
11config SND_SOC 11config SND_SOC
12 tristate "SoC audio support" 12 tristate "ALSA for SoC audio support"
13 depends on SND 13 depends on SND
14 select SND_PCM 14 select SND_PCM
15 ---help--- 15 ---help---
16 16
17 If you want SoC support, you should say Y here and also to the 17 If you want ASoC support, you should say Y here and also to the
18 specific driver for your SoC below. You will also need to select the 18 specific driver for your SoC platform below.
19 specific codec(s) attached to the SoC 19
20 ASoC provides power efficient ALSA support for embedded battery powered
21 SoC based systems like PDA's, Phones and Personal Media Players.
20 22
21 This SoC audio support can also be built as a module. If so, the module 23 This ASoC audio support can also be built as a module. If so, the module
22 will be called snd-soc-core. 24 will be called snd-soc-core.
23 25
24# All the supported Soc's 26# All the supported Soc's
25menu "SoC Platforms"
26depends on SND_SOC
27source "sound/soc/at91/Kconfig" 27source "sound/soc/at91/Kconfig"
28source "sound/soc/pxa/Kconfig" 28source "sound/soc/pxa/Kconfig"
29endmenu 29source "sound/soc/s3c24xx/Kconfig"
30 30
31# Supported codecs 31# Supported codecs
32source "sound/soc/codecs/Kconfig" 32source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 98e6f49dafc2..0ae2e49036f9 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
1snd-soc-core-objs := soc-core.o soc-dapm.o 1snd-soc-core-objs := soc-core.o soc-dapm.o
2 2
3obj-$(CONFIG_SND_SOC) += snd-soc-core.o 3obj-$(CONFIG_SND_SOC) += snd-soc-core.o
4obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ 4obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
index a5b2558916c1..5cb93fd3a407 100644
--- a/sound/soc/at91/Kconfig
+++ b/sound/soc/at91/Kconfig
@@ -1,5 +1,3 @@
1menu "SoC Audio for the Atmel AT91"
2
3config SND_AT91_SOC 1config SND_AT91_SOC
4 tristate "SoC Audio for the Atmel AT91 System-on-Chip" 2 tristate "SoC Audio for the Atmel AT91 System-on-Chip"
5 depends on ARCH_AT91 && SND_SOC 3 depends on ARCH_AT91 && SND_SOC
@@ -8,13 +6,13 @@ config SND_AT91_SOC
8 the AT91 SSC interface. You will also need 6 the AT91 SSC interface. You will also need
9 to select the audio interfaces to support below. 7 to select the audio interfaces to support below.
10 8
11config SND_AT91_SOC_I2S 9config SND_AT91_SOC_SSC
12 tristate 10 tristate
13 11
14config SND_AT91_SOC_ETI_B1_WM8731 12config SND_AT91_SOC_ETI_B1_WM8731
15 tristate "SoC I2S Audio support for WM8731-based Endrelia ETI-B1 boards" 13 tristate "SoC Audio support for WM8731-based Endrelia ETI-B1 boards"
16 depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1) 14 depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1)
17 select SND_AT91_SOC_I2S 15 select SND_AT91_SOC_SSC
18 select SND_SOC_WM8731 16 select SND_SOC_WM8731
19 help 17 help
20 Say Y if you want to add support for SoC audio on WM8731-based 18 Say Y if you want to add support for SoC audio on WM8731-based
@@ -27,5 +25,3 @@ config SND_AT91_SOC_ETI_SLAVE
27 help 25 help
28 Say Y if you want to run with the AT91 SSC generating the BCLK 26 Say Y if you want to run with the AT91 SSC generating the BCLK
29 and LRC signals on Endrelia boards. 27 and LRC signals on Endrelia boards.
30
31endmenu
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile
index b77b01ab2028..f23da17cc328 100644
--- a/sound/soc/at91/Makefile
+++ b/sound/soc/at91/Makefile
@@ -1,9 +1,9 @@
1# AT91 Platform Support 1# AT91 Platform Support
2snd-soc-at91-objs := at91-pcm.o 2snd-soc-at91-objs := at91-pcm.o
3snd-soc-at91-i2s-objs := at91-i2s.o 3snd-soc-at91-ssc-objs := at91-ssc.o
4 4
5obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o 5obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
6obj-$(CONFIG_SND_AT91_SOC_I2S) += snd-soc-at91-i2s.o 6obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o
7 7
8# AT91 Machine Support 8# AT91 Machine Support
9snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o 9snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o
diff --git a/sound/soc/at91/at91-i2s.c b/sound/soc/at91/at91-ssc.c
index 9fc0c0388881..3d4e32cff75e 100644
--- a/sound/soc/at91/at91-i2s.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * at91-i2s.c -- ALSA SoC I2S Audio Layer Platform driver 2 * at91-ssc.c -- ALSA SoC AT91 SSC Audio Layer Platform driver
3 * 3 *
4 * Author: Frank Mandarino <fmandarino@endrelia.com> 4 * Author: Frank Mandarino <fmandarino@endrelia.com>
5 * Endrelia Technologies Inc. 5 * Endrelia Technologies Inc.
@@ -25,6 +25,7 @@
25#include <sound/driver.h> 25#include <sound/driver.h>
26#include <sound/core.h> 26#include <sound/core.h>
27#include <sound/pcm.h> 27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
28#include <sound/initval.h> 29#include <sound/initval.h>
29#include <sound/soc.h> 30#include <sound/soc.h>
30 31
@@ -33,10 +34,10 @@
33#include <asm/arch/at91_ssc.h> 34#include <asm/arch/at91_ssc.h>
34 35
35#include "at91-pcm.h" 36#include "at91-pcm.h"
36#include "at91-i2s.h" 37#include "at91-ssc.h"
37 38
38#if 0 39#if 0
39#define DBG(x...) printk(KERN_DEBUG "at91-i2s:" x) 40#define DBG(x...) printk(KERN_DEBUG "at91-ssc:" x)
40#else 41#else
41#define DBG(x...) 42#define DBG(x...)
42#endif 43#endif
@@ -92,33 +93,33 @@ static struct at91_ssc_mask ssc_rx_mask = {
92 */ 93 */
93static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { 94static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
94 {{ 95 {{
95 .name = "SSC0/I2S PCM Stereo out", 96 .name = "SSC0 PCM out",
96 .pdc = &pdc_tx_reg, 97 .pdc = &pdc_tx_reg,
97 .mask = &ssc_tx_mask, 98 .mask = &ssc_tx_mask,
98 }, 99 },
99 { 100 {
100 .name = "SSC0/I2S PCM Stereo in", 101 .name = "SSC0 PCM in",
101 .pdc = &pdc_rx_reg, 102 .pdc = &pdc_rx_reg,
102 .mask = &ssc_rx_mask, 103 .mask = &ssc_rx_mask,
103 }}, 104 }},
104#if NUM_SSC_DEVICES == 3 105#if NUM_SSC_DEVICES == 3
105 {{ 106 {{
106 .name = "SSC1/I2S PCM Stereo out", 107 .name = "SSC1 PCM out",
107 .pdc = &pdc_tx_reg, 108 .pdc = &pdc_tx_reg,
108 .mask = &ssc_tx_mask, 109 .mask = &ssc_tx_mask,
109 }, 110 },
110 { 111 {
111 .name = "SSC1/I2S PCM Stereo in", 112 .name = "SSC1 PCM in",
112 .pdc = &pdc_rx_reg, 113 .pdc = &pdc_rx_reg,
113 .mask = &ssc_rx_mask, 114 .mask = &ssc_rx_mask,
114 }}, 115 }},
115 {{ 116 {{
116 .name = "SSC2/I2S PCM Stereo out", 117 .name = "SSC2 PCM out",
117 .pdc = &pdc_tx_reg, 118 .pdc = &pdc_tx_reg,
118 .mask = &ssc_tx_mask, 119 .mask = &ssc_tx_mask,
119 }, 120 },
120 { 121 {
121 .name = "SSC1/I2S PCM Stereo in", 122 .name = "SSC2 PCM in",
122 .pdc = &pdc_rx_reg, 123 .pdc = &pdc_rx_reg,
123 .mask = &ssc_rx_mask, 124 .mask = &ssc_rx_mask,
124 }}, 125 }},
@@ -151,33 +152,33 @@ static struct at91_ssc_info {
151} ssc_info[NUM_SSC_DEVICES] = { 152} ssc_info[NUM_SSC_DEVICES] = {
152 { 153 {
153 .name = "ssc0", 154 .name = "ssc0",
154 .lock = SPIN_LOCK_UNLOCKED, 155 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
155 .dir_mask = 0, 156 .dir_mask = 0,
156 .initialized = 0, 157 .initialized = 0,
157 }, 158 },
158#if NUM_SSC_DEVICES == 3 159#if NUM_SSC_DEVICES == 3
159 { 160 {
160 .name = "ssc1", 161 .name = "ssc1",
161 .lock = SPIN_LOCK_UNLOCKED, 162 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
162 .dir_mask = 0, 163 .dir_mask = 0,
163 .initialized = 0, 164 .initialized = 0,
164 }, 165 },
165 { 166 {
166 .name = "ssc2", 167 .name = "ssc2",
167 .lock = SPIN_LOCK_UNLOCKED, 168 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
168 .dir_mask = 0, 169 .dir_mask = 0,
169 .initialized = 0, 170 .initialized = 0,
170 }, 171 },
171#endif 172#endif
172}; 173};
173 174
174static unsigned int at91_i2s_sysclk; 175static unsigned int at91_ssc_sysclk;
175 176
176/* 177/*
177 * SSC interrupt handler. Passes PDC interrupts to the DMA 178 * SSC interrupt handler. Passes PDC interrupts to the DMA
178 * interrupt handler in the PCM driver. 179 * interrupt handler in the PCM driver.
179 */ 180 */
180static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id) 181static irqreturn_t at91_ssc_interrupt(int irq, void *dev_id)
181{ 182{
182 struct at91_ssc_info *ssc_p = dev_id; 183 struct at91_ssc_info *ssc_p = dev_id;
183 struct at91_pcm_dma_params *dma_params; 184 struct at91_pcm_dma_params *dma_params;
@@ -209,13 +210,13 @@ static irqreturn_t at91_i2s_interrupt(int irq, void *dev_id)
209/* 210/*
210 * Startup. Only that one substream allowed in each direction. 211 * Startup. Only that one substream allowed in each direction.
211 */ 212 */
212static int at91_i2s_startup(struct snd_pcm_substream *substream) 213static int at91_ssc_startup(struct snd_pcm_substream *substream)
213{ 214{
214 struct snd_soc_pcm_runtime *rtd = substream->private_data; 215 struct snd_soc_pcm_runtime *rtd = substream->private_data;
215 struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; 216 struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
216 int dir_mask; 217 int dir_mask;
217 218
218 DBG("i2s_startup: SSC_SR=0x%08lx\n", 219 DBG("ssc_startup: SSC_SR=0x%08lx\n",
219 at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)); 220 at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
220 dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2; 221 dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2;
221 222
@@ -234,7 +235,7 @@ static int at91_i2s_startup(struct snd_pcm_substream *substream)
234 * Shutdown. Clear DMA parameters and shutdown the SSC if there 235 * Shutdown. Clear DMA parameters and shutdown the SSC if there
235 * are no other substreams open. 236 * are no other substreams open.
236 */ 237 */
237static void at91_i2s_shutdown(struct snd_pcm_substream *substream) 238static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
238{ 239{
239 struct snd_soc_pcm_runtime *rtd = substream->private_data; 240 struct snd_soc_pcm_runtime *rtd = substream->private_data;
240 struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; 241 struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
@@ -281,7 +282,7 @@ static void at91_i2s_shutdown(struct snd_pcm_substream *substream)
281/* 282/*
282 * Record the SSC system clock rate. 283 * Record the SSC system clock rate.
283 */ 284 */
284static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai, 285static int at91_ssc_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
285 int clk_id, unsigned int freq, int dir) 286 int clk_id, unsigned int freq, int dir)
286{ 287{
287 /* 288 /*
@@ -291,7 +292,7 @@ static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
291 */ 292 */
292 switch (clk_id) { 293 switch (clk_id) {
293 case AT91_SYSCLK_MCK: 294 case AT91_SYSCLK_MCK:
294 at91_i2s_sysclk = freq; 295 at91_ssc_sysclk = freq;
295 break; 296 break;
296 default: 297 default:
297 return -EINVAL; 298 return -EINVAL;
@@ -303,14 +304,11 @@ static int at91_i2s_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
303/* 304/*
304 * Record the DAI format for use in hw_params(). 305 * Record the DAI format for use in hw_params().
305 */ 306 */
306static int at91_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai, 307static int at91_ssc_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
307 unsigned int fmt) 308 unsigned int fmt)
308{ 309{
309 struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; 310 struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
310 311
311 if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S)
312 return -EINVAL;
313
314 ssc_p->daifmt = fmt; 312 ssc_p->daifmt = fmt;
315 return 0; 313 return 0;
316} 314}
@@ -318,7 +316,7 @@ static int at91_i2s_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
318/* 316/*
319 * Record SSC clock dividers for use in hw_params(). 317 * Record SSC clock dividers for use in hw_params().
320 */ 318 */
321static int at91_i2s_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai, 319static int at91_ssc_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
322 int div_id, int div) 320 int div_id, int div)
323{ 321{
324 struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; 322 struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
@@ -355,7 +353,7 @@ static int at91_i2s_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
355/* 353/*
356 * Configure the SSC. 354 * Configure the SSC.
357 */ 355 */
358static int at91_i2s_hw_params(struct snd_pcm_substream *substream, 356static int at91_ssc_hw_params(struct snd_pcm_substream *substream,
359 struct snd_pcm_hw_params *params) 357 struct snd_pcm_hw_params *params)
360{ 358{
361 struct snd_soc_pcm_runtime *rtd = substream->private_data; 359 struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -391,20 +389,50 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream,
391 channels = params_channels(params); 389 channels = params_channels(params);
392 390
393 /* 391 /*
392 * Determine sample size in bits and the PDC increment.
393 */
394 switch(params_format(params)) {
395 case SNDRV_PCM_FORMAT_S8:
396 bits = 8;
397 dma_params->pdc_xfer_size = 1;
398 break;
399 case SNDRV_PCM_FORMAT_S16_LE:
400 bits = 16;
401 dma_params->pdc_xfer_size = 2;
402 break;
403 case SNDRV_PCM_FORMAT_S24_LE:
404 bits = 24;
405 dma_params->pdc_xfer_size = 4;
406 break;
407 case SNDRV_PCM_FORMAT_S32_LE:
408 bits = 32;
409 dma_params->pdc_xfer_size = 4;
410 break;
411 default:
412 printk(KERN_WARNING "at91-ssc: unsupported PCM format");
413 return -EINVAL;
414 }
415
416 /*
394 * The SSC only supports up to 16-bit samples in I2S format, due 417 * The SSC only supports up to 16-bit samples in I2S format, due
395 * to the size of the Frame Mode Register FSLEN field. Also, I2S 418 * to the size of the Frame Mode Register FSLEN field.
396 * implies signed data.
397 */ 419 */
398 bits = 16; 420 if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
399 dma_params->pdc_xfer_size = 2; 421 && bits > 16) {
422 printk(KERN_WARNING
423 "at91-ssc: sample size %d is too large for I2S\n", bits);
424 return -EINVAL;
425 }
400 426
401 /* 427 /*
402 * Compute SSC register settings. 428 * Compute SSC register settings.
403 */ 429 */
404 switch (ssc_p->daifmt) { 430 switch (ssc_p->daifmt
405 case SND_SOC_DAIFMT_CBS_CFS: 431 & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
432
433 case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
406 /* 434 /*
407 * SSC provides BCLK and LRC clocks. 435 * I2S format, SSC provides BCLK and LRC clocks.
408 * 436 *
409 * The SSC transmit and receive clocks are generated from the 437 * The SSC transmit and receive clocks are generated from the
410 * MCK divider, and the BCLK signal is output on the SSC TK line. 438 * MCK divider, and the BCLK signal is output on the SSC TK line.
@@ -441,10 +469,9 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream,
441 | (((bits - 1) << 0) & AT91_SSC_DATALEN); 469 | (((bits - 1) << 0) & AT91_SSC_DATALEN);
442 break; 470 break;
443 471
444 case SND_SOC_DAIFMT_CBM_CFM: 472 case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
445
446 /* 473 /*
447 * CODEC supplies BCLK and LRC clocks. 474 * I2S format, CODEC supplies BCLK and LRC clocks.
448 * 475 *
449 * The SSC transmit clock is obtained from the BCLK signal on 476 * The SSC transmit clock is obtained from the BCLK signal on
450 * on the TK line, and the SSC receive clock is generated from the 477 * on the TK line, and the SSC receive clock is generated from the
@@ -490,10 +517,51 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream,
490 | (((bits - 1) << 0) & AT91_SSC_DATALEN); 517 | (((bits - 1) << 0) & AT91_SSC_DATALEN);
491 break; 518 break;
492 519
493 case SND_SOC_DAIFMT_CBS_CFM: 520 case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
494 case SND_SOC_DAIFMT_CBM_CFS: 521 /*
522 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
523 *
524 * The SSC transmit and receive clocks are generated from the
525 * MCK divider, and the BCLK signal is output on the SSC TK line.
526 */
527 rcmr = (( ssc_p->rcmr_period << 24) & AT91_SSC_PERIOD)
528 | (( 1 << 16) & AT91_SSC_STTDLY)
529 | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START)
530 | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
531 | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
532 | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
533
534 rfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
535 | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS)
536 | (( 0 << 16) & AT91_SSC_FSLEN)
537 | (((channels - 1) << 8) & AT91_SSC_DATNB)
538 | (( 1 << 7) & AT91_SSC_MSBF)
539 | (( 0 << 5) & AT91_SSC_LOOP)
540 | (((bits - 1) << 0) & AT91_SSC_DATALEN);
541
542 tcmr = (( ssc_p->tcmr_period << 24) & AT91_SSC_PERIOD)
543 | (( 1 << 16) & AT91_SSC_STTDLY)
544 | (( AT91_SSC_START_RISING_RF ) & AT91_SSC_START)
545 | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
546 | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO)
547 | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
548
549 tfmr = (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
550 | (( 0 << 23) & AT91_SSC_FSDEN)
551 | (( AT91_SSC_FSOS_POSITIVE ) & AT91_SSC_FSOS)
552 | (( 0 << 16) & AT91_SSC_FSLEN)
553 | (((channels - 1) << 8) & AT91_SSC_DATNB)
554 | (( 1 << 7) & AT91_SSC_MSBF)
555 | (( 0 << 5) & AT91_SSC_DATDEF)
556 | (((bits - 1) << 0) & AT91_SSC_DATALEN);
557
558
559
560 break;
561
562 case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
495 default: 563 default:
496 printk(KERN_WARNING "at91-i2s: unsupported DAI format 0x%x.\n", 564 printk(KERN_WARNING "at91-ssc: unsupported DAI format 0x%x.\n",
497 ssc_p->daifmt); 565 ssc_p->daifmt);
498 return -EINVAL; 566 return -EINVAL;
499 break; 567 break;
@@ -518,9 +586,9 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream,
518 at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0); 586 at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0);
519 at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0); 587 at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0);
520 588
521 if ((ret = request_irq(ssc_p->ssc.pid, at91_i2s_interrupt, 589 if ((ret = request_irq(ssc_p->ssc.pid, at91_ssc_interrupt,
522 0, ssc_p->name, ssc_p)) < 0) { 590 0, ssc_p->name, ssc_p)) < 0) {
523 printk(KERN_WARNING "at91-i2s: request_irq failure\n"); 591 printk(KERN_WARNING "at91-ssc: request_irq failure\n");
524 592
525 DBG("Stopping pid %d clock\n", ssc_p->ssc.pid); 593 DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
526 at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid); 594 at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid);
@@ -546,7 +614,7 @@ static int at91_i2s_hw_params(struct snd_pcm_substream *substream,
546} 614}
547 615
548 616
549static int at91_i2s_prepare(struct snd_pcm_substream *substream) 617static int at91_ssc_prepare(struct snd_pcm_substream *substream)
550{ 618{
551 struct snd_soc_pcm_runtime *rtd = substream->private_data; 619 struct snd_soc_pcm_runtime *rtd = substream->private_data;
552 struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id]; 620 struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
@@ -566,7 +634,7 @@ static int at91_i2s_prepare(struct snd_pcm_substream *substream)
566 634
567 635
568#ifdef CONFIG_PM 636#ifdef CONFIG_PM
569static int at91_i2s_suspend(struct platform_device *pdev, 637static int at91_ssc_suspend(struct platform_device *pdev,
570 struct snd_soc_cpu_dai *cpu_dai) 638 struct snd_soc_cpu_dai *cpu_dai)
571{ 639{
572 struct at91_ssc_info *ssc_p; 640 struct at91_ssc_info *ssc_p;
@@ -594,7 +662,7 @@ static int at91_i2s_suspend(struct platform_device *pdev,
594 return 0; 662 return 0;
595} 663}
596 664
597static int at91_i2s_resume(struct platform_device *pdev, 665static int at91_ssc_resume(struct platform_device *pdev,
598 struct snd_soc_cpu_dai *cpu_dai) 666 struct snd_soc_cpu_dai *cpu_dai)
599{ 667{
600 struct at91_ssc_info *ssc_p; 668 struct at91_ssc_info *ssc_p;
@@ -620,102 +688,105 @@ static int at91_i2s_resume(struct platform_device *pdev,
620} 688}
621 689
622#else 690#else
623#define at91_i2s_suspend NULL 691#define at91_ssc_suspend NULL
624#define at91_i2s_resume NULL 692#define at91_ssc_resume NULL
625#endif 693#endif
626 694
627#define AT91_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 695#define AT91_SSC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
628 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ 696 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
629 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ 697 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
630 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ 698 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
631 SNDRV_PCM_RATE_96000) 699 SNDRV_PCM_RATE_96000)
632 700
633struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { 701#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
634 { .name = "at91_ssc0/i2s", 702 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
703
704struct snd_soc_cpu_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
705 { .name = "at91-ssc0",
635 .id = 0, 706 .id = 0,
636 .type = SND_SOC_DAI_I2S, 707 .type = SND_SOC_DAI_PCM,
637 .suspend = at91_i2s_suspend, 708 .suspend = at91_ssc_suspend,
638 .resume = at91_i2s_resume, 709 .resume = at91_ssc_resume,
639 .playback = { 710 .playback = {
640 .channels_min = 1, 711 .channels_min = 1,
641 .channels_max = 2, 712 .channels_max = 2,
642 .rates = AT91_I2S_RATES, 713 .rates = AT91_SSC_RATES,
643 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 714 .formats = AT91_SSC_FORMATS,},
644 .capture = { 715 .capture = {
645 .channels_min = 1, 716 .channels_min = 1,
646 .channels_max = 2, 717 .channels_max = 2,
647 .rates = AT91_I2S_RATES, 718 .rates = AT91_SSC_RATES,
648 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 719 .formats = AT91_SSC_FORMATS,},
649 .ops = { 720 .ops = {
650 .startup = at91_i2s_startup, 721 .startup = at91_ssc_startup,
651 .shutdown = at91_i2s_shutdown, 722 .shutdown = at91_ssc_shutdown,
652 .prepare = at91_i2s_prepare, 723 .prepare = at91_ssc_prepare,
653 .hw_params = at91_i2s_hw_params,}, 724 .hw_params = at91_ssc_hw_params,},
654 .dai_ops = { 725 .dai_ops = {
655 .set_sysclk = at91_i2s_set_dai_sysclk, 726 .set_sysclk = at91_ssc_set_dai_sysclk,
656 .set_fmt = at91_i2s_set_dai_fmt, 727 .set_fmt = at91_ssc_set_dai_fmt,
657 .set_clkdiv = at91_i2s_set_dai_clkdiv,}, 728 .set_clkdiv = at91_ssc_set_dai_clkdiv,},
658 .private_data = &ssc_info[0].ssc, 729 .private_data = &ssc_info[0].ssc,
659 }, 730 },
660#if NUM_SSC_DEVICES == 3 731#if NUM_SSC_DEVICES == 3
661 { .name = "at91_ssc1/i2s", 732 { .name = "at91-ssc1",
662 .id = 1, 733 .id = 1,
663 .type = SND_SOC_DAI_I2S, 734 .type = SND_SOC_DAI_PCM,
664 .suspend = at91_i2s_suspend, 735 .suspend = at91_ssc_suspend,
665 .resume = at91_i2s_resume, 736 .resume = at91_ssc_resume,
666 .playback = { 737 .playback = {
667 .channels_min = 1, 738 .channels_min = 1,
668 .channels_max = 2, 739 .channels_max = 2,
669 .rates = AT91_I2S_RATES, 740 .rates = AT91_SSC_RATES,
670 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 741 .formats = AT91_SSC_FORMATS,},
671 .capture = { 742 .capture = {
672 .channels_min = 1, 743 .channels_min = 1,
673 .channels_max = 2, 744 .channels_max = 2,
674 .rates = AT91_I2S_RATES, 745 .rates = AT91_SSC_RATES,
675 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 746 .formats = AT91_SSC_FORMATS,},
676 .ops = { 747 .ops = {
677 .startup = at91_i2s_startup, 748 .startup = at91_ssc_startup,
678 .shutdown = at91_i2s_shutdown, 749 .shutdown = at91_ssc_shutdown,
679 .prepare = at91_i2s_prepare, 750 .prepare = at91_ssc_prepare,
680 .hw_params = at91_i2s_hw_params,}, 751 .hw_params = at91_ssc_hw_params,},
681 .dai_ops = { 752 .dai_ops = {
682 .set_sysclk = at91_i2s_set_dai_sysclk, 753 .set_sysclk = at91_ssc_set_dai_sysclk,
683 .set_fmt = at91_i2s_set_dai_fmt, 754 .set_fmt = at91_ssc_set_dai_fmt,
684 .set_clkdiv = at91_i2s_set_dai_clkdiv,}, 755 .set_clkdiv = at91_ssc_set_dai_clkdiv,},
685 .private_data = &ssc_info[1].ssc, 756 .private_data = &ssc_info[1].ssc,
686 }, 757 },
687 { .name = "at91_ssc2/i2s", 758 { .name = "at91-ssc2",
688 .id = 2, 759 .id = 2,
689 .type = SND_SOC_DAI_I2S, 760 .type = SND_SOC_DAI_PCM,
690 .suspend = at91_i2s_suspend, 761 .suspend = at91_ssc_suspend,
691 .resume = at91_i2s_resume, 762 .resume = at91_ssc_resume,
692 .playback = { 763 .playback = {
693 .channels_min = 1, 764 .channels_min = 1,
694 .channels_max = 2, 765 .channels_max = 2,
695 .rates = AT91_I2S_RATES, 766 .rates = AT91_SSC_RATES,
696 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 767 .formats = AT91_SSC_FORMATS,},
697 .capture = { 768 .capture = {
698 .channels_min = 1, 769 .channels_min = 1,
699 .channels_max = 2, 770 .channels_max = 2,
700 .rates = AT91_I2S_RATES, 771 .rates = AT91_SSC_RATES,
701 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 772 .formats = AT91_SSC_FORMATS,},
702 .ops = { 773 .ops = {
703 .startup = at91_i2s_startup, 774 .startup = at91_ssc_startup,
704 .shutdown = at91_i2s_shutdown, 775 .shutdown = at91_ssc_shutdown,
705 .prepare = at91_i2s_prepare, 776 .prepare = at91_ssc_prepare,
706 .hw_params = at91_i2s_hw_params,}, 777 .hw_params = at91_ssc_hw_params,},
707 .dai_ops = { 778 .dai_ops = {
708 .set_sysclk = at91_i2s_set_dai_sysclk, 779 .set_sysclk = at91_ssc_set_dai_sysclk,
709 .set_fmt = at91_i2s_set_dai_fmt, 780 .set_fmt = at91_ssc_set_dai_fmt,
710 .set_clkdiv = at91_i2s_set_dai_clkdiv,}, 781 .set_clkdiv = at91_ssc_set_dai_clkdiv,},
711 .private_data = &ssc_info[2].ssc, 782 .private_data = &ssc_info[2].ssc,
712 }, 783 },
713#endif 784#endif
714}; 785};
715 786
716EXPORT_SYMBOL_GPL(at91_i2s_dai); 787EXPORT_SYMBOL_GPL(at91_ssc_dai);
717 788
718/* Module information */ 789/* Module information */
719MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com"); 790MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");
720MODULE_DESCRIPTION("AT91 I2S ASoC Interface"); 791MODULE_DESCRIPTION("AT91 SSC ASoC Interface");
721MODULE_LICENSE("GPL"); 792MODULE_LICENSE("GPL");
diff --git a/sound/soc/at91/at91-i2s.h b/sound/soc/at91/at91-ssc.h
index f8a875ba0ccc..b188f973df9f 100644
--- a/sound/soc/at91/at91-i2s.h
+++ b/sound/soc/at91/at91-ssc.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * at91-i2s.h - ALSA I2S interface for the Atmel AT91 SoC 2 * at91-ssc.h - ALSA SSC interface for the Atmel AT91 SoC
3 * 3 *
4 * Author: Frank Mandarino <fmandarino@endrelia.com> 4 * Author: Frank Mandarino <fmandarino@endrelia.com>
5 * Endrelia Technologies Inc. 5 * Endrelia Technologies Inc.
@@ -10,18 +10,18 @@
10 * published by the Free Software Foundation. 10 * published by the Free Software Foundation.
11 */ 11 */
12 12
13#ifndef _AT91_I2S_H 13#ifndef _AT91_SSC_H
14#define _AT91_I2S_H 14#define _AT91_SSC_H
15 15
16/* I2S system clock ids */ 16/* SSC system clock ids */
17#define AT91_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */ 17#define AT91_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */
18 18
19/* I2S divider ids */ 19/* SSC divider ids */
20#define AT91SSC_CMR_DIV 0 /* MCK divider for BCLK */ 20#define AT91SSC_CMR_DIV 0 /* MCK divider for BCLK */
21#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */ 21#define AT91SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */
22#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */ 22#define AT91SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */
23 23
24extern struct snd_soc_cpu_dai at91_i2s_dai[]; 24extern struct snd_soc_cpu_dai at91_ssc_dai[];
25 25
26#endif /* _AT91_I2S_H */ 26#endif /* _AT91_SSC_H */
27 27
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
index 8179df3bb2f3..820a676c56bf 100644
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ b/sound/soc/at91/eti_b1_wm8731.c
@@ -40,7 +40,7 @@
40 40
41#include "../codecs/wm8731.h" 41#include "../codecs/wm8731.h"
42#include "at91-pcm.h" 42#include "at91-pcm.h"
43#include "at91-i2s.h" 43#include "at91-ssc.h"
44 44
45#if 0 45#if 0
46#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x) 46#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x)
@@ -248,15 +248,15 @@ static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
248 248
249static struct snd_soc_dai_link eti_b1_dai = { 249static struct snd_soc_dai_link eti_b1_dai = {
250 .name = "WM8731", 250 .name = "WM8731",
251 .stream_name = "WM8731", 251 .stream_name = "WM8731 PCM",
252 .cpu_dai = &at91_i2s_dai[1], 252 .cpu_dai = &at91_ssc_dai[1],
253 .codec_dai = &wm8731_dai, 253 .codec_dai = &wm8731_dai,
254 .init = eti_b1_wm8731_init, 254 .init = eti_b1_wm8731_init,
255 .ops = &eti_b1_ops, 255 .ops = &eti_b1_ops,
256}; 256};
257 257
258static struct snd_soc_machine snd_soc_machine_eti_b1 = { 258static struct snd_soc_machine snd_soc_machine_eti_b1 = {
259 .name = "ETI_B1", 259 .name = "ETI_B1_WM8731",
260 .dai_link = &eti_b1_dai, 260 .dai_link = &eti_b1_dai,
261 .num_links = 1, 261 .num_links = 1,
262}; 262};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ec2a2787957a..e5fb437b86e8 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -10,6 +10,10 @@ config SND_SOC_WM8750
10 tristate 10 tristate
11 depends on SND_SOC 11 depends on SND_SOC
12 12
13config SND_SOC_WM8753
14 tristate
15 depends on SND_SOC
16
13config SND_SOC_WM9712 17config SND_SOC_WM9712
14 tristate 18 tristate
15 depends on SND_SOC 19 depends on SND_SOC
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 3249a6e4f1d0..e39a747a17cf 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,9 +1,11 @@
1snd-soc-ac97-objs := ac97.o 1snd-soc-ac97-objs := ac97.o
2snd-soc-wm8731-objs := wm8731.o 2snd-soc-wm8731-objs := wm8731.o
3snd-soc-wm8750-objs := wm8750.o 3snd-soc-wm8750-objs := wm8750.o
4snd-soc-wm8753-objs := wm8753.o
4snd-soc-wm9712-objs := wm9712.o 5snd-soc-wm9712-objs := wm9712.o
5 6
6obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o 7obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
7obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o 8obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
8obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o 9obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
10obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
9obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o 11obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 55bc55eb6e24..0cdef971cbd3 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -60,6 +60,7 @@ static struct snd_soc_codec_dai ac97_dai = {
60 .ops = { 60 .ops = {
61 .prepare = ac97_prepare,}, 61 .prepare = ac97_prepare,},
62}; 62};
63EXPORT_SYMBOL_GPL(ac97_dai);
63 64
64static unsigned int ac97_read(struct snd_soc_codec *codec, 65static unsigned int ac97_read(struct snd_soc_codec *codec,
65 unsigned int reg) 66 unsigned int reg)
diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h
index 930ddfc2321a..2bf6d69fd069 100644
--- a/sound/soc/codecs/ac97.h
+++ b/sound/soc/codecs/ac97.h
@@ -14,5 +14,6 @@
14#define __LINUX_SND_SOC_AC97_H 14#define __LINUX_SND_SOC_AC97_H
15 15
16extern struct snd_soc_codec_device soc_codec_dev_ac97; 16extern struct snd_soc_codec_device soc_codec_dev_ac97;
17extern struct snd_soc_codec_dai ac97_dai;
17 18
18#endif 19#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 7073e8e294fc..28684eeda738 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -808,7 +808,7 @@ static int wm8750_init(struct snd_soc_device *socdev)
808 codec->dai = &wm8750_dai; 808 codec->dai = &wm8750_dai;
809 codec->num_dai = 1; 809 codec->num_dai = 1;
810 codec->reg_cache_size = sizeof(wm8750_reg); 810 codec->reg_cache_size = sizeof(wm8750_reg);
811 codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KRENEL); 811 codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KERNEL);
812 if (codec->reg_cache == NULL) 812 if (codec->reg_cache == NULL)
813 return -ENOMEM; 813 return -ENOMEM;
814 814
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
new file mode 100644
index 000000000000..efced934566d
--- /dev/null
+++ b/sound/soc/codecs/wm8753.c
@@ -0,0 +1,1811 @@
1/*
2 * wm8753.c -- WM8753 ALSA Soc Audio driver
3 *
4 * Copyright 2003 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood
6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * Notes:
14 * The WM8753 is a low power, high quality stereo codec with integrated PCM
15 * codec designed for portable digital telephony applications.
16 *
17 * Dual DAI:-
18 *
19 * This driver support 2 DAI PCM's. This makes the default PCM available for
20 * HiFi audio (e.g. MP3, ogg) playback/capture and the other PCM available for
21 * voice.
22 *
23 * Please note that the voice PCM can be connected directly to a Bluetooth
24 * codec or GSM modem and thus cannot be read or written to, although it is
25 * available to be configured with snd_hw_params(), etc and kcontrols in the
26 * normal alsa manner.
27 *
28 * Fast DAI switching:-
29 *
30 * The driver can now fast switch between the DAI configurations via a
31 * an alsa kcontrol. This allows the PCM to remain open.
32 *
33 */
34
35#include <linux/module.h>
36#include <linux/moduleparam.h>
37#include <linux/version.h>
38#include <linux/kernel.h>
39#include <linux/init.h>
40#include <linux/delay.h>
41#include <linux/pm.h>
42#include <linux/i2c.h>
43#include <linux/platform_device.h>
44#include <sound/driver.h>
45#include <sound/core.h>
46#include <sound/pcm.h>
47#include <sound/pcm_params.h>
48#include <sound/soc.h>
49#include <sound/soc-dapm.h>
50#include <sound/initval.h>
51#include <asm/div64.h>
52
53#include "wm8753.h"
54
55#define AUDIO_NAME "wm8753"
56#define WM8753_VERSION "0.16"
57
58/*
59 * Debug
60 */
61
62#define WM8753_DEBUG 0
63
64#ifdef WM8753_DEBUG
65#define dbg(format, arg...) \
66 printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
67#else
68#define dbg(format, arg...) do {} while (0)
69#endif
70#define err(format, arg...) \
71 printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
72#define info(format, arg...) \
73 printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
74#define warn(format, arg...) \
75 printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
76
77static int caps_charge = 2000;
78module_param(caps_charge, int, 0);
79MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
80
81static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
82 unsigned int mode);
83
84/* codec private data */
85struct wm8753_priv {
86 unsigned int sysclk;
87 unsigned int pcmclk;
88};
89
90/*
91 * wm8753 register cache
92 * We can't read the WM8753 register space when we
93 * are using 2 wire for device control, so we cache them instead.
94 */
95static const u16 wm8753_reg[] = {
96 0x0008, 0x0000, 0x000a, 0x000a,
97 0x0033, 0x0000, 0x0007, 0x00ff,
98 0x00ff, 0x000f, 0x000f, 0x007b,
99 0x0000, 0x0032, 0x0000, 0x00c3,
100 0x00c3, 0x00c0, 0x0000, 0x0000,
101 0x0000, 0x0000, 0x0000, 0x0000,
102 0x0000, 0x0000, 0x0000, 0x0000,
103 0x0000, 0x0000, 0x0000, 0x0055,
104 0x0005, 0x0050, 0x0055, 0x0050,
105 0x0055, 0x0050, 0x0055, 0x0079,
106 0x0079, 0x0079, 0x0079, 0x0079,
107 0x0000, 0x0000, 0x0000, 0x0000,
108 0x0097, 0x0097, 0x0000, 0x0004,
109 0x0000, 0x0083, 0x0024, 0x01ba,
110 0x0000, 0x0083, 0x0024, 0x01ba,
111 0x0000, 0x0000
112};
113
114/*
115 * read wm8753 register cache
116 */
117static inline unsigned int wm8753_read_reg_cache(struct snd_soc_codec *codec,
118 unsigned int reg)
119{
120 u16 *cache = codec->reg_cache;
121 if (reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))
122 return -1;
123 return cache[reg - 1];
124}
125
126/*
127 * write wm8753 register cache
128 */
129static inline void wm8753_write_reg_cache(struct snd_soc_codec *codec,
130 unsigned int reg, unsigned int value)
131{
132 u16 *cache = codec->reg_cache;
133 if (reg < 1 || reg > 0x3f)
134 return;
135 cache[reg - 1] = value;
136}
137
138/*
139 * write to the WM8753 register space
140 */
141static int wm8753_write(struct snd_soc_codec *codec, unsigned int reg,
142 unsigned int value)
143{
144 u8 data[2];
145
146 /* data is
147 * D15..D9 WM8753 register offset
148 * D8...D0 register data
149 */
150 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
151 data[1] = value & 0x00ff;
152
153 wm8753_write_reg_cache (codec, reg, value);
154 if (codec->hw_write(codec->control_data, data, 2) == 2)
155 return 0;
156 else
157 return -EIO;
158}
159
160#define wm8753_reset(c) wm8753_write(c, WM8753_RESET, 0)
161
162/*
163 * WM8753 Controls
164 */
165static const char *wm8753_base[] = {"Linear Control", "Adaptive Boost"};
166static const char *wm8753_base_filter[] =
167 {"130Hz @ 48kHz", "200Hz @ 48kHz", "100Hz @ 16kHz", "400Hz @ 48kHz",
168 "100Hz @ 8kHz", "200Hz @ 8kHz"};
169static const char *wm8753_treble[] = {"8kHz", "4kHz"};
170static const char *wm8753_alc_func[] = {"Off", "Right", "Left", "Stereo"};
171static const char *wm8753_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"};
172static const char *wm8753_3d_func[] = {"Capture", "Playback"};
173static const char *wm8753_3d_uc[] = {"2.2kHz", "1.5kHz"};
174static const char *wm8753_3d_lc[] = {"200Hz", "500Hz"};
175static const char *wm8753_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz"};
176static const char *wm8753_mono_mix[] = {"Stereo", "Left", "Right", "Mono"};
177static const char *wm8753_dac_phase[] = {"Non Inverted", "Inverted"};
178static const char *wm8753_line_mix[] = {"Line 1 + 2", "Line 1 - 2",
179 "Line 1", "Line 2"};
180static const char *wm8753_mono_mux[] = {"Line Mix", "Rx Mix"};
181static const char *wm8753_right_mux[] = {"Line 2", "Rx Mix"};
182static const char *wm8753_left_mux[] = {"Line 1", "Rx Mix"};
183static const char *wm8753_rxmsel[] = {"RXP - RXN", "RXP + RXN", "RXP", "RXN"};
184static const char *wm8753_sidetone_mux[] = {"Left PGA", "Mic 1", "Mic 2",
185 "Right PGA"};
186static const char *wm8753_mono2_src[] = {"Inverted Mono 1", "Left", "Right",
187 "Left + Right"};
188static const char *wm8753_out3[] = {"VREF", "ROUT2", "Left + Right"};
189static const char *wm8753_out4[] = {"VREF", "Capture ST", "LOUT2"};
190static const char *wm8753_radcsel[] = {"PGA", "Line or RXP-RXN", "Sidetone"};
191static const char *wm8753_ladcsel[] = {"PGA", "Line or RXP-RXN", "Line"};
192static const char *wm8753_mono_adc[] = {"Stereo", "Analogue Mix Left",
193 "Analogue Mix Right", "Digital Mono Mix"};
194static const char *wm8753_adc_hp[] = {"3.4Hz @ 48kHz", "82Hz @ 16k",
195 "82Hz @ 8kHz", "170Hz @ 8kHz"};
196static const char *wm8753_adc_filter[] = {"HiFi", "Voice"};
197static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"};
198static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"};
199static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC",
200 "Channel Swap"};
201
202static const struct soc_enum wm8753_enum[] = {
203SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base),
204SOC_ENUM_SINGLE(WM8753_BASS, 4, 6, wm8753_base_filter),
205SOC_ENUM_SINGLE(WM8753_TREBLE, 6, 2, wm8753_treble),
206SOC_ENUM_SINGLE(WM8753_ALC1, 7, 4, wm8753_alc_func),
207SOC_ENUM_SINGLE(WM8753_NGATE, 1, 2, wm8753_ng_type),
208SOC_ENUM_SINGLE(WM8753_3D, 7, 2, wm8753_3d_func),
209SOC_ENUM_SINGLE(WM8753_3D, 6, 2, wm8753_3d_uc),
210SOC_ENUM_SINGLE(WM8753_3D, 5, 2, wm8753_3d_lc),
211SOC_ENUM_SINGLE(WM8753_DAC, 1, 4, wm8753_deemp),
212SOC_ENUM_SINGLE(WM8753_DAC, 4, 4, wm8753_mono_mix),
213SOC_ENUM_SINGLE(WM8753_DAC, 6, 2, wm8753_dac_phase),
214SOC_ENUM_SINGLE(WM8753_INCTL1, 3, 4, wm8753_line_mix),
215SOC_ENUM_SINGLE(WM8753_INCTL1, 2, 2, wm8753_mono_mux),
216SOC_ENUM_SINGLE(WM8753_INCTL1, 1, 2, wm8753_right_mux),
217SOC_ENUM_SINGLE(WM8753_INCTL1, 0, 2, wm8753_left_mux),
218SOC_ENUM_SINGLE(WM8753_INCTL2, 6, 4, wm8753_rxmsel),
219SOC_ENUM_SINGLE(WM8753_INCTL2, 4, 4, wm8753_sidetone_mux),
220SOC_ENUM_SINGLE(WM8753_OUTCTL, 7, 4, wm8753_mono2_src),
221SOC_ENUM_SINGLE(WM8753_OUTCTL, 0, 3, wm8753_out3),
222SOC_ENUM_SINGLE(WM8753_ADCTL2, 7, 3, wm8753_out4),
223SOC_ENUM_SINGLE(WM8753_ADCIN, 2, 3, wm8753_radcsel),
224SOC_ENUM_SINGLE(WM8753_ADCIN, 0, 3, wm8753_ladcsel),
225SOC_ENUM_SINGLE(WM8753_ADCIN, 4, 4, wm8753_mono_adc),
226SOC_ENUM_SINGLE(WM8753_ADC, 2, 4, wm8753_adc_hp),
227SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter),
228SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel),
229SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode),
230SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel),
231};
232
233
234static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
235 struct snd_ctl_elem_value *ucontrol)
236{
237 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
238 int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);
239
240 ucontrol->value.integer.value[0] = (mode & 0xc) >> 2;
241 return 0;
242}
243
244static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
245 struct snd_ctl_elem_value *ucontrol)
246{
247 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
248 int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);
249
250 if (((mode &0xc) >> 2) == ucontrol->value.integer.value[0])
251 return 0;
252
253 mode &= 0xfff3;
254 mode |= (ucontrol->value.integer.value[0] << 2);
255
256 wm8753_write(codec, WM8753_IOCTL, mode);
257 wm8753_set_dai_mode(codec, ucontrol->value.integer.value[0]);
258 return 1;
259}
260
261static const struct snd_kcontrol_new wm8753_snd_controls[] = {
262SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
263
264SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0),
265
266SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),
267SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),
268
269SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),
270
271SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),
272SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),
273SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),
274
275SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),
276SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),
277
278SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
279SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
280SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1),
281SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
282
283SOC_ENUM("Bass Boost", wm8753_enum[0]),
284SOC_ENUM("Bass Filter", wm8753_enum[1]),
285SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),
286
287SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
288SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
289
290SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1),
291SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1),
292
293SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
294SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
295SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),
296
297SOC_ENUM("Capture Filter Select", wm8753_enum[23]),
298SOC_ENUM("Capture Filter Cut-off", wm8753_enum[24]),
299SOC_SINGLE("Capture Filter Switch", WM8753_ADC, 0, 1, 1),
300
301SOC_SINGLE("ALC Capture Target Volume", WM8753_ALC1, 0, 7, 0),
302SOC_SINGLE("ALC Capture Max Volume", WM8753_ALC1, 4, 7, 0),
303SOC_ENUM("ALC Capture Function", wm8753_enum[3]),
304SOC_SINGLE("ALC Capture ZC Switch", WM8753_ALC2, 8, 1, 0),
305SOC_SINGLE("ALC Capture Hold Time", WM8753_ALC2, 0, 15, 1),
306SOC_SINGLE("ALC Capture Decay Time", WM8753_ALC3, 4, 15, 1),
307SOC_SINGLE("ALC Capture Attack Time", WM8753_ALC3, 0, 15, 0),
308SOC_SINGLE("ALC Capture NG Threshold", WM8753_NGATE, 3, 31, 0),
309SOC_ENUM("ALC Capture NG Type", wm8753_enum[4]),
310SOC_SINGLE("ALC Capture NG Switch", WM8753_NGATE, 0, 1, 0),
311
312SOC_ENUM("3D Function", wm8753_enum[5]),
313SOC_ENUM("3D Upper Cut-off", wm8753_enum[6]),
314SOC_ENUM("3D Lower Cut-off", wm8753_enum[7]),
315SOC_SINGLE("3D Volume", WM8753_3D, 1, 15, 0),
316SOC_SINGLE("3D Switch", WM8753_3D, 0, 1, 0),
317
318SOC_SINGLE("Capture 6dB Attenuate", WM8753_ADCTL1, 2, 1, 0),
319SOC_SINGLE("Playback 6dB Attenuate", WM8753_ADCTL1, 1, 1, 0),
320
321SOC_ENUM("De-emphasis", wm8753_enum[8]),
322SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
323SOC_ENUM("Playback Phase", wm8753_enum[10]),
324
325SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
326SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
327
328SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
329
330SOC_ENUM("ADC Data Select", wm8753_enum[27]),
331};
332
333/* add non dapm controls */
334static int wm8753_add_controls(struct snd_soc_codec *codec)
335{
336 int err, i;
337
338 for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {
339 err = snd_ctl_add(codec->card,
340 snd_soc_cnew(&wm8753_snd_controls[i],codec, NULL));
341 if (err < 0)
342 return err;
343 }
344 return 0;
345}
346
347/*
348 * _DAPM_ Controls
349 */
350
351/* Left Mixer */
352static const struct snd_kcontrol_new wm8753_left_mixer_controls[] = {
353SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_LOUTM2, 8, 1, 0),
354SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_LOUTM2, 7, 1, 0),
355SOC_DAPM_SINGLE("Left Playback Switch", WM8753_LOUTM1, 8, 1, 0),
356SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_LOUTM1, 7, 1, 0),
357};
358
359/* Right mixer */
360static const struct snd_kcontrol_new wm8753_right_mixer_controls[] = {
361SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_ROUTM2, 8, 1, 0),
362SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_ROUTM2, 7, 1, 0),
363SOC_DAPM_SINGLE("Right Playback Switch", WM8753_ROUTM1, 8, 1, 0),
364SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0),
365};
366
367/* Mono mixer */
368static const struct snd_kcontrol_new wm8753_mono_mixer_controls[] = {
369SOC_DAPM_SINGLE("Left Playback Switch", WM8753_MOUTM1, 8, 1, 0),
370SOC_DAPM_SINGLE("Right Playback Switch", WM8753_MOUTM2, 8, 1, 0),
371SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_MOUTM2, 3, 1, 0),
372SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_MOUTM2, 7, 1, 0),
373SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_MOUTM1, 7, 1, 0),
374};
375
376/* Mono 2 Mux */
377static const struct snd_kcontrol_new wm8753_mono2_controls =
378SOC_DAPM_ENUM("Route", wm8753_enum[17]);
379
380/* Out 3 Mux */
381static const struct snd_kcontrol_new wm8753_out3_controls =
382SOC_DAPM_ENUM("Route", wm8753_enum[18]);
383
384/* Out 4 Mux */
385static const struct snd_kcontrol_new wm8753_out4_controls =
386SOC_DAPM_ENUM("Route", wm8753_enum[19]);
387
388/* ADC Mono Mix */
389static const struct snd_kcontrol_new wm8753_adc_mono_controls =
390SOC_DAPM_ENUM("Route", wm8753_enum[22]);
391
392/* Record mixer */
393static const struct snd_kcontrol_new wm8753_record_mixer_controls[] = {
394SOC_DAPM_SINGLE("Voice Capture Switch", WM8753_RECMIX2, 3, 1, 0),
395SOC_DAPM_SINGLE("Left Capture Switch", WM8753_RECMIX1, 3, 1, 0),
396SOC_DAPM_SINGLE("Right Capture Switch", WM8753_RECMIX1, 7, 1, 0),
397};
398
399/* Left ADC mux */
400static const struct snd_kcontrol_new wm8753_adc_left_controls =
401SOC_DAPM_ENUM("Route", wm8753_enum[21]);
402
403/* Right ADC mux */
404static const struct snd_kcontrol_new wm8753_adc_right_controls =
405SOC_DAPM_ENUM("Route", wm8753_enum[20]);
406
407/* MIC mux */
408static const struct snd_kcontrol_new wm8753_mic_mux_controls =
409SOC_DAPM_ENUM("Route", wm8753_enum[16]);
410
411/* ALC mixer */
412static const struct snd_kcontrol_new wm8753_alc_mixer_controls[] = {
413SOC_DAPM_SINGLE("Line Capture Switch", WM8753_INCTL2, 3, 1, 0),
414SOC_DAPM_SINGLE("Mic2 Capture Switch", WM8753_INCTL2, 2, 1, 0),
415SOC_DAPM_SINGLE("Mic1 Capture Switch", WM8753_INCTL2, 1, 1, 0),
416SOC_DAPM_SINGLE("Rx Capture Switch", WM8753_INCTL2, 0, 1, 0),
417};
418
419/* Left Line mux */
420static const struct snd_kcontrol_new wm8753_line_left_controls =
421SOC_DAPM_ENUM("Route", wm8753_enum[14]);
422
423/* Right Line mux */
424static const struct snd_kcontrol_new wm8753_line_right_controls =
425SOC_DAPM_ENUM("Route", wm8753_enum[13]);
426
427/* Mono Line mux */
428static const struct snd_kcontrol_new wm8753_line_mono_controls =
429SOC_DAPM_ENUM("Route", wm8753_enum[12]);
430
431/* Line mux and mixer */
432static const struct snd_kcontrol_new wm8753_line_mux_mix_controls =
433SOC_DAPM_ENUM("Route", wm8753_enum[11]);
434
435/* Rx mux and mixer */
436static const struct snd_kcontrol_new wm8753_rx_mux_mix_controls =
437SOC_DAPM_ENUM("Route", wm8753_enum[15]);
438
439/* Mic Selector Mux */
440static const struct snd_kcontrol_new wm8753_mic_sel_mux_controls =
441SOC_DAPM_ENUM("Route", wm8753_enum[25]);
442
443static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
444SND_SOC_DAPM_MICBIAS("Mic Bias", WM8753_PWR1, 5, 0),
445SND_SOC_DAPM_MIXER("Left Mixer", WM8753_PWR4, 0, 0,
446 &wm8753_left_mixer_controls[0], ARRAY_SIZE(wm8753_left_mixer_controls)),
447SND_SOC_DAPM_PGA("Left Out 1", WM8753_PWR3, 8, 0, NULL, 0),
448SND_SOC_DAPM_PGA("Left Out 2", WM8753_PWR3, 6, 0, NULL, 0),
449SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", WM8753_PWR1, 3, 0),
450SND_SOC_DAPM_OUTPUT("LOUT1"),
451SND_SOC_DAPM_OUTPUT("LOUT2"),
452SND_SOC_DAPM_MIXER("Right Mixer", WM8753_PWR4, 1, 0,
453 &wm8753_right_mixer_controls[0], ARRAY_SIZE(wm8753_right_mixer_controls)),
454SND_SOC_DAPM_PGA("Right Out 1", WM8753_PWR3, 7, 0, NULL, 0),
455SND_SOC_DAPM_PGA("Right Out 2", WM8753_PWR3, 5, 0, NULL, 0),
456SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", WM8753_PWR1, 2, 0),
457SND_SOC_DAPM_OUTPUT("ROUT1"),
458SND_SOC_DAPM_OUTPUT("ROUT2"),
459SND_SOC_DAPM_MIXER("Mono Mixer", WM8753_PWR4, 2, 0,
460 &wm8753_mono_mixer_controls[0], ARRAY_SIZE(wm8753_mono_mixer_controls)),
461SND_SOC_DAPM_PGA("Mono Out 1", WM8753_PWR3, 2, 0, NULL, 0),
462SND_SOC_DAPM_PGA("Mono Out 2", WM8753_PWR3, 1, 0, NULL, 0),
463SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0),
464SND_SOC_DAPM_OUTPUT("MONO1"),
465SND_SOC_DAPM_MUX("Mono 2 Mux", SND_SOC_NOPM, 0, 0, &wm8753_mono2_controls),
466SND_SOC_DAPM_OUTPUT("MONO2"),
467SND_SOC_DAPM_MIXER("Out3 Left + Right", -1, 0, 0, NULL, 0),
468SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls),
469SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
470SND_SOC_DAPM_OUTPUT("OUT3"),
471SND_SOC_DAPM_MUX("Out4 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out4_controls),
472SND_SOC_DAPM_PGA("Out 4", WM8753_PWR3, 3, 0, NULL, 0),
473SND_SOC_DAPM_OUTPUT("OUT4"),
474SND_SOC_DAPM_MIXER("Playback Mixer", WM8753_PWR4, 3, 0,
475 &wm8753_record_mixer_controls[0],
476 ARRAY_SIZE(wm8753_record_mixer_controls)),
477SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8753_PWR2, 3, 0),
478SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8753_PWR2, 2, 0),
479SND_SOC_DAPM_MUX("Capture Left Mixer", SND_SOC_NOPM, 0, 0,
480 &wm8753_adc_mono_controls),
481SND_SOC_DAPM_MUX("Capture Right Mixer", SND_SOC_NOPM, 0, 0,
482 &wm8753_adc_mono_controls),
483SND_SOC_DAPM_MUX("Capture Left Mux", SND_SOC_NOPM, 0, 0,
484 &wm8753_adc_left_controls),
485SND_SOC_DAPM_MUX("Capture Right Mux", SND_SOC_NOPM, 0, 0,
486 &wm8753_adc_right_controls),
487SND_SOC_DAPM_MUX("Mic Sidetone Mux", SND_SOC_NOPM, 0, 0,
488 &wm8753_mic_mux_controls),
489SND_SOC_DAPM_PGA("Left Capture Volume", WM8753_PWR2, 5, 0, NULL, 0),
490SND_SOC_DAPM_PGA("Right Capture Volume", WM8753_PWR2, 4, 0, NULL, 0),
491SND_SOC_DAPM_MIXER("ALC Mixer", WM8753_PWR2, 6, 0,
492 &wm8753_alc_mixer_controls[0], ARRAY_SIZE(wm8753_alc_mixer_controls)),
493SND_SOC_DAPM_MUX("Line Left Mux", SND_SOC_NOPM, 0, 0,
494 &wm8753_line_left_controls),
495SND_SOC_DAPM_MUX("Line Right Mux", SND_SOC_NOPM, 0, 0,
496 &wm8753_line_right_controls),
497SND_SOC_DAPM_MUX("Line Mono Mux", SND_SOC_NOPM, 0, 0,
498 &wm8753_line_mono_controls),
499SND_SOC_DAPM_MUX("Line Mixer", WM8753_PWR2, 0, 0,
500 &wm8753_line_mux_mix_controls),
501SND_SOC_DAPM_MUX("Rx Mixer", WM8753_PWR2, 1, 0,
502 &wm8753_rx_mux_mix_controls),
503SND_SOC_DAPM_PGA("Mic 1 Volume", WM8753_PWR2, 8, 0, NULL, 0),
504SND_SOC_DAPM_PGA("Mic 2 Volume", WM8753_PWR2, 7, 0, NULL, 0),
505SND_SOC_DAPM_MUX("Mic Selection Mux", SND_SOC_NOPM, 0, 0,
506 &wm8753_mic_sel_mux_controls),
507SND_SOC_DAPM_INPUT("LINE1"),
508SND_SOC_DAPM_INPUT("LINE2"),
509SND_SOC_DAPM_INPUT("RXP"),
510SND_SOC_DAPM_INPUT("RXN"),
511SND_SOC_DAPM_INPUT("ACIN"),
512SND_SOC_DAPM_OUTPUT("ACOP"),
513SND_SOC_DAPM_INPUT("MIC1N"),
514SND_SOC_DAPM_INPUT("MIC1"),
515SND_SOC_DAPM_INPUT("MIC2N"),
516SND_SOC_DAPM_INPUT("MIC2"),
517SND_SOC_DAPM_VMID("VREF"),
518};
519
520static const char *audio_map[][3] = {
521 /* left mixer */
522 {"Left Mixer", "Left Playback Switch", "Left DAC"},
523 {"Left Mixer", "Voice Playback Switch", "Voice DAC"},
524 {"Left Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
525 {"Left Mixer", "Bypass Playback Switch", "Line Left Mux"},
526
527 /* right mixer */
528 {"Right Mixer", "Right Playback Switch", "Right DAC"},
529 {"Right Mixer", "Voice Playback Switch", "Voice DAC"},
530 {"Right Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
531 {"Right Mixer", "Bypass Playback Switch", "Line Right Mux"},
532
533 /* mono mixer */
534 {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
535 {"Mono Mixer", "Left Playback Switch", "Left DAC"},
536 {"Mono Mixer", "Right Playback Switch", "Right DAC"},
537 {"Mono Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
538 {"Mono Mixer", "Bypass Playback Switch", "Line Mono Mux"},
539
540 /* left out */
541 {"Left Out 1", NULL, "Left Mixer"},
542 {"Left Out 2", NULL, "Left Mixer"},
543 {"LOUT1", NULL, "Left Out 1"},
544 {"LOUT2", NULL, "Left Out 2"},
545
546 /* right out */
547 {"Right Out 1", NULL, "Right Mixer"},
548 {"Right Out 2", NULL, "Right Mixer"},
549 {"ROUT1", NULL, "Right Out 1"},
550 {"ROUT2", NULL, "Right Out 2"},
551
552 /* mono 1 out */
553 {"Mono Out 1", NULL, "Mono Mixer"},
554 {"MONO1", NULL, "Mono Out 1"},
555
556 /* mono 2 out */
557 {"Mono 2 Mux", "Left + Right", "Out3 Left + Right"},
558 {"Mono 2 Mux", "Inverted Mono 1", "MONO1"},
559 {"Mono 2 Mux", "Left", "Left Mixer"},
560 {"Mono 2 Mux", "Right", "Right Mixer"},
561 {"Mono Out 2", NULL, "Mono 2 Mux"},
562 {"MONO2", NULL, "Mono Out 2"},
563
564 /* out 3 */
565 {"Out3 Left + Right", NULL, "Left Mixer"},
566 {"Out3 Left + Right", NULL, "Right Mixer"},
567 {"Out3 Mux", "VREF", "VREF"},
568 {"Out3 Mux", "Left + Right", "Out3 Left + Right"},
569 {"Out3 Mux", "ROUT2", "ROUT2"},
570 {"Out 3", NULL, "Out3 Mux"},
571 {"OUT3", NULL, "Out 3"},
572
573 /* out 4 */
574 {"Out4 Mux", "VREF", "VREF"},
575 {"Out4 Mux", "Capture ST", "Capture ST Mixer"},
576 {"Out4 Mux", "LOUT2", "LOUT2"},
577 {"Out 4", NULL, "Out4 Mux"},
578 {"OUT4", NULL, "Out 4"},
579
580 /* record mixer */
581 {"Playback Mixer", "Left Capture Switch", "Left Mixer"},
582 {"Playback Mixer", "Voice Capture Switch", "Mono Mixer"},
583 {"Playback Mixer", "Right Capture Switch", "Right Mixer"},
584
585 /* Mic/SideTone Mux */
586 {"Mic Sidetone Mux", "Left PGA", "Left Capture Volume"},
587 {"Mic Sidetone Mux", "Right PGA", "Right Capture Volume"},
588 {"Mic Sidetone Mux", "Mic 1", "Mic 1 Volume"},
589 {"Mic Sidetone Mux", "Mic 2", "Mic 2 Volume"},
590
591 /* Capture Left Mux */
592 {"Capture Left Mux", "PGA", "Left Capture Volume"},
593 {"Capture Left Mux", "Line or RXP-RXN", "Line Left Mux"},
594 {"Capture Left Mux", "Line", "LINE1"},
595
596 /* Capture Right Mux */
597 {"Capture Right Mux", "PGA", "Right Capture Volume"},
598 {"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"},
599 {"Capture Right Mux", "Sidetone", "Capture ST Mixer"},
600
601 /* Mono Capture mixer-mux */
602 {"Capture Right Mixer", "Stereo", "Capture Right Mux"},
603 {"Capture Left Mixer", "Analogue Mix Left", "Capture Left Mux"},
604 {"Capture Left Mixer", "Analogue Mix Left", "Capture Right Mux"},
605 {"Capture Right Mixer", "Analogue Mix Right", "Capture Left Mux"},
606 {"Capture Right Mixer", "Analogue Mix Right", "Capture Right Mux"},
607 {"Capture Left Mixer", "Digital Mono Mix", "Capture Left Mux"},
608 {"Capture Left Mixer", "Digital Mono Mix", "Capture Right Mux"},
609 {"Capture Right Mixer", "Digital Mono Mix", "Capture Left Mux"},
610 {"Capture Right Mixer", "Digital Mono Mix", "Capture Right Mux"},
611
612 /* ADC */
613 {"Left ADC", NULL, "Capture Left Mixer"},
614 {"Right ADC", NULL, "Capture Right Mixer"},
615
616 /* Left Capture Volume */
617 {"Left Capture Volume", NULL, "ACIN"},
618
619 /* Right Capture Volume */
620 {"Right Capture Volume", NULL, "Mic 2 Volume"},
621
622 /* ALC Mixer */
623 {"ALC Mixer", "Line Capture Switch", "Line Mixer"},
624 {"ALC Mixer", "Mic2 Capture Switch", "Mic 2 Volume"},
625 {"ALC Mixer", "Mic1 Capture Switch", "Mic 1 Volume"},
626 {"ALC Mixer", "Rx Capture Switch", "Rx Mixer"},
627
628 /* Line Left Mux */
629 {"Line Left Mux", "Line 1", "LINE1"},
630 {"Line Left Mux", "Rx Mix", "Rx Mixer"},
631
632 /* Line Right Mux */
633 {"Line Right Mux", "Line 2", "LINE2"},
634 {"Line Right Mux", "Rx Mix", "Rx Mixer"},
635
636 /* Line Mono Mux */
637 {"Line Mono Mux", "Line Mix", "Line Mixer"},
638 {"Line Mono Mux", "Rx Mix", "Rx Mixer"},
639
640 /* Line Mixer/Mux */
641 {"Line Mixer", "Line 1 + 2", "LINE1"},
642 {"Line Mixer", "Line 1 - 2", "LINE1"},
643 {"Line Mixer", "Line 1 + 2", "LINE2"},
644 {"Line Mixer", "Line 1 - 2", "LINE2"},
645 {"Line Mixer", "Line 1", "LINE1"},
646 {"Line Mixer", "Line 2", "LINE2"},
647
648 /* Rx Mixer/Mux */
649 {"Rx Mixer", "RXP - RXN", "RXP"},
650 {"Rx Mixer", "RXP + RXN", "RXP"},
651 {"Rx Mixer", "RXP - RXN", "RXN"},
652 {"Rx Mixer", "RXP + RXN", "RXN"},
653 {"Rx Mixer", "RXP", "RXP"},
654 {"Rx Mixer", "RXN", "RXN"},
655
656 /* Mic 1 Volume */
657 {"Mic 1 Volume", NULL, "MIC1N"},
658 {"Mic 1 Volume", NULL, "Mic Selection Mux"},
659
660 /* Mic 2 Volume */
661 {"Mic 2 Volume", NULL, "MIC2N"},
662 {"Mic 2 Volume", NULL, "MIC2"},
663
664 /* Mic Selector Mux */
665 {"Mic Selection Mux", "Mic 1", "MIC1"},
666 {"Mic Selection Mux", "Mic 2", "MIC2N"},
667 {"Mic Selection Mux", "Mic 3", "MIC2"},
668
669 /* ACOP */
670 {"ACOP", NULL, "ALC Mixer"},
671
672 /* terminator */
673 {NULL, NULL, NULL},
674};
675
676static int wm8753_add_widgets(struct snd_soc_codec *codec)
677{
678 int i;
679
680 for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
681 snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
682
683 /* set up the WM8753 audio map */
684 for (i = 0; audio_map[i][0] != NULL; i++) {
685 snd_soc_dapm_connect_input(codec, audio_map[i][0],
686 audio_map[i][1], audio_map[i][2]);
687 }
688
689 snd_soc_dapm_new_widgets(codec);
690 return 0;
691}
692
693/* PLL divisors */
694struct _pll_div {
695 u32 div2:1;
696 u32 n:4;
697 u32 k:24;
698};
699
700/* The size in bits of the pll divide multiplied by 10
701 * to allow rounding later */
702#define FIXED_PLL_SIZE ((1 << 22) * 10)
703
704static void pll_factors(struct _pll_div *pll_div, unsigned int target,
705 unsigned int source)
706{
707 u64 Kpart;
708 unsigned int K, Ndiv, Nmod;
709
710 Ndiv = target / source;
711 if (Ndiv < 6) {
712 source >>= 1;
713 pll_div->div2 = 1;
714 Ndiv = target / source;
715 } else
716 pll_div->div2 = 0;
717
718 if ((Ndiv < 6) || (Ndiv > 12))
719 printk(KERN_WARNING
720 "WM8753 N value outwith recommended range! N = %d\n",Ndiv);
721
722 pll_div->n = Ndiv;
723 Nmod = target % source;
724 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
725
726 do_div(Kpart, source);
727
728 K = Kpart & 0xFFFFFFFF;
729
730 /* Check if we need to round */
731 if ((K % 10) >= 5)
732 K += 5;
733
734 /* Move down to proper range now rounding is done */
735 K /= 10;
736
737 pll_div->k = K;
738}
739
740static int wm8753_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
741 int pll_id, unsigned int freq_in, unsigned int freq_out)
742{
743 u16 reg, enable;
744 int offset;
745 struct snd_soc_codec *codec = codec_dai->codec;
746
747 if (pll_id < WM8753_PLL1 || pll_id > WM8753_PLL2)
748 return -ENODEV;
749
750 if (pll_id == WM8753_PLL1) {
751 offset = 0;
752 enable = 0x10;
753 reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xffef;
754 } else {
755 offset = 4;
756 enable = 0x8;
757 reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfff7;
758 }
759
760 if (!freq_in || !freq_out) {
761 /* disable PLL */
762 wm8753_write(codec, WM8753_PLL1CTL1 + offset, 0x0026);
763 wm8753_write(codec, WM8753_CLOCK, reg);
764 return 0;
765 } else {
766 u16 value = 0;
767 struct _pll_div pll_div;
768
769 pll_factors(&pll_div, freq_out * 8, freq_in);
770
771 /* set up N and K PLL divisor ratios */
772 /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */
773 value = (pll_div.n << 5) + ((pll_div.k & 0x3c0000) >> 18);
774 wm8753_write(codec, WM8753_PLL1CTL2 + offset, value);
775
776 /* bits 8:0 = PLL_K[17:9] */
777 value = (pll_div.k & 0x03fe00) >> 9;
778 wm8753_write(codec, WM8753_PLL1CTL3 + offset, value);
779
780 /* bits 8:0 = PLL_K[8:0] */
781 value = pll_div.k & 0x0001ff;
782 wm8753_write(codec, WM8753_PLL1CTL4 + offset, value);
783
784 /* set PLL as input and enable */
785 wm8753_write(codec, WM8753_PLL1CTL1 + offset, 0x0027 |
786 (pll_div.div2 << 3));
787 wm8753_write(codec, WM8753_CLOCK, reg | enable);
788 }
789 return 0;
790}
791
792struct _coeff_div {
793 u32 mclk;
794 u32 rate;
795 u8 sr:5;
796 u8 usb:1;
797};
798
799/* codec hifi mclk (after PLL) clock divider coefficients */
800static const struct _coeff_div coeff_div[] = {
801 /* 8k */
802 {12288000, 8000, 0x6, 0x0},
803 {11289600, 8000, 0x16, 0x0},
804 {18432000, 8000, 0x7, 0x0},
805 {16934400, 8000, 0x17, 0x0},
806 {12000000, 8000, 0x6, 0x1},
807
808 /* 11.025k */
809 {11289600, 11025, 0x18, 0x0},
810 {16934400, 11025, 0x19, 0x0},
811 {12000000, 11025, 0x19, 0x1},
812
813 /* 16k */
814 {12288000, 16000, 0xa, 0x0},
815 {18432000, 16000, 0xb, 0x0},
816 {12000000, 16000, 0xa, 0x1},
817
818 /* 22.05k */
819 {11289600, 22050, 0x1a, 0x0},
820 {16934400, 22050, 0x1b, 0x0},
821 {12000000, 22050, 0x1b, 0x1},
822
823 /* 32k */
824 {12288000, 32000, 0xc, 0x0},
825 {18432000, 32000, 0xd, 0x0},
826 {12000000, 32000, 0xa, 0x1},
827
828 /* 44.1k */
829 {11289600, 44100, 0x10, 0x0},
830 {16934400, 44100, 0x11, 0x0},
831 {12000000, 44100, 0x11, 0x1},
832
833 /* 48k */
834 {12288000, 48000, 0x0, 0x0},
835 {18432000, 48000, 0x1, 0x0},
836 {12000000, 48000, 0x0, 0x1},
837
838 /* 88.2k */
839 {11289600, 88200, 0x1e, 0x0},
840 {16934400, 88200, 0x1f, 0x0},
841 {12000000, 88200, 0x1f, 0x1},
842
843 /* 96k */
844 {12288000, 96000, 0xe, 0x0},
845 {18432000, 96000, 0xf, 0x0},
846 {12000000, 96000, 0xe, 0x1},
847};
848
849static int get_coeff(int mclk, int rate)
850{
851 int i;
852
853 for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
854 if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
855 return i;
856 }
857 return -EINVAL;
858}
859
860/*
861 * Clock after PLL and dividers
862 */
863static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
864 int clk_id, unsigned int freq, int dir)
865{
866 struct snd_soc_codec *codec = codec_dai->codec;
867 struct wm8753_priv *wm8753 = codec->private_data;
868
869 switch (freq) {
870 case 11289600:
871 case 12000000:
872 case 12288000:
873 case 16934400:
874 case 18432000:
875 if (clk_id == WM8753_MCLK) {
876 wm8753->sysclk = freq;
877 return 0;
878 } else if (clk_id == WM8753_PCMCLK) {
879 wm8753->pcmclk = freq;
880 return 0;
881 }
882 break;
883 }
884 return -EINVAL;
885}
886
887/*
888 * Set's ADC and Voice DAC format.
889 */
890static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
891 unsigned int fmt)
892{
893 struct snd_soc_codec *codec = codec_dai->codec;
894 u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01ec;
895
896 /* interface format */
897 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
898 case SND_SOC_DAIFMT_I2S:
899 voice |= 0x0002;
900 break;
901 case SND_SOC_DAIFMT_RIGHT_J:
902 break;
903 case SND_SOC_DAIFMT_LEFT_J:
904 voice |= 0x0001;
905 break;
906 case SND_SOC_DAIFMT_DSP_A:
907 voice |= 0x0003;
908 break;
909 case SND_SOC_DAIFMT_DSP_B:
910 voice |= 0x0013;
911 break;
912 default:
913 return -EINVAL;
914 }
915
916 wm8753_write(codec, WM8753_PCM, voice);
917 return 0;
918}
919
920/*
921 * Set PCM DAI bit size and sample rate.
922 */
923static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
924 struct snd_pcm_hw_params *params)
925{
926 struct snd_soc_pcm_runtime *rtd = substream->private_data;
927 struct snd_soc_device *socdev = rtd->socdev;
928 struct snd_soc_codec *codec = socdev->codec;
929 struct wm8753_priv *wm8753 = codec->private_data;
930 u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01f3;
931 u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f;
932
933 /* bit size */
934 switch (params_format(params)) {
935 case SNDRV_PCM_FORMAT_S16_LE:
936 break;
937 case SNDRV_PCM_FORMAT_S20_3LE:
938 voice |= 0x0004;
939 break;
940 case SNDRV_PCM_FORMAT_S24_LE:
941 voice |= 0x0008;
942 break;
943 case SNDRV_PCM_FORMAT_S32_LE:
944 voice |= 0x000c;
945 break;
946 }
947
948 /* sample rate */
949 if (params_rate(params) * 384 == wm8753->pcmclk)
950 srate |= 0x80;
951 wm8753_write(codec, WM8753_SRATE1, srate);
952
953 wm8753_write(codec, WM8753_PCM, voice);
954 return 0;
955}
956
957/*
958 * Set's PCM dai fmt and BCLK.
959 */
960static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
961 unsigned int fmt)
962{
963 struct snd_soc_codec *codec = codec_dai->codec;
964 u16 voice, ioctl;
965
966 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x011f;
967 ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x015d;
968
969 /* set master/slave audio interface */
970 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
971 case SND_SOC_DAIFMT_CBS_CFS:
972 break;
973 case SND_SOC_DAIFMT_CBM_CFM:
974 ioctl |= 0x2;
975 case SND_SOC_DAIFMT_CBM_CFS:
976 voice |= 0x0040;
977 break;
978 default:
979 return -EINVAL;
980 }
981
982 /* clock inversion */
983 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
984 case SND_SOC_DAIFMT_DSP_A:
985 case SND_SOC_DAIFMT_DSP_B:
986 /* frame inversion not valid for DSP modes */
987 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
988 case SND_SOC_DAIFMT_NB_NF:
989 break;
990 case SND_SOC_DAIFMT_IB_NF:
991 voice |= 0x0080;
992 break;
993 default:
994 return -EINVAL;
995 }
996 break;
997 case SND_SOC_DAIFMT_I2S:
998 case SND_SOC_DAIFMT_RIGHT_J:
999 case SND_SOC_DAIFMT_LEFT_J:
1000 voice &= ~0x0010;
1001 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1002 case SND_SOC_DAIFMT_NB_NF:
1003 break;
1004 case SND_SOC_DAIFMT_IB_IF:
1005 voice |= 0x0090;
1006 break;
1007 case SND_SOC_DAIFMT_IB_NF:
1008 voice |= 0x0080;
1009 break;
1010 case SND_SOC_DAIFMT_NB_IF:
1011 voice |= 0x0010;
1012 break;
1013 default:
1014 return -EINVAL;
1015 }
1016 break;
1017 default:
1018 return -EINVAL;
1019 }
1020
1021 wm8753_write(codec, WM8753_PCM, voice);
1022 wm8753_write(codec, WM8753_IOCTL, ioctl);
1023 return 0;
1024}
1025
1026static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
1027 int div_id, int div)
1028{
1029 struct snd_soc_codec *codec = codec_dai->codec;
1030 u16 reg;
1031
1032 switch (div_id) {
1033 case WM8753_PCMDIV:
1034 reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x003f;
1035 wm8753_write(codec, WM8753_CLOCK, reg | div);
1036 break;
1037 case WM8753_BCLKDIV:
1038 reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x01c7;
1039 wm8753_write(codec, WM8753_SRATE2, reg | div);
1040 break;
1041 case WM8753_VXCLKDIV:
1042 reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x003f;
1043 wm8753_write(codec, WM8753_SRATE2, reg | div);
1044 break;
1045 default:
1046 return -EINVAL;
1047 }
1048 return 0;
1049}
1050
1051/*
1052 * Set's HiFi DAC format.
1053 */
1054static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
1055 unsigned int fmt)
1056{
1057 struct snd_soc_codec *codec = codec_dai->codec;
1058 u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01e0;
1059
1060 /* interface format */
1061 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1062 case SND_SOC_DAIFMT_I2S:
1063 hifi |= 0x0002;
1064 break;
1065 case SND_SOC_DAIFMT_RIGHT_J:
1066 break;
1067 case SND_SOC_DAIFMT_LEFT_J:
1068 hifi |= 0x0001;
1069 break;
1070 case SND_SOC_DAIFMT_DSP_A:
1071 hifi |= 0x0003;
1072 break;
1073 case SND_SOC_DAIFMT_DSP_B:
1074 hifi |= 0x0013;
1075 break;
1076 default:
1077 return -EINVAL;
1078 }
1079
1080 wm8753_write(codec, WM8753_HIFI, hifi);
1081 return 0;
1082}
1083
1084/*
1085 * Set's I2S DAI format.
1086 */
1087static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
1088 unsigned int fmt)
1089{
1090 struct snd_soc_codec *codec = codec_dai->codec;
1091 u16 ioctl, hifi;
1092
1093 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x011f;
1094 ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x00ae;
1095
1096 /* set master/slave audio interface */
1097 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
1098 case SND_SOC_DAIFMT_CBS_CFS:
1099 break;
1100 case SND_SOC_DAIFMT_CBM_CFM:
1101 ioctl |= 0x1;
1102 case SND_SOC_DAIFMT_CBM_CFS:
1103 hifi |= 0x0040;
1104 break;
1105 default:
1106 return -EINVAL;
1107 }
1108
1109 /* clock inversion */
1110 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1111 case SND_SOC_DAIFMT_DSP_A:
1112 case SND_SOC_DAIFMT_DSP_B:
1113 /* frame inversion not valid for DSP modes */
1114 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1115 case SND_SOC_DAIFMT_NB_NF:
1116 break;
1117 case SND_SOC_DAIFMT_IB_NF:
1118 hifi |= 0x0080;
1119 break;
1120 default:
1121 return -EINVAL;
1122 }
1123 break;
1124 case SND_SOC_DAIFMT_I2S:
1125 case SND_SOC_DAIFMT_RIGHT_J:
1126 case SND_SOC_DAIFMT_LEFT_J:
1127 hifi &= ~0x0010;
1128 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1129 case SND_SOC_DAIFMT_NB_NF:
1130 break;
1131 case SND_SOC_DAIFMT_IB_IF:
1132 hifi |= 0x0090;
1133 break;
1134 case SND_SOC_DAIFMT_IB_NF:
1135 hifi |= 0x0080;
1136 break;
1137 case SND_SOC_DAIFMT_NB_IF:
1138 hifi |= 0x0010;
1139 break;
1140 default:
1141 return -EINVAL;
1142 }
1143 break;
1144 default:
1145 return -EINVAL;
1146 }
1147
1148 wm8753_write(codec, WM8753_HIFI, hifi);
1149 wm8753_write(codec, WM8753_IOCTL, ioctl);
1150 return 0;
1151}
1152
1153/*
1154 * Set PCM DAI bit size and sample rate.
1155 */
1156static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
1157 struct snd_pcm_hw_params *params)
1158{
1159 struct snd_soc_pcm_runtime *rtd = substream->private_data;
1160 struct snd_soc_device *socdev = rtd->socdev;
1161 struct snd_soc_codec *codec = socdev->codec;
1162 struct wm8753_priv *wm8753 = codec->private_data;
1163 u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0;
1164 u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01f3;
1165 int coeff;
1166
1167 /* is digital filter coefficient valid ? */
1168 coeff = get_coeff(wm8753->sysclk, params_rate(params));
1169 if (coeff < 0) {
1170 printk(KERN_ERR "wm8753 invalid MCLK or rate\n");
1171 return coeff;
1172 }
1173 wm8753_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) |
1174 coeff_div[coeff].usb);
1175
1176 /* bit size */
1177 switch (params_format(params)) {
1178 case SNDRV_PCM_FORMAT_S16_LE:
1179 break;
1180 case SNDRV_PCM_FORMAT_S20_3LE:
1181 hifi |= 0x0004;
1182 break;
1183 case SNDRV_PCM_FORMAT_S24_LE:
1184 hifi |= 0x0008;
1185 break;
1186 case SNDRV_PCM_FORMAT_S32_LE:
1187 hifi |= 0x000c;
1188 break;
1189 }
1190
1191 wm8753_write(codec, WM8753_HIFI, hifi);
1192 return 0;
1193}
1194
1195static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
1196 unsigned int fmt)
1197{
1198 struct snd_soc_codec *codec = codec_dai->codec;
1199 u16 clock;
1200
1201 /* set clk source as pcmclk */
1202 clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
1203 wm8753_write(codec, WM8753_CLOCK, clock);
1204
1205 if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
1206 return -EINVAL;
1207 return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
1208}
1209
1210static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
1211 unsigned int fmt)
1212{
1213 if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
1214 return -EINVAL;
1215 return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
1216}
1217
1218static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
1219 unsigned int fmt)
1220{
1221 struct snd_soc_codec *codec = codec_dai->codec;
1222 u16 clock;
1223
1224 /* set clk source as pcmclk */
1225 clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
1226 wm8753_write(codec, WM8753_CLOCK, clock);
1227
1228 if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
1229 return -EINVAL;
1230 return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
1231}
1232
1233static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
1234 unsigned int fmt)
1235{
1236 struct snd_soc_codec *codec = codec_dai->codec;
1237 u16 clock;
1238
1239 /* set clk source as mclk */
1240 clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
1241 wm8753_write(codec, WM8753_CLOCK, clock | 0x4);
1242
1243 if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
1244 return -EINVAL;
1245 if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
1246 return -EINVAL;
1247 return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
1248}
1249
1250static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
1251{
1252 struct snd_soc_codec *codec = dai->codec;
1253 u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7;
1254
1255 /* the digital mute covers the HiFi and Voice DAC's on the WM8753.
1256 * make sure we check if they are not both active when we mute */
1257 if (mute && dai->id == 1) {
1258 if (!wm8753_dai[WM8753_DAI_VOICE].playback.active ||
1259 !wm8753_dai[WM8753_DAI_HIFI].playback.active)
1260 wm8753_write(codec, WM8753_DAC, mute_reg | 0x8);
1261 } else {
1262 if (mute)
1263 wm8753_write(codec, WM8753_DAC, mute_reg | 0x8);
1264 else
1265 wm8753_write(codec, WM8753_DAC, mute_reg);
1266 }
1267
1268 return 0;
1269}
1270
1271static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
1272{
1273 u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e;
1274
1275 switch (event) {
1276 case SNDRV_CTL_POWER_D0: /* full On */
1277 /* set vmid to 50k and unmute dac */
1278 wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
1279 break;
1280 case SNDRV_CTL_POWER_D1: /* partial On */
1281 case SNDRV_CTL_POWER_D2: /* partial On */
1282 /* set vmid to 5k for quick power up */
1283 wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
1284 break;
1285 case SNDRV_CTL_POWER_D3hot: /* Off, with power */
1286 /* mute dac and set vmid to 500k, enable VREF */
1287 wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
1288 break;
1289 case SNDRV_CTL_POWER_D3cold: /* Off, without power */
1290 wm8753_write(codec, WM8753_PWR1, 0x0001);
1291 break;
1292 }
1293 codec->dapm_state = event;
1294 return 0;
1295}
1296
1297#define WM8753_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
1298 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
1299 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
1300
1301#define WM8753_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
1302 SNDRV_PCM_FMTBIT_S24_LE)
1303
1304/*
1305 * The WM8753 supports upto 4 different and mutually exclusive DAI
1306 * configurations. This gives 2 PCM's available for use, hifi and voice.
1307 * NOTE: The Voice PCM cannot play or capture audio to the CPU as it's DAI
1308 * is connected between the wm8753 and a BT codec or GSM modem.
1309 *
1310 * 1. Voice over PCM DAI - HIFI DAC over HIFI DAI
1311 * 2. Voice over HIFI DAI - HIFI disabled
1312 * 3. Voice disabled - HIFI over HIFI
1313 * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
1314 */
1315static const struct snd_soc_codec_dai wm8753_all_dai[] = {
1316/* DAI HiFi mode 1 */
1317{ .name = "WM8753 HiFi",
1318 .id = 1,
1319 .playback = {
1320 .stream_name = "HiFi Playback",
1321 .channels_min = 1,
1322 .channels_max = 2,
1323 .rates = WM8753_RATES,
1324 .formats = WM8753_FORMATS,},
1325 .capture = { /* dummy for fast DAI switching */
1326 .stream_name = "Capture",
1327 .channels_min = 1,
1328 .channels_max = 2,
1329 .rates = WM8753_RATES,
1330 .formats = WM8753_FORMATS,},
1331 .ops = {
1332 .hw_params = wm8753_i2s_hw_params,},
1333 .dai_ops = {
1334 .digital_mute = wm8753_mute,
1335 .set_fmt = wm8753_mode1h_set_dai_fmt,
1336 .set_clkdiv = wm8753_set_dai_clkdiv,
1337 .set_pll = wm8753_set_dai_pll,
1338 .set_sysclk = wm8753_set_dai_sysclk,
1339 },
1340},
1341/* DAI Voice mode 1 */
1342{ .name = "WM8753 Voice",
1343 .id = 1,
1344 .playback = {
1345 .stream_name = "Voice Playback",
1346 .channels_min = 1,
1347 .channels_max = 1,
1348 .rates = WM8753_RATES,
1349 .formats = WM8753_FORMATS,},
1350 .capture = {
1351 .stream_name = "Capture",
1352 .channels_min = 1,
1353 .channels_max = 2,
1354 .rates = WM8753_RATES,
1355 .formats = WM8753_FORMATS,},
1356 .ops = {
1357 .hw_params = wm8753_pcm_hw_params,},
1358 .dai_ops = {
1359 .digital_mute = wm8753_mute,
1360 .set_fmt = wm8753_mode1v_set_dai_fmt,
1361 .set_clkdiv = wm8753_set_dai_clkdiv,
1362 .set_pll = wm8753_set_dai_pll,
1363 .set_sysclk = wm8753_set_dai_sysclk,
1364 },
1365},
1366/* DAI HiFi mode 2 - dummy */
1367{ .name = "WM8753 HiFi",
1368 .id = 2,
1369},
1370/* DAI Voice mode 2 */
1371{ .name = "WM8753 Voice",
1372 .id = 2,
1373 .playback = {
1374 .stream_name = "Voice Playback",
1375 .channels_min = 1,
1376 .channels_max = 1,
1377 .rates = WM8753_RATES,
1378 .formats = WM8753_FORMATS,},
1379 .capture = {
1380 .stream_name = "Capture",
1381 .channels_min = 1,
1382 .channels_max = 2,
1383 .rates = WM8753_RATES,
1384 .formats = WM8753_FORMATS,},
1385 .ops = {
1386 .hw_params = wm8753_pcm_hw_params,},
1387 .dai_ops = {
1388 .digital_mute = wm8753_mute,
1389 .set_fmt = wm8753_mode2_set_dai_fmt,
1390 .set_clkdiv = wm8753_set_dai_clkdiv,
1391 .set_pll = wm8753_set_dai_pll,
1392 .set_sysclk = wm8753_set_dai_sysclk,
1393 },
1394},
1395/* DAI HiFi mode 3 */
1396{ .name = "WM8753 HiFi",
1397 .id = 3,
1398 .playback = {
1399 .stream_name = "HiFi Playback",
1400 .channels_min = 1,
1401 .channels_max = 2,
1402 .rates = WM8753_RATES,
1403 .formats = WM8753_FORMATS,},
1404 .capture = {
1405 .stream_name = "Capture",
1406 .channels_min = 1,
1407 .channels_max = 2,
1408 .rates = WM8753_RATES,
1409 .formats = WM8753_FORMATS,},
1410 .ops = {
1411 .hw_params = wm8753_i2s_hw_params,},
1412 .dai_ops = {
1413 .digital_mute = wm8753_mute,
1414 .set_fmt = wm8753_mode3_4_set_dai_fmt,
1415 .set_clkdiv = wm8753_set_dai_clkdiv,
1416 .set_pll = wm8753_set_dai_pll,
1417 .set_sysclk = wm8753_set_dai_sysclk,
1418 },
1419},
1420/* DAI Voice mode 3 - dummy */
1421{ .name = "WM8753 Voice",
1422 .id = 3,
1423},
1424/* DAI HiFi mode 4 */
1425{ .name = "WM8753 HiFi",
1426 .id = 4,
1427 .playback = {
1428 .stream_name = "HiFi Playback",
1429 .channels_min = 1,
1430 .channels_max = 2,
1431 .rates = WM8753_RATES,
1432 .formats = WM8753_FORMATS,},
1433 .capture = {
1434 .stream_name = "Capture",
1435 .channels_min = 1,
1436 .channels_max = 2,
1437 .rates = WM8753_RATES,
1438 .formats = WM8753_FORMATS,},
1439 .ops = {
1440 .hw_params = wm8753_i2s_hw_params,},
1441 .dai_ops = {
1442 .digital_mute = wm8753_mute,
1443 .set_fmt = wm8753_mode3_4_set_dai_fmt,
1444 .set_clkdiv = wm8753_set_dai_clkdiv,
1445 .set_pll = wm8753_set_dai_pll,
1446 .set_sysclk = wm8753_set_dai_sysclk,
1447 },
1448},
1449/* DAI Voice mode 4 - dummy */
1450{ .name = "WM8753 Voice",
1451 .id = 4,
1452},
1453};
1454
1455struct snd_soc_codec_dai wm8753_dai[2];
1456EXPORT_SYMBOL_GPL(wm8753_dai);
1457
1458static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
1459{
1460 if (mode < 4) {
1461 int playback_active, capture_active, codec_active, pop_wait;
1462 void *private_data;
1463
1464 playback_active = wm8753_dai[0].playback.active;
1465 capture_active = wm8753_dai[0].capture.active;
1466 codec_active = wm8753_dai[0].active;
1467 private_data = wm8753_dai[0].private_data;
1468 pop_wait = wm8753_dai[0].pop_wait;
1469 wm8753_dai[0] = wm8753_all_dai[mode << 1];
1470 wm8753_dai[0].playback.active = playback_active;
1471 wm8753_dai[0].capture.active = capture_active;
1472 wm8753_dai[0].active = codec_active;
1473 wm8753_dai[0].private_data = private_data;
1474 wm8753_dai[0].pop_wait = pop_wait;
1475
1476 playback_active = wm8753_dai[1].playback.active;
1477 capture_active = wm8753_dai[1].capture.active;
1478 codec_active = wm8753_dai[1].active;
1479 private_data = wm8753_dai[1].private_data;
1480 pop_wait = wm8753_dai[1].pop_wait;
1481 wm8753_dai[1] = wm8753_all_dai[(mode << 1) + 1];
1482 wm8753_dai[1].playback.active = playback_active;
1483 wm8753_dai[1].capture.active = capture_active;
1484 wm8753_dai[1].active = codec_active;
1485 wm8753_dai[1].private_data = private_data;
1486 wm8753_dai[1].pop_wait = pop_wait;
1487 }
1488 wm8753_dai[0].codec = codec;
1489 wm8753_dai[1].codec = codec;
1490}
1491
1492static void wm8753_work(struct work_struct *work)
1493{
1494 struct snd_soc_codec *codec =
1495 container_of(work, struct snd_soc_codec, delayed_work.work);
1496 wm8753_dapm_event(codec, codec->dapm_state);
1497}
1498
1499static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
1500{
1501 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1502 struct snd_soc_codec *codec = socdev->codec;
1503
1504 /* we only need to suspend if we are a valid card */
1505 if(!codec->card)
1506 return 0;
1507
1508 wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
1509 return 0;
1510}
1511
1512static int wm8753_resume(struct platform_device *pdev)
1513{
1514 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1515 struct snd_soc_codec *codec = socdev->codec;
1516 int i;
1517 u8 data[2];
1518 u16 *cache = codec->reg_cache;
1519
1520 /* we only need to resume if we are a valid card */
1521 if(!codec->card)
1522 return 0;
1523
1524 /* Sync reg_cache with the hardware */
1525 for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
1526 if (i + 1 == WM8753_RESET)
1527 continue;
1528 data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
1529 data[1] = cache[i] & 0x00ff;
1530 codec->hw_write(codec->control_data, data, 2);
1531 }
1532
1533 wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
1534
1535 /* charge wm8753 caps */
1536 if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
1537 wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
1538 codec->dapm_state = SNDRV_CTL_POWER_D0;
1539 schedule_delayed_work(&codec->delayed_work,
1540 msecs_to_jiffies(caps_charge));
1541 }
1542
1543 return 0;
1544}
1545
1546/*
1547 * initialise the WM8753 driver
1548 * register the mixer and dsp interfaces with the kernel
1549 */
1550static int wm8753_init(struct snd_soc_device *socdev)
1551{
1552 struct snd_soc_codec *codec = socdev->codec;
1553 int reg, ret = 0;
1554
1555 codec->name = "WM8753";
1556 codec->owner = THIS_MODULE;
1557 codec->read = wm8753_read_reg_cache;
1558 codec->write = wm8753_write;
1559 codec->dapm_event = wm8753_dapm_event;
1560 codec->dai = wm8753_dai;
1561 codec->num_dai = 2;
1562 codec->reg_cache_size = sizeof(wm8753_reg);
1563 codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL);
1564
1565 if (codec->reg_cache == NULL)
1566 return -ENOMEM;
1567
1568 wm8753_set_dai_mode(codec, 0);
1569
1570 wm8753_reset(codec);
1571
1572 /* register pcms */
1573 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
1574 if (ret < 0) {
1575 printk(KERN_ERR "wm8753: failed to create pcms\n");
1576 goto pcm_err;
1577 }
1578
1579 /* charge output caps */
1580 wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
1581 codec->dapm_state = SNDRV_CTL_POWER_D3hot;
1582 schedule_delayed_work(&codec->delayed_work,
1583 msecs_to_jiffies(caps_charge));
1584
1585 /* set the update bits */
1586 reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
1587 wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
1588 reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
1589 wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
1590 reg = wm8753_read_reg_cache(codec, WM8753_LADC);
1591 wm8753_write(codec, WM8753_LADC, reg | 0x0100);
1592 reg = wm8753_read_reg_cache(codec, WM8753_RADC);
1593 wm8753_write(codec, WM8753_RADC, reg | 0x0100);
1594 reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
1595 wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
1596 reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
1597 wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
1598 reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
1599 wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
1600 reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
1601 wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
1602 reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
1603 wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
1604 reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
1605 wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
1606
1607 wm8753_add_controls(codec);
1608 wm8753_add_widgets(codec);
1609 ret = snd_soc_register_card(socdev);
1610 if (ret < 0) {
1611 printk(KERN_ERR "wm8753: failed to register card\n");
1612 goto card_err;
1613 }
1614 return ret;
1615
1616card_err:
1617 snd_soc_free_pcms(socdev);
1618 snd_soc_dapm_free(socdev);
1619pcm_err:
1620 kfree(codec->reg_cache);
1621 return ret;
1622}
1623
1624/* If the i2c layer weren't so broken, we could pass this kind of data
1625 around */
1626static struct snd_soc_device *wm8753_socdev;
1627
1628#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
1629
1630/*
1631 * WM8753 2 wire address is determined by GPIO5
1632 * state during powerup.
1633 * low = 0x1a
1634 * high = 0x1b
1635 */
1636static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
1637
1638/* Magic definition of all other variables and things */
1639I2C_CLIENT_INSMOD;
1640
1641static struct i2c_driver wm8753_i2c_driver;
1642static struct i2c_client client_template;
1643
1644static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind)
1645{
1646 struct snd_soc_device *socdev = wm8753_socdev;
1647 struct wm8753_setup_data *setup = socdev->codec_data;
1648 struct snd_soc_codec *codec = socdev->codec;
1649 struct i2c_client *i2c;
1650 int ret;
1651
1652 if (addr != setup->i2c_address)
1653 return -ENODEV;
1654
1655 client_template.adapter = adap;
1656 client_template.addr = addr;
1657
1658 i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
1659 if (i2c == NULL){
1660 kfree(codec);
1661 return -ENOMEM;
1662 }
1663 i2c_set_clientdata(i2c, codec);
1664 codec->control_data = i2c;
1665
1666 ret = i2c_attach_client(i2c);
1667 if (ret < 0) {
1668 err("failed to attach codec at addr %x\n", addr);
1669 goto err;
1670 }
1671
1672 ret = wm8753_init(socdev);
1673 if (ret < 0) {
1674 err("failed to initialise WM8753\n");
1675 goto err;
1676 }
1677
1678 return ret;
1679
1680err:
1681 kfree(codec);
1682 kfree(i2c);
1683 return ret;
1684}
1685
1686static int wm8753_i2c_detach(struct i2c_client *client)
1687{
1688 struct snd_soc_codec *codec = i2c_get_clientdata(client);
1689 i2c_detach_client(client);
1690 kfree(codec->reg_cache);
1691 kfree(client);
1692 return 0;
1693}
1694
1695static int wm8753_i2c_attach(struct i2c_adapter *adap)
1696{
1697 return i2c_probe(adap, &addr_data, wm8753_codec_probe);
1698}
1699
1700/* corgi i2c codec control layer */
1701static struct i2c_driver wm8753_i2c_driver = {
1702 .driver = {
1703 .name = "WM8753 I2C Codec",
1704 .owner = THIS_MODULE,
1705 },
1706 .id = I2C_DRIVERID_WM8753,
1707 .attach_adapter = wm8753_i2c_attach,
1708 .detach_client = wm8753_i2c_detach,
1709 .command = NULL,
1710};
1711
1712static struct i2c_client client_template = {
1713 .name = "WM8753",
1714 .driver = &wm8753_i2c_driver,
1715};
1716#endif
1717
1718static int wm8753_probe(struct platform_device *pdev)
1719{
1720 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1721 struct wm8753_setup_data *setup;
1722 struct snd_soc_codec *codec;
1723 struct wm8753_priv *wm8753;
1724 int ret = 0;
1725
1726 info("WM8753 Audio Codec %s", WM8753_VERSION);
1727
1728 setup = socdev->codec_data;
1729 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
1730 if (codec == NULL)
1731 return -ENOMEM;
1732
1733 wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
1734 if (wm8753 == NULL) {
1735 kfree(codec);
1736 return -ENOMEM;
1737 }
1738
1739 codec->private_data = wm8753;
1740 socdev->codec = codec;
1741 mutex_init(&codec->mutex);
1742 INIT_LIST_HEAD(&codec->dapm_widgets);
1743 INIT_LIST_HEAD(&codec->dapm_paths);
1744 wm8753_socdev = socdev;
1745 INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
1746
1747#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
1748 if (setup->i2c_address) {
1749 normal_i2c[0] = setup->i2c_address;
1750 codec->hw_write = (hw_write_t)i2c_master_send;
1751 ret = i2c_add_driver(&wm8753_i2c_driver);
1752 if (ret != 0)
1753 printk(KERN_ERR "can't add i2c driver");
1754 }
1755#else
1756 /* Add other interfaces here */
1757#endif
1758 return ret;
1759}
1760
1761/*
1762 * This function forces any delayed work to be queued and run.
1763 */
1764static int run_delayed_work(struct delayed_work *dwork)
1765{
1766 int ret;
1767
1768 /* cancel any work waiting to be queued. */
1769 ret = cancel_delayed_work(dwork);
1770
1771 /* if there was any work waiting then we run it now and
1772 * wait for it's completion */
1773 if (ret) {
1774 schedule_delayed_work(dwork, 0);
1775 flush_scheduled_work();
1776 }
1777 return ret;
1778}
1779
1780/* power down chip */
1781static int wm8753_remove(struct platform_device *pdev)
1782{
1783 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1784 struct snd_soc_codec *codec = socdev->codec;
1785
1786 if (codec->control_data)
1787 wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
1788 run_delayed_work(&codec->delayed_work);
1789 snd_soc_free_pcms(socdev);
1790 snd_soc_dapm_free(socdev);
1791#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
1792 i2c_del_driver(&wm8753_i2c_driver);
1793#endif
1794 kfree(codec->private_data);
1795 kfree(codec);
1796
1797 return 0;
1798}
1799
1800struct snd_soc_codec_device soc_codec_dev_wm8753 = {
1801 .probe = wm8753_probe,
1802 .remove = wm8753_remove,
1803 .suspend = wm8753_suspend,
1804 .resume = wm8753_resume,
1805};
1806
1807EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
1808
1809MODULE_DESCRIPTION("ASoC WM8753 driver");
1810MODULE_AUTHOR("Liam Girdwood");
1811MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
new file mode 100644
index 000000000000..95e2a1f53169
--- /dev/null
+++ b/sound/soc/codecs/wm8753.h
@@ -0,0 +1,126 @@
1/*
2 * wm8753.h -- audio driver for WM8753
3 *
4 * Copyright 2003 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood
6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#ifndef _WM8753_H
16#define _WM8753_H
17
18/* WM8753 register space */
19
20#define WM8753_DAC 0x01
21#define WM8753_ADC 0x02
22#define WM8753_PCM 0x03
23#define WM8753_HIFI 0x04
24#define WM8753_IOCTL 0x05
25#define WM8753_SRATE1 0x06
26#define WM8753_SRATE2 0x07
27#define WM8753_LDAC 0x08
28#define WM8753_RDAC 0x09
29#define WM8753_BASS 0x0a
30#define WM8753_TREBLE 0x0b
31#define WM8753_ALC1 0x0c
32#define WM8753_ALC2 0x0d
33#define WM8753_ALC3 0x0e
34#define WM8753_NGATE 0x0f
35#define WM8753_LADC 0x10
36#define WM8753_RADC 0x11
37#define WM8753_ADCTL1 0x12
38#define WM8753_3D 0x13
39#define WM8753_PWR1 0x14
40#define WM8753_PWR2 0x15
41#define WM8753_PWR3 0x16
42#define WM8753_PWR4 0x17
43#define WM8753_ID 0x18
44#define WM8753_INTPOL 0x19
45#define WM8753_INTEN 0x1a
46#define WM8753_GPIO1 0x1b
47#define WM8753_GPIO2 0x1c
48#define WM8753_RESET 0x1f
49#define WM8753_RECMIX1 0x20
50#define WM8753_RECMIX2 0x21
51#define WM8753_LOUTM1 0x22
52#define WM8753_LOUTM2 0x23
53#define WM8753_ROUTM1 0x24
54#define WM8753_ROUTM2 0x25
55#define WM8753_MOUTM1 0x26
56#define WM8753_MOUTM2 0x27
57#define WM8753_LOUT1V 0x28
58#define WM8753_ROUT1V 0x29
59#define WM8753_LOUT2V 0x2a
60#define WM8753_ROUT2V 0x2b
61#define WM8753_MOUTV 0x2c
62#define WM8753_OUTCTL 0x2d
63#define WM8753_ADCIN 0x2e
64#define WM8753_INCTL1 0x2f
65#define WM8753_INCTL2 0x30
66#define WM8753_LINVOL 0x31
67#define WM8753_RINVOL 0x32
68#define WM8753_MICBIAS 0x33
69#define WM8753_CLOCK 0x34
70#define WM8753_PLL1CTL1 0x35
71#define WM8753_PLL1CTL2 0x36
72#define WM8753_PLL1CTL3 0x37
73#define WM8753_PLL1CTL4 0x38
74#define WM8753_PLL2CTL1 0x39
75#define WM8753_PLL2CTL2 0x3a
76#define WM8753_PLL2CTL3 0x3b
77#define WM8753_PLL2CTL4 0x3c
78#define WM8753_BIASCTL 0x3d
79#define WM8753_ADCTL2 0x3f
80
81struct wm8753_setup_data {
82 unsigned short i2c_address;
83};
84
85#define WM8753_PLL1 0
86#define WM8753_PLL2 1
87
88/* clock inputs */
89#define WM8753_MCLK 0
90#define WM8753_PCMCLK 1
91
92/* clock divider id's */
93#define WM8753_PCMDIV 0
94#define WM8753_BCLKDIV 1
95#define WM8753_VXCLKDIV 2
96
97/* PCM clock dividers */
98#define WM8753_PCM_DIV_1 (0 << 6)
99#define WM8753_PCM_DIV_3 (2 << 6)
100#define WM8753_PCM_DIV_5_5 (3 << 6)
101#define WM8753_PCM_DIV_2 (4 << 6)
102#define WM8753_PCM_DIV_4 (5 << 6)
103#define WM8753_PCM_DIV_6 (6 << 6)
104#define WM8753_PCM_DIV_8 (7 << 6)
105
106/* BCLK clock dividers */
107#define WM8753_BCLK_DIV_1 (0 << 3)
108#define WM8753_BCLK_DIV_2 (1 << 3)
109#define WM8753_BCLK_DIV_4 (2 << 3)
110#define WM8753_BCLK_DIV_8 (3 << 3)
111#define WM8753_BCLK_DIV_16 (4 << 3)
112
113/* VXCLK clock dividers */
114#define WM8753_VXCLK_DIV_1 (0 << 6)
115#define WM8753_VXCLK_DIV_2 (1 << 6)
116#define WM8753_VXCLK_DIV_4 (2 << 6)
117#define WM8753_VXCLK_DIV_8 (3 << 6)
118#define WM8753_VXCLK_DIV_16 (4 << 6)
119
120#define WM8753_DAI_HIFI 0
121#define WM8753_DAI_VOICE 1
122
123extern struct snd_soc_codec_dai wm8753_dai[2];
124extern struct snd_soc_codec_device soc_codec_dev_wm8753;
125
126#endif
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index ee7a691a9ba1..264413a00cac 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -676,14 +676,13 @@ static int wm9712_soc_probe(struct platform_device *pdev)
676 codec = socdev->codec; 676 codec = socdev->codec;
677 mutex_init(&codec->mutex); 677 mutex_init(&codec->mutex);
678 678
679 codec->reg_cache = 679 codec->reg_cache = kmemdup(wm9712_reg, sizeof(wm9712_reg), GFP_KERNEL);
680 kzalloc(sizeof(u16) * ARRAY_SIZE(wm9712_reg), GFP_KERNEL); 680
681 if (codec->reg_cache == NULL) { 681 if (codec->reg_cache == NULL) {
682 ret = -ENOMEM; 682 ret = -ENOMEM;
683 goto cache_err; 683 goto cache_err;
684 } 684 }
685 memcpy(codec->reg_cache, wm9712_reg, sizeof(u16) * ARRAY_SIZE(wm9712_reg)); 685 codec->reg_cache_size = sizeof(wm9712_reg);
686 codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9712_reg);
687 codec->reg_cache_step = 2; 686 codec->reg_cache_step = 2;
688 687
689 codec->name = "WM9712"; 688 codec->name = "WM9712";
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index b9ab3b8e1d3e..a83e22937c27 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,5 +1,3 @@
1menu "SoC Audio for the Intel PXA2xx"
2
3config SND_PXA2XX_SOC 1config SND_PXA2XX_SOC
4 tristate "SoC Audio for the Intel PXA2xx chip" 2 tristate "SoC Audio for the Intel PXA2xx chip"
5 depends on ARCH_PXA && SND_SOC 3 depends on ARCH_PXA && SND_SOC
@@ -55,5 +53,3 @@ config SND_PXA2XX_SOC_TOSA
55 help 53 help
56 Say Y if you want to add support for SoC audio on Sharp 54 Say Y if you want to add support for SoC audio on Sharp
57 Zaurus SL-C6000x models (Tosa). 55 Zaurus SL-C6000x models (Tosa).
58
59endmenu
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
new file mode 100644
index 000000000000..044a3712077a
--- /dev/null
+++ b/sound/soc/s3c24xx/Kconfig
@@ -0,0 +1,10 @@
1config SND_S3C24XX_SOC
2 tristate "SoC Audio for the Samsung S3C24XX chips"
3 depends on ARCH_S3C2410 && SND_SOC
4 help
5 Say Y or M if you want to add support for codecs attached to
6 the S3C24XX AC97, I2S or SSP interface. You will also need
7 to select the audio interfaces to support below.
8
9config SND_S3C24XX_SOC_I2S
10 tristate
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
new file mode 100644
index 000000000000..6f0fffcb30f5
--- /dev/null
+++ b/sound/soc/s3c24xx/Makefile
@@ -0,0 +1,6 @@
1# S3c24XX Platform Support
2snd-soc-s3c24xx-objs := s3c24xx-pcm.o
3snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
4
5obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
6obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
new file mode 100644
index 000000000000..8ca314dc8891
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -0,0 +1,441 @@
1/*
2 * s3c24xx-i2s.c -- ALSA Soc Audio Layer
3 *
4 * (c) 2006 Wolfson Microelectronics PLC.
5 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
6 *
7 * (c) 2004-2005 Simtec Electronics
8 * http://armlinux.simtec.co.uk/
9 * Ben Dooks <ben@simtec.co.uk>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 *
16 *
17 * Revision history
18 * 11th Dec 2006 Merged with Simtec driver
19 * 10th Nov 2006 Initial version.
20 */
21
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/device.h>
25#include <linux/delay.h>
26#include <linux/clk.h>
27#include <sound/driver.h>
28#include <sound/core.h>
29#include <sound/pcm.h>
30#include <sound/pcm_params.h>
31#include <sound/initval.h>
32#include <sound/soc.h>
33
34#include <asm/hardware.h>
35#include <asm/io.h>
36#include <asm/arch/regs-iis.h>
37#include <asm/arch/regs-gpio.h>
38#include <asm/arch/regs-clock.h>
39#include <asm/arch/audio.h>
40#include <asm/dma.h>
41#include <asm/arch/dma.h>
42
43#include "s3c24xx-pcm.h"
44#include "s3c24xx-i2s.h"
45
46#define S3C24XX_I2S_DEBUG 0
47#if S3C24XX_I2S_DEBUG
48#define DBG(x...) printk(KERN_DEBUG x)
49#else
50#define DBG(x...)
51#endif
52
53static struct s3c2410_dma_client s3c24xx_dma_client_out = {
54 .name = "I2S PCM Stereo out"
55};
56
57static struct s3c2410_dma_client s3c24xx_dma_client_in = {
58 .name = "I2S PCM Stereo in"
59};
60
61static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = {
62 .client = &s3c24xx_dma_client_out,
63 .channel = DMACH_I2S_OUT,
64 .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO,
65 .dma_size = 2,
66};
67
68static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {
69 .client = &s3c24xx_dma_client_in,
70 .channel = DMACH_I2S_IN,
71 .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO,
72 .dma_size = 2,
73};
74
75struct s3c24xx_i2s_info {
76 void __iomem *regs;
77 struct clk *iis_clk;
78};
79static struct s3c24xx_i2s_info s3c24xx_i2s;
80
81static void s3c24xx_snd_txctrl(int on)
82{
83 u32 iisfcon;
84 u32 iiscon;
85 u32 iismod;
86
87 DBG("Entered %s\n", __FUNCTION__);
88
89 iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
90 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
91 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
92
93 DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
94
95 if (on) {
96 iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
97 iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
98 iiscon &= ~S3C2410_IISCON_TXIDLE;
99 iismod |= S3C2410_IISMOD_TXMODE;
100
101 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
102 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
103 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
104 } else {
105 /* note, we have to disable the FIFOs otherwise bad things
106 * seem to happen when the DMA stops. According to the
107 * Samsung supplied kernel, this should allow the DMA
108 * engine and FIFOs to reset. If this isn't allowed, the
109 * DMA engine will simply freeze randomly.
110 */
111
112 iisfcon &= ~S3C2410_IISFCON_TXENABLE;
113 iisfcon &= ~S3C2410_IISFCON_TXDMA;
114 iiscon |= S3C2410_IISCON_TXIDLE;
115 iiscon &= ~S3C2410_IISCON_TXDMAEN;
116 iismod &= ~S3C2410_IISMOD_TXMODE;
117
118 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
119 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
120 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
121 }
122
123 DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
124}
125
126static void s3c24xx_snd_rxctrl(int on)
127{
128 u32 iisfcon;
129 u32 iiscon;
130 u32 iismod;
131
132 DBG("Entered %s\n", __FUNCTION__);
133
134 iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
135 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
136 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
137
138 DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
139
140 if (on) {
141 iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
142 iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
143 iiscon &= ~S3C2410_IISCON_RXIDLE;
144 iismod |= S3C2410_IISMOD_RXMODE;
145
146 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
147 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
148 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
149 } else {
150 /* note, we have to disable the FIFOs otherwise bad things
151 * seem to happen when the DMA stops. According to the
152 * Samsung supplied kernel, this should allow the DMA
153 * engine and FIFOs to reset. If this isn't allowed, the
154 * DMA engine will simply freeze randomly.
155 */
156
157 iisfcon &= ~S3C2410_IISFCON_RXENABLE;
158 iisfcon &= ~S3C2410_IISFCON_RXDMA;
159 iiscon |= S3C2410_IISCON_RXIDLE;
160 iiscon &= ~S3C2410_IISCON_RXDMAEN;
161 iismod &= ~S3C2410_IISMOD_RXMODE;
162
163 writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
164 writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
165 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
166 }
167
168 DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
169}
170
171/*
172 * Wait for the LR signal to allow synchronisation to the L/R clock
173 * from the codec. May only be needed for slave mode.
174 */
175static int s3c24xx_snd_lrsync(void)
176{
177 u32 iiscon;
178 unsigned long timeout = jiffies + msecs_to_jiffies(5);
179
180 DBG("Entered %s\n", __FUNCTION__);
181
182 while (1) {
183 iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
184 if (iiscon & S3C2410_IISCON_LRINDEX)
185 break;
186
187 if (timeout < jiffies)
188 return -ETIMEDOUT;
189 }
190
191 return 0;
192}
193
194/*
195 * Check whether CPU is the master or slave
196 */
197static inline int s3c24xx_snd_is_clkmaster(void)
198{
199 DBG("Entered %s\n", __FUNCTION__);
200
201 return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
202}
203
204/*
205 * Set S3C24xx I2S DAI format
206 */
207static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
208 unsigned int fmt)
209{
210 u32 iismod;
211
212 DBG("Entered %s\n", __FUNCTION__);
213
214 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
215 DBG("hw_params r: IISMOD: %lx \n", iismod);
216
217 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
218 case SND_SOC_DAIFMT_CBM_CFM:
219 iismod |= S3C2410_IISMOD_SLAVE;
220 break;
221 case SND_SOC_DAIFMT_CBS_CFS:
222 break;
223 default:
224 return -EINVAL;
225 }
226
227 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
228 case SND_SOC_DAIFMT_LEFT_J:
229 iismod |= S3C2410_IISMOD_MSB;
230 break;
231 case SND_SOC_DAIFMT_I2S:
232 break;
233 default:
234 return -EINVAL;
235 }
236
237 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
238 DBG("hw_params w: IISMOD: %lx \n", iismod);
239 return 0;
240}
241
242static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
243 struct snd_pcm_hw_params *params)
244{
245 struct snd_soc_pcm_runtime *rtd = substream->private_data;
246 u32 iismod;
247
248 DBG("Entered %s\n", __FUNCTION__);
249
250 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
251 rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;
252 else
253 rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_in;
254
255 /* Working copies of register */
256 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
257 DBG("hw_params r: IISMOD: %lx\n", iismod);
258
259 switch (params_format(params)) {
260 case SNDRV_PCM_FORMAT_S8:
261 break;
262 case SNDRV_PCM_FORMAT_S16_LE:
263 iismod |= S3C2410_IISMOD_16BIT;
264 break;
265 }
266
267 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
268 DBG("hw_params w: IISMOD: %lx\n", iismod);
269 return 0;
270}
271
272static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
273{
274 int ret = 0;
275
276 DBG("Entered %s\n", __FUNCTION__);
277
278 switch (cmd) {
279 case SNDRV_PCM_TRIGGER_START:
280 case SNDRV_PCM_TRIGGER_RESUME:
281 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
282 if (!s3c24xx_snd_is_clkmaster()) {
283 ret = s3c24xx_snd_lrsync();
284 if (ret)
285 goto exit_err;
286 }
287
288 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
289 s3c24xx_snd_rxctrl(1);
290 else
291 s3c24xx_snd_txctrl(1);
292 break;
293 case SNDRV_PCM_TRIGGER_STOP:
294 case SNDRV_PCM_TRIGGER_SUSPEND:
295 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
296 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
297 s3c24xx_snd_rxctrl(0);
298 else
299 s3c24xx_snd_txctrl(0);
300 break;
301 default:
302 ret = -EINVAL;
303 break;
304 }
305
306exit_err:
307 return ret;
308}
309
310/*
311 * Set S3C24xx Clock source
312 */
313static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
314 int clk_id, unsigned int freq, int dir)
315{
316 u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
317
318 DBG("Entered %s\n", __FUNCTION__);
319
320 iismod &= ~S3C2440_IISMOD_MPLL;
321
322 switch (clk_id) {
323 case S3C24XX_CLKSRC_PCLK:
324 break;
325 case S3C24XX_CLKSRC_MPLL:
326 iismod |= S3C2440_IISMOD_MPLL;
327 break;
328 default:
329 return -EINVAL;
330 }
331
332 writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
333 return 0;
334}
335
336/*
337 * Set S3C24xx Clock dividers
338 */
339static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
340 int div_id, int div)
341{
342 u32 reg;
343
344 DBG("Entered %s\n", __FUNCTION__);
345
346 switch (div_id) {
347 case S3C24XX_DIV_MCLK:
348 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
349 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
350 break;
351 case S3C24XX_DIV_BCLK:
352 reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
353 writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
354 break;
355 case S3C24XX_DIV_PRESCALER:
356 writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
357 reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
358 writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
359 break;
360 default:
361 return -EINVAL;
362 }
363
364 return 0;
365}
366
367/*
368 * To avoid duplicating clock code, allow machine driver to
369 * get the clockrate from here.
370 */
371u32 s3c24xx_i2s_get_clockrate(void)
372{
373 return clk_get_rate(s3c24xx_i2s.iis_clk);
374}
375EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
376
377static int s3c24xx_i2s_probe(struct platform_device *pdev)
378{
379 DBG("Entered %s\n", __FUNCTION__);
380
381 s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
382 if (s3c24xx_i2s.regs == NULL)
383 return -ENXIO;
384
385 s3c24xx_i2s.iis_clk=clk_get(&pdev->dev, "iis");
386 if (s3c24xx_i2s.iis_clk == NULL) {
387 DBG("failed to get iis_clock\n");
388 return -ENODEV;
389 }
390 clk_enable(s3c24xx_i2s.iis_clk);
391
392 /* Configure the I2S pins in correct mode */
393 s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
394 s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
395 s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
396 s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
397 s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
398
399 writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
400
401 s3c24xx_snd_txctrl(0);
402 s3c24xx_snd_rxctrl(0);
403
404 return 0;
405}
406
407#define S3C24XX_I2S_RATES \
408 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
409 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
410 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
411
412struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
413 .name = "s3c24xx-i2s",
414 .id = 0,
415 .type = SND_SOC_DAI_I2S,
416 .probe = s3c24xx_i2s_probe,
417 .playback = {
418 .channels_min = 2,
419 .channels_max = 2,
420 .rates = S3C24XX_I2S_RATES,
421 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
422 .capture = {
423 .channels_min = 2,
424 .channels_max = 2,
425 .rates = S3C24XX_I2S_RATES,
426 .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
427 .ops = {
428 .trigger = s3c24xx_i2s_trigger,
429 .hw_params = s3c24xx_i2s_hw_params,},
430 .dai_ops = {
431 .set_fmt = s3c24xx_i2s_set_fmt,
432 .set_clkdiv = s3c24xx_i2s_set_clkdiv,
433 .set_sysclk = s3c24xx_i2s_set_sysclk,
434 },
435};
436EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);
437
438/* Module information */
439MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
440MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
441MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.h b/sound/soc/s3c24xx/s3c24xx-i2s.h
new file mode 100644
index 000000000000..537b4ecce8a3
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.h
@@ -0,0 +1,37 @@
1/*
2 * s3c24xx-i2s.c -- ALSA Soc Audio Layer
3 *
4 * Copyright 2005 Wolfson Microelectronics PLC.
5 * Author: Graeme Gregory
6 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * Revision history
14 * 10th Nov 2006 Initial version.
15 */
16
17#ifndef S3C24XXI2S_H_
18#define S3C24XXI2S_H_
19
20/* clock sources */
21#define S3C24XX_CLKSRC_PCLK 0
22#define S3C24XX_CLKSRC_MPLL 1
23
24/* Clock dividers */
25#define S3C24XX_DIV_MCLK 0
26#define S3C24XX_DIV_BCLK 1
27#define S3C24XX_DIV_PRESCALER 2
28
29/* prescaler */
30#define S3C24XX_PRESCALE(a,b) \
31 (((a - 1) << S3C2410_IISPSR_INTSHIFT) | ((b - 1) << S3C2410_IISPSR_EXTSHFIT))
32
33u32 s3c24xx_i2s_get_clockrate(void);
34
35extern struct snd_soc_cpu_dai s3c24xx_i2s_dai;
36
37#endif /*S3C24XXI2S_H_*/
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
new file mode 100644
index 000000000000..21dc6974d6a3
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -0,0 +1,468 @@
1/*
2 * s3c24xx-pcm.c -- ALSA Soc Audio Layer
3 *
4 * (c) 2006 Wolfson Microelectronics PLC.
5 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
6 *
7 * (c) 2004-2005 Simtec Electronics
8 * http://armlinux.simtec.co.uk/
9 * Ben Dooks <ben@simtec.co.uk>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 *
16 * Revision history
17 * 11th Dec 2006 Merged with Simtec driver
18 * 10th Nov 2006 Initial version.
19 */
20
21#include <linux/module.h>
22#include <linux/init.h>
23#include <linux/platform_device.h>
24#include <linux/slab.h>
25#include <linux/dma-mapping.h>
26
27#include <sound/driver.h>
28#include <sound/core.h>
29#include <sound/pcm.h>
30#include <sound/pcm_params.h>
31#include <sound/soc.h>
32
33#include <asm/dma.h>
34#include <asm/io.h>
35#include <asm/hardware.h>
36#include <asm/arch/dma.h>
37#include <asm/arch/audio.h>
38
39#include "s3c24xx-pcm.h"
40
41#define S3C24XX_PCM_DEBUG 0
42#if S3C24XX_PCM_DEBUG
43#define DBG(x...) printk(KERN_DEBUG x)
44#else
45#define DBG(x...)
46#endif
47
48static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
49 .info = SNDRV_PCM_INFO_INTERLEAVED |
50 SNDRV_PCM_INFO_BLOCK_TRANSFER |
51 SNDRV_PCM_INFO_MMAP |
52 SNDRV_PCM_INFO_MMAP_VALID,
53 .formats = SNDRV_PCM_FMTBIT_S16_LE |
54 SNDRV_PCM_FMTBIT_U16_LE |
55 SNDRV_PCM_FMTBIT_U8 |
56 SNDRV_PCM_FMTBIT_S8,
57 .channels_min = 2,
58 .channels_max = 2,
59 .buffer_bytes_max = 128*1024,
60 .period_bytes_min = PAGE_SIZE,
61 .period_bytes_max = PAGE_SIZE*2,
62 .periods_min = 2,
63 .periods_max = 128,
64 .fifo_size = 32,
65};
66
67struct s3c24xx_runtime_data {
68 spinlock_t lock;
69 int state;
70 unsigned int dma_loaded;
71 unsigned int dma_limit;
72 unsigned int dma_period;
73 dma_addr_t dma_start;
74 dma_addr_t dma_pos;
75 dma_addr_t dma_end;
76 struct s3c24xx_pcm_dma_params *params;
77};
78
79/* s3c24xx_pcm_enqueue
80 *
81 * place a dma buffer onto the queue for the dma system
82 * to handle.
83*/
84static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream)
85{
86 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
87 dma_addr_t pos = prtd->dma_pos;
88 int ret;
89
90 DBG("Entered %s\n", __FUNCTION__);
91
92 while (prtd->dma_loaded < prtd->dma_limit) {
93 unsigned long len = prtd->dma_period;
94
95 DBG("dma_loaded: %d\n",prtd->dma_loaded);
96
97 if ((pos + len) > prtd->dma_end) {
98 len = prtd->dma_end - pos;
99 DBG(KERN_DEBUG "%s: corrected dma len %ld\n",
100 __FUNCTION__, len);
101 }
102
103 ret = s3c2410_dma_enqueue(prtd->params->channel,
104 substream, pos, len);
105
106 if (ret == 0) {
107 prtd->dma_loaded++;
108 pos += prtd->dma_period;
109 if (pos >= prtd->dma_end)
110 pos = prtd->dma_start;
111 } else
112 break;
113 }
114
115 prtd->dma_pos = pos;
116}
117
118static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel,
119 void *dev_id, int size,
120 enum s3c2410_dma_buffresult result)
121{
122 struct snd_pcm_substream *substream = dev_id;
123 struct s3c24xx_runtime_data *prtd;
124
125 DBG("Entered %s\n", __FUNCTION__);
126
127 if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
128 return;
129
130 prtd = substream->runtime->private_data;
131
132 if (substream)
133 snd_pcm_period_elapsed(substream);
134
135 spin_lock(&prtd->lock);
136 if (prtd->state & ST_RUNNING) {
137 prtd->dma_loaded--;
138 s3c24xx_pcm_enqueue(substream);
139 }
140
141 spin_unlock(&prtd->lock);
142}
143
144static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
145 struct snd_pcm_hw_params *params)
146{
147 struct snd_pcm_runtime *runtime = substream->runtime;
148 struct s3c24xx_runtime_data *prtd = runtime->private_data;
149 struct snd_soc_pcm_runtime *rtd = substream->private_data;
150 struct s3c24xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
151 unsigned long totbytes = params_buffer_bytes(params);
152 int ret=0;
153
154 DBG("Entered %s\n", __FUNCTION__);
155
156 /* return if this is a bufferless transfer e.g.
157 * codec <--> BT codec or GSM modem -- lg FIXME */
158 if (!dma)
159 return 0;
160
161 /* prepare DMA */
162 prtd->params = dma;
163
164 DBG("params %p, client %p, channel %d\n", prtd->params,
165 prtd->params->client, prtd->params->channel);
166
167 ret = s3c2410_dma_request(prtd->params->channel,
168 prtd->params->client, NULL);
169
170 if (ret) {
171 DBG(KERN_ERR "failed to get dma channel\n");
172 return ret;
173 }
174
175 /* channel needs configuring for mem=>device, increment memory addr,
176 * sync to pclk, half-word transfers to the IIS-FIFO. */
177 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
178 s3c2410_dma_devconfig(prtd->params->channel,
179 S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |
180 S3C2410_DISRCC_APB, prtd->params->dma_addr);
181
182 s3c2410_dma_config(prtd->params->channel,
183 prtd->params->dma_size,
184 S3C2410_DCON_SYNC_PCLK |
185 S3C2410_DCON_HANDSHAKE);
186 } else {
187 s3c2410_dma_config(prtd->params->channel,
188 prtd->params->dma_size,
189 S3C2410_DCON_HANDSHAKE |
190 S3C2410_DCON_SYNC_PCLK);
191
192 s3c2410_dma_devconfig(prtd->params->channel,
193 S3C2410_DMASRC_HW, 0x3,
194 prtd->params->dma_addr);
195 }
196
197 s3c2410_dma_set_buffdone_fn(prtd->params->channel,
198 s3c24xx_audio_buffdone);
199
200 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
201
202 runtime->dma_bytes = totbytes;
203
204 spin_lock_irq(&prtd->lock);
205 prtd->dma_loaded = 0;
206 prtd->dma_limit = runtime->hw.periods_min;
207 prtd->dma_period = params_period_bytes(params);
208 prtd->dma_start = runtime->dma_addr;
209 prtd->dma_pos = prtd->dma_start;
210 prtd->dma_end = prtd->dma_start + totbytes;
211 spin_unlock_irq(&prtd->lock);
212
213 return 0;
214}
215
216static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream)
217{
218 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
219
220 DBG("Entered %s\n", __FUNCTION__);
221
222 /* TODO - do we need to ensure DMA flushed */
223 snd_pcm_set_runtime_buffer(substream, NULL);
224
225 if (prtd->params) {
226 s3c2410_dma_free(prtd->params->channel, prtd->params->client);
227 prtd->params = NULL;
228 }
229
230 return 0;
231}
232
233static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
234{
235 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
236 int ret = 0;
237
238 DBG("Entered %s\n", __FUNCTION__);
239
240 /* return if this is a bufferless transfer e.g.
241 * codec <--> BT codec or GSM modem -- lg FIXME */
242 if (!prtd->params)
243 return 0;
244
245 /* flush the DMA channel */
246 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
247 prtd->dma_loaded = 0;
248 prtd->dma_pos = prtd->dma_start;
249
250 /* enqueue dma buffers */
251 s3c24xx_pcm_enqueue(substream);
252
253 return ret;
254}
255
256static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
257{
258 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
259 int ret = 0;
260
261 DBG("Entered %s\n", __FUNCTION__);
262
263 spin_lock(&prtd->lock);
264
265 switch (cmd) {
266 case SNDRV_PCM_TRIGGER_START:
267 case SNDRV_PCM_TRIGGER_RESUME:
268 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
269 prtd->state |= ST_RUNNING;
270 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
271 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STARTED);
272 break;
273
274 case SNDRV_PCM_TRIGGER_STOP:
275 case SNDRV_PCM_TRIGGER_SUSPEND:
276 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
277 prtd->state &= ~ST_RUNNING;
278 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);
279 break;
280
281 default:
282 ret = -EINVAL;
283 break;
284 }
285
286 spin_unlock(&prtd->lock);
287
288 return ret;
289}
290
291static snd_pcm_uframes_t
292 s3c24xx_pcm_pointer(struct snd_pcm_substream *substream)
293{
294 struct snd_pcm_runtime *runtime = substream->runtime;
295 struct s3c24xx_runtime_data *prtd = runtime->private_data;
296 unsigned long res;
297 dma_addr_t src, dst;
298
299 DBG("Entered %s\n", __FUNCTION__);
300
301 spin_lock(&prtd->lock);
302 s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
303
304 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
305 res = dst - prtd->dma_start;
306 else
307 res = src - prtd->dma_start;
308
309 spin_unlock(&prtd->lock);
310
311 DBG("Pointer %x %x\n",src,dst);
312
313 /* we seem to be getting the odd error from the pcm library due
314 * to out-of-bounds pointers. this is maybe due to the dma engine
315 * not having loaded the new values for the channel before being
316 * callled... (todo - fix )
317 */
318
319 if (res >= snd_pcm_lib_buffer_bytes(substream)) {
320 if (res == snd_pcm_lib_buffer_bytes(substream))
321 res = 0;
322 }
323
324 return bytes_to_frames(substream->runtime, res);
325}
326
327static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
328{
329 struct snd_pcm_runtime *runtime = substream->runtime;
330 struct s3c24xx_runtime_data *prtd;
331
332 DBG("Entered %s\n", __FUNCTION__);
333
334 snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
335
336 prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL);
337 if (prtd == NULL)
338 return -ENOMEM;
339
340 runtime->private_data = prtd;
341 return 0;
342}
343
344static int s3c24xx_pcm_close(struct snd_pcm_substream *substream)
345{
346 struct snd_pcm_runtime *runtime = substream->runtime;
347 struct s3c24xx_runtime_data *prtd = runtime->private_data;
348
349 DBG("Entered %s\n", __FUNCTION__);
350
351 if (prtd)
352 kfree(prtd);
353 else
354 DBG("s3c24xx_pcm_close called with prtd == NULL\n");
355
356 return 0;
357}
358
359static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream,
360 struct vm_area_struct *vma)
361{
362 struct snd_pcm_runtime *runtime = substream->runtime;
363
364 DBG("Entered %s\n", __FUNCTION__);
365
366 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
367 runtime->dma_area,
368 runtime->dma_addr,
369 runtime->dma_bytes);
370}
371
372static struct snd_pcm_ops s3c24xx_pcm_ops = {
373 .open = s3c24xx_pcm_open,
374 .close = s3c24xx_pcm_close,
375 .ioctl = snd_pcm_lib_ioctl,
376 .hw_params = s3c24xx_pcm_hw_params,
377 .hw_free = s3c24xx_pcm_hw_free,
378 .prepare = s3c24xx_pcm_prepare,
379 .trigger = s3c24xx_pcm_trigger,
380 .pointer = s3c24xx_pcm_pointer,
381 .mmap = s3c24xx_pcm_mmap,
382};
383
384static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
385{
386 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
387 struct snd_dma_buffer *buf = &substream->dma_buffer;
388 size_t size = s3c24xx_pcm_hardware.buffer_bytes_max;
389
390 DBG("Entered %s\n", __FUNCTION__);
391
392 buf->dev.type = SNDRV_DMA_TYPE_DEV;
393 buf->dev.dev = pcm->card->dev;
394 buf->private_data = NULL;
395 buf->area = dma_alloc_writecombine(pcm->card->dev, size,
396 &buf->addr, GFP_KERNEL);
397 if (!buf->area)
398 return -ENOMEM;
399 buf->bytes = size;
400 return 0;
401}
402
403static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
404{
405 struct snd_pcm_substream *substream;
406 struct snd_dma_buffer *buf;
407 int stream;
408
409 DBG("Entered %s\n", __FUNCTION__);
410
411 for (stream = 0; stream < 2; stream++) {
412 substream = pcm->streams[stream].substream;
413 if (!substream)
414 continue;
415
416 buf = &substream->dma_buffer;
417 if (!buf->area)
418 continue;
419
420 dma_free_writecombine(pcm->card->dev, buf->bytes,
421 buf->area, buf->addr);
422 buf->area = NULL;
423 }
424}
425
426static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK;
427
428static int s3c24xx_pcm_new(struct snd_card *card,
429 struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
430{
431 int ret = 0;
432
433 DBG("Entered %s\n", __FUNCTION__);
434
435 if (!card->dev->dma_mask)
436 card->dev->dma_mask = &s3c24xx_pcm_dmamask;
437 if (!card->dev->coherent_dma_mask)
438 card->dev->coherent_dma_mask = 0xffffffff;
439
440 if (dai->playback.channels_min) {
441 ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
442 SNDRV_PCM_STREAM_PLAYBACK);
443 if (ret)
444 goto out;
445 }
446
447 if (dai->capture.channels_min) {
448 ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
449 SNDRV_PCM_STREAM_CAPTURE);
450 if (ret)
451 goto out;
452 }
453 out:
454 return ret;
455}
456
457struct snd_soc_platform s3c24xx_soc_platform = {
458 .name = "s3c24xx-audio",
459 .pcm_ops = &s3c24xx_pcm_ops,
460 .pcm_new = s3c24xx_pcm_new,
461 .pcm_free = s3c24xx_pcm_free_dma_buffers,
462};
463
464EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
465
466MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
467MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");
468MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.h b/sound/soc/s3c24xx/s3c24xx-pcm.h
new file mode 100644
index 000000000000..0088c79822ea
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.h
@@ -0,0 +1,31 @@
1/*
2 * s3c24xx-pcm.h --
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * ALSA PCM interface for the Samsung S3C24xx CPU
10 */
11
12#ifndef _S3C24XX_PCM_H
13#define _S3C24XX_PCM_H
14
15#define ST_RUNNING (1<<0)
16#define ST_OPENED (1<<1)
17
18struct s3c24xx_pcm_dma_params {
19 struct s3c2410_dma_client *client; /* stream identifier */
20 int channel; /* Channel ID */
21 dma_addr_t dma_addr;
22 int dma_size; /* Size of the DMA transfer */
23};
24
25#define S3C24XX_DAI_I2S 0
26
27/* platform data */
28extern struct snd_soc_platform s3c24xx_soc_platform;
29extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
30
31#endif
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7caf8c7b0ac5..96bce55572a0 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -882,13 +882,15 @@ int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
882 if (wsink->id == snd_soc_dapm_input) { 882 if (wsink->id == snd_soc_dapm_input) {
883 if (wsource->id == snd_soc_dapm_micbias || 883 if (wsource->id == snd_soc_dapm_micbias ||
884 wsource->id == snd_soc_dapm_mic || 884 wsource->id == snd_soc_dapm_mic ||
885 wsink->id == snd_soc_dapm_line) 885 wsink->id == snd_soc_dapm_line ||
886 wsink->id == snd_soc_dapm_output)
886 wsink->ext = 1; 887 wsink->ext = 1;
887 } 888 }
888 if (wsource->id == snd_soc_dapm_output) { 889 if (wsource->id == snd_soc_dapm_output) {
889 if (wsink->id == snd_soc_dapm_spk || 890 if (wsink->id == snd_soc_dapm_spk ||
890 wsink->id == snd_soc_dapm_hp || 891 wsink->id == snd_soc_dapm_hp ||
891 wsink->id == snd_soc_dapm_line) 892 wsink->id == snd_soc_dapm_line ||
893 wsink->id == snd_soc_dapm_input)
892 wsource->ext = 1; 894 wsource->ext = 1;
893 } 895 }
894 896