aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/blackfin/Kconfig2
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c4
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c4
-rw-r--r--sound/soc/codecs/Kconfig8
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/tlv320aic23.c2
-rw-r--r--sound/soc/codecs/twl4030.c258
-rw-r--r--sound/soc/codecs/twl4030.h18
-rw-r--r--sound/soc/codecs/wm8903.c1
-rw-r--r--sound/soc/codecs/wm8960.c969
-rw-r--r--sound/soc/codecs/wm8960.h127
-rw-r--r--sound/soc/codecs/wm8988.c1097
-rw-r--r--sound/soc/codecs/wm8988.h60
-rw-r--r--sound/soc/codecs/wm9713.c40
-rw-r--r--sound/soc/omap/n810.c7
-rw-r--r--sound/soc/omap/omap-mcbsp.c38
-rw-r--r--sound/soc/omap/omap-pcm.c2
-rw-r--r--sound/soc/omap/omap2evm.c2
-rw-r--r--sound/soc/omap/omap3beagle.c2
-rw-r--r--sound/soc/omap/omap3pandora.c4
-rw-r--r--sound/soc/omap/overo.c2
-rw-r--r--sound/soc/omap/sdp3430.c2
-rw-r--r--sound/soc/pxa/corgi.c2
-rw-r--r--sound/soc/pxa/em-x270.c2
-rw-r--r--sound/soc/pxa/poodle.c2
-rw-r--r--sound/soc/pxa/pxa-ssp.c13
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c2
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c2
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c4
-rw-r--r--sound/soc/pxa/spitz.c2
-rw-r--r--sound/soc/pxa/tosa.c2
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c2
-rw-r--r--sound/soc/s6000/Kconfig19
-rw-r--r--sound/soc/s6000/Makefile11
-rw-r--r--sound/soc/s6000/s6000-i2s.c629
-rw-r--r--sound/soc/s6000/s6000-i2s.h25
-rw-r--r--sound/soc/s6000/s6000-pcm.c497
-rw-r--r--sound/soc/s6000/s6000-pcm.h35
-rw-r--r--sound/soc/s6000/s6105-ipcam.c244
-rw-r--r--sound/soc/soc-core.c58
-rw-r--r--sound/soc/soc-dapm.c131
43 files changed, 4182 insertions, 155 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3d2bb6fc6dcc..3304f9dd92fa 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -32,6 +32,7 @@ source "sound/soc/fsl/Kconfig"
32source "sound/soc/omap/Kconfig" 32source "sound/soc/omap/Kconfig"
33source "sound/soc/pxa/Kconfig" 33source "sound/soc/pxa/Kconfig"
34source "sound/soc/s3c24xx/Kconfig" 34source "sound/soc/s3c24xx/Kconfig"
35source "sound/soc/s6000/Kconfig"
35source "sound/soc/sh/Kconfig" 36source "sound/soc/sh/Kconfig"
36 37
37# Supported codecs 38# Supported codecs
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 0237879fd412..8943a140c818 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -10,4 +10,5 @@ obj-$(CONFIG_SND_SOC) += fsl/
10obj-$(CONFIG_SND_SOC) += omap/ 10obj-$(CONFIG_SND_SOC) += omap/
11obj-$(CONFIG_SND_SOC) += pxa/ 11obj-$(CONFIG_SND_SOC) += pxa/
12obj-$(CONFIG_SND_SOC) += s3c24xx/ 12obj-$(CONFIG_SND_SOC) += s3c24xx/
13obj-$(CONFIG_SND_SOC) += s6000/
13obj-$(CONFIG_SND_SOC) += sh/ 14obj-$(CONFIG_SND_SOC) += sh/
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 0a2f8f9eff53..811596f4c092 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -42,7 +42,7 @@ config SND_BF5XX_AC97
42 You will also need to select the audio interfaces to support below. 42 You will also need to select the audio interfaces to support below.
43 43
44 Note: 44 Note:
45 AC97 codecs which do not implment the slot-16 mode will not function 45 AC97 codecs which do not implement the slot-16 mode will not function
46 properly with this driver. This driver is known to work with the 46 properly with this driver. This driver is known to work with the
47 Analog Devices line of AC97 codecs. 47 Analog Devices line of AC97 codecs.
48 48
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 8cfed1a5dcbe..cf0dfb7ca221 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -413,7 +413,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
413 sport_done(sport_handle); 413 sport_done(sport_handle);
414} 414}
415 415
416static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; 416static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
417 417
418int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai, 418int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
419 struct snd_pcm *pcm) 419 struct snd_pcm *pcm)
@@ -424,7 +424,7 @@ int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
424 if (!card->dev->dma_mask) 424 if (!card->dev->dma_mask)
425 card->dev->dma_mask = &bf5xx_pcm_dmamask; 425 card->dev->dma_mask = &bf5xx_pcm_dmamask;
426 if (!card->dev->coherent_dma_mask) 426 if (!card->dev->coherent_dma_mask)
427 card->dev->coherent_dma_mask = DMA_32BIT_MASK; 427 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
428 428
429 if (dai->playback.channels_min) { 429 if (dai->playback.channels_min) {
430 ret = bf5xx_pcm_preallocate_dma_buffer(pcm, 430 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 1318c4f627b7..62fbb8459569 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -245,7 +245,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
245 sport_done(sport_handle); 245 sport_done(sport_handle);
246} 246}
247 247
248static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; 248static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
249 249
250int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai, 250int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
251 struct snd_pcm *pcm) 251 struct snd_pcm *pcm)
@@ -256,7 +256,7 @@ int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
256 if (!card->dev->dma_mask) 256 if (!card->dev->dma_mask)
257 card->dev->dma_mask = &bf5xx_pcm_dmamask; 257 card->dev->dma_mask = &bf5xx_pcm_dmamask;
258 if (!card->dev->coherent_dma_mask) 258 if (!card->dev->coherent_dma_mask)
259 card->dev->coherent_dma_mask = DMA_32BIT_MASK; 259 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
260 260
261 if (dai->playback.channels_min) { 261 if (dai->playback.channels_min) {
262 ret = bf5xx_pcm_preallocate_dma_buffer(pcm, 262 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b6c7f7a01cb0..121d63f13dbb 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -35,7 +35,9 @@ config SND_SOC_ALL_CODECS
35 select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI 35 select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
36 select SND_SOC_WM8900 if I2C 36 select SND_SOC_WM8900 if I2C
37 select SND_SOC_WM8903 if I2C 37 select SND_SOC_WM8903 if I2C
38 select SND_SOC_WM8960 if I2C
38 select SND_SOC_WM8971 if I2C 39 select SND_SOC_WM8971 if I2C
40 select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
39 select SND_SOC_WM8990 if I2C 41 select SND_SOC_WM8990 if I2C
40 select SND_SOC_WM9705 if SND_SOC_AC97_BUS 42 select SND_SOC_WM9705 if SND_SOC_AC97_BUS
41 select SND_SOC_WM9712 if SND_SOC_AC97_BUS 43 select SND_SOC_WM9712 if SND_SOC_AC97_BUS
@@ -138,9 +140,15 @@ config SND_SOC_WM8900
138config SND_SOC_WM8903 140config SND_SOC_WM8903
139 tristate 141 tristate
140 142
143config SND_SOC_WM8960
144 tristate
145
141config SND_SOC_WM8971 146config SND_SOC_WM8971
142 tristate 147 tristate
143 148
149config SND_SOC_WM8988
150 tristate
151
144config SND_SOC_WM8990 152config SND_SOC_WM8990
145 tristate 153 tristate
146 154
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 030d2454725f..d8e15a47711e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -23,7 +23,9 @@ snd-soc-wm8750-objs := wm8750.o
23snd-soc-wm8753-objs := wm8753.o 23snd-soc-wm8753-objs := wm8753.o
24snd-soc-wm8900-objs := wm8900.o 24snd-soc-wm8900-objs := wm8900.o
25snd-soc-wm8903-objs := wm8903.o 25snd-soc-wm8903-objs := wm8903.o
26snd-soc-wm8960-objs := wm8960.o
26snd-soc-wm8971-objs := wm8971.o 27snd-soc-wm8971-objs := wm8971.o
28snd-soc-wm8988-objs := wm8988.o
27snd-soc-wm8990-objs := wm8990.o 29snd-soc-wm8990-objs := wm8990.o
28snd-soc-wm9705-objs := wm9705.o 30snd-soc-wm9705-objs := wm9705.o
29snd-soc-wm9712-objs := wm9712.o 31snd-soc-wm9712-objs := wm9712.o
@@ -55,6 +57,8 @@ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
55obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o 57obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
56obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o 58obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
57obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o 59obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
60obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
61obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
58obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o 62obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
59obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o 63obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
60obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o 64obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index c3f4afb5d017..21f69df9994c 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -523,6 +523,8 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
523 case SND_SOC_DAIFMT_I2S: 523 case SND_SOC_DAIFMT_I2S:
524 iface_reg |= TLV320AIC23_FOR_I2S; 524 iface_reg |= TLV320AIC23_FOR_I2S;
525 break; 525 break;
526 case SND_SOC_DAIFMT_DSP_A:
527 iface_reg |= TLV320AIC23_LRP_ON;
526 case SND_SOC_DAIFMT_DSP_B: 528 case SND_SOC_DAIFMT_DSP_B:
527 iface_reg |= TLV320AIC23_FOR_DSP; 529 iface_reg |= TLV320AIC23_FOR_DSP;
528 break; 530 break;
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 921b205de28a..cc2968cf6409 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -125,6 +125,11 @@ struct twl4030_priv {
125 125
126 struct snd_pcm_substream *master_substream; 126 struct snd_pcm_substream *master_substream;
127 struct snd_pcm_substream *slave_substream; 127 struct snd_pcm_substream *slave_substream;
128
129 unsigned int configured;
130 unsigned int rate;
131 unsigned int sample_bits;
132 unsigned int channels;
128}; 133};
129 134
130/* 135/*
@@ -1220,6 +1225,36 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
1220 return 0; 1225 return 0;
1221} 1226}
1222 1227
1228static void twl4030_constraints(struct twl4030_priv *twl4030,
1229 struct snd_pcm_substream *mst_substream)
1230{
1231 struct snd_pcm_substream *slv_substream;
1232
1233 /* Pick the stream, which need to be constrained */
1234 if (mst_substream == twl4030->master_substream)
1235 slv_substream = twl4030->slave_substream;
1236 else if (mst_substream == twl4030->slave_substream)
1237 slv_substream = twl4030->master_substream;
1238 else /* This should not happen.. */
1239 return;
1240
1241 /* Set the constraints according to the already configured stream */
1242 snd_pcm_hw_constraint_minmax(slv_substream->runtime,
1243 SNDRV_PCM_HW_PARAM_RATE,
1244 twl4030->rate,
1245 twl4030->rate);
1246
1247 snd_pcm_hw_constraint_minmax(slv_substream->runtime,
1248 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
1249 twl4030->sample_bits,
1250 twl4030->sample_bits);
1251
1252 snd_pcm_hw_constraint_minmax(slv_substream->runtime,
1253 SNDRV_PCM_HW_PARAM_CHANNELS,
1254 twl4030->channels,
1255 twl4030->channels);
1256}
1257
1223static int twl4030_startup(struct snd_pcm_substream *substream, 1258static int twl4030_startup(struct snd_pcm_substream *substream,
1224 struct snd_soc_dai *dai) 1259 struct snd_soc_dai *dai)
1225{ 1260{
@@ -1228,26 +1263,16 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
1228 struct snd_soc_codec *codec = socdev->card->codec; 1263 struct snd_soc_codec *codec = socdev->card->codec;
1229 struct twl4030_priv *twl4030 = codec->private_data; 1264 struct twl4030_priv *twl4030 = codec->private_data;
1230 1265
1231 /* If we already have a playback or capture going then constrain
1232 * this substream to match it.
1233 */
1234 if (twl4030->master_substream) { 1266 if (twl4030->master_substream) {
1235 struct snd_pcm_runtime *master_runtime;
1236 master_runtime = twl4030->master_substream->runtime;
1237
1238 snd_pcm_hw_constraint_minmax(substream->runtime,
1239 SNDRV_PCM_HW_PARAM_RATE,
1240 master_runtime->rate,
1241 master_runtime->rate);
1242
1243 snd_pcm_hw_constraint_minmax(substream->runtime,
1244 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
1245 master_runtime->sample_bits,
1246 master_runtime->sample_bits);
1247
1248 twl4030->slave_substream = substream; 1267 twl4030->slave_substream = substream;
1249 } else 1268 /* The DAI has one configuration for playback and capture, so
1269 * if the DAI has been already configured then constrain this
1270 * substream to match it. */
1271 if (twl4030->configured)
1272 twl4030_constraints(twl4030, twl4030->master_substream);
1273 } else {
1250 twl4030->master_substream = substream; 1274 twl4030->master_substream = substream;
1275 }
1251 1276
1252 return 0; 1277 return 0;
1253} 1278}
@@ -1264,6 +1289,13 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
1264 twl4030->master_substream = twl4030->slave_substream; 1289 twl4030->master_substream = twl4030->slave_substream;
1265 1290
1266 twl4030->slave_substream = NULL; 1291 twl4030->slave_substream = NULL;
1292
1293 /* If all streams are closed, or the remaining stream has not yet
1294 * been configured than set the DAI as not configured. */
1295 if (!twl4030->master_substream)
1296 twl4030->configured = 0;
1297 else if (!twl4030->master_substream->runtime->channels)
1298 twl4030->configured = 0;
1267} 1299}
1268 1300
1269static int twl4030_hw_params(struct snd_pcm_substream *substream, 1301static int twl4030_hw_params(struct snd_pcm_substream *substream,
@@ -1276,8 +1308,8 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
1276 struct twl4030_priv *twl4030 = codec->private_data; 1308 struct twl4030_priv *twl4030 = codec->private_data;
1277 u8 mode, old_mode, format, old_format; 1309 u8 mode, old_mode, format, old_format;
1278 1310
1279 if (substream == twl4030->slave_substream) 1311 if (twl4030->configured)
1280 /* Ignoring hw_params for slave substream */ 1312 /* Ignoring hw_params for already configured DAI */
1281 return 0; 1313 return 0;
1282 1314
1283 /* bit rate */ 1315 /* bit rate */
@@ -1357,6 +1389,21 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
1357 /* set CODECPDZ afterwards */ 1389 /* set CODECPDZ afterwards */
1358 twl4030_codec_enable(codec, 1); 1390 twl4030_codec_enable(codec, 1);
1359 } 1391 }
1392
1393 /* Store the important parameters for the DAI configuration and set
1394 * the DAI as configured */
1395 twl4030->configured = 1;
1396 twl4030->rate = params_rate(params);
1397 twl4030->sample_bits = hw_param_interval(params,
1398 SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
1399 twl4030->channels = params_channels(params);
1400
1401 /* If both playback and capture streams are open, and one of them
1402 * is setting the hw parameters right now (since we are here), set
1403 * constraints to the other stream to match the current one. */
1404 if (twl4030->slave_substream)
1405 twl4030_constraints(twl4030, substream);
1406
1360 return 0; 1407 return 0;
1361} 1408}
1362 1409
@@ -1437,6 +1484,144 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
1437 return 0; 1484 return 0;
1438} 1485}
1439 1486
1487static int twl4030_voice_startup(struct snd_pcm_substream *substream,
1488 struct snd_soc_dai *dai)
1489{
1490 struct snd_soc_pcm_runtime *rtd = substream->private_data;
1491 struct snd_soc_device *socdev = rtd->socdev;
1492 struct snd_soc_codec *codec = socdev->card->codec;
1493 u8 infreq;
1494 u8 mode;
1495
1496 /* If the system master clock is not 26MHz, the voice PCM interface is
1497 * not avilable.
1498 */
1499 infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL)
1500 & TWL4030_APLL_INFREQ;
1501
1502 if (infreq != TWL4030_APLL_INFREQ_26000KHZ) {
1503 printk(KERN_ERR "TWL4030 voice startup: "
1504 "MCLK is not 26MHz, call set_sysclk() on init\n");
1505 return -EINVAL;
1506 }
1507
1508 /* If the codec mode is not option2, the voice PCM interface is not
1509 * avilable.
1510 */
1511 mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
1512 & TWL4030_OPT_MODE;
1513
1514 if (mode != TWL4030_OPTION_2) {
1515 printk(KERN_ERR "TWL4030 voice startup: "
1516 "the codec mode is not option2\n");
1517 return -EINVAL;
1518 }
1519
1520 return 0;
1521}
1522
1523static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
1524 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
1525{
1526 struct snd_soc_pcm_runtime *rtd = substream->private_data;
1527 struct snd_soc_device *socdev = rtd->socdev;
1528 struct snd_soc_codec *codec = socdev->card->codec;
1529 u8 old_mode, mode;
1530
1531 /* bit rate */
1532 old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
1533 & ~(TWL4030_CODECPDZ);
1534 mode = old_mode;
1535
1536 switch (params_rate(params)) {
1537 case 8000:
1538 mode &= ~(TWL4030_SEL_16K);
1539 break;
1540 case 16000:
1541 mode |= TWL4030_SEL_16K;
1542 break;
1543 default:
1544 printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n",
1545 params_rate(params));
1546 return -EINVAL;
1547 }
1548
1549 if (mode != old_mode) {
1550 /* change rate and set CODECPDZ */
1551 twl4030_codec_enable(codec, 0);
1552 twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
1553 twl4030_codec_enable(codec, 1);
1554 }
1555
1556 return 0;
1557}
1558
1559static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
1560 int clk_id, unsigned int freq, int dir)
1561{
1562 struct snd_soc_codec *codec = codec_dai->codec;
1563 u8 infreq;
1564
1565 switch (freq) {
1566 case 26000000:
1567 infreq = TWL4030_APLL_INFREQ_26000KHZ;
1568 break;
1569 default:
1570 printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
1571 freq);
1572 return -EINVAL;
1573 }
1574
1575 infreq |= TWL4030_APLL_EN;
1576 twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
1577
1578 return 0;
1579}
1580
1581static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
1582 unsigned int fmt)
1583{
1584 struct snd_soc_codec *codec = codec_dai->codec;
1585 u8 old_format, format;
1586
1587 /* get format */
1588 old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
1589 format = old_format;
1590
1591 /* set master/slave audio interface */
1592 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
1593 case SND_SOC_DAIFMT_CBS_CFM:
1594 format &= ~(TWL4030_VIF_SLAVE_EN);
1595 break;
1596 case SND_SOC_DAIFMT_CBS_CFS:
1597 format |= TWL4030_VIF_SLAVE_EN;
1598 break;
1599 default:
1600 return -EINVAL;
1601 }
1602
1603 /* clock inversion */
1604 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1605 case SND_SOC_DAIFMT_IB_NF:
1606 format &= ~(TWL4030_VIF_FORMAT);
1607 break;
1608 case SND_SOC_DAIFMT_NB_IF:
1609 format |= TWL4030_VIF_FORMAT;
1610 break;
1611 default:
1612 return -EINVAL;
1613 }
1614
1615 if (format != old_format) {
1616 /* change format and set CODECPDZ */
1617 twl4030_codec_enable(codec, 0);
1618 twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
1619 twl4030_codec_enable(codec, 1);
1620 }
1621
1622 return 0;
1623}
1624
1440#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) 1625#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
1441#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) 1626#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
1442 1627
@@ -1448,7 +1633,15 @@ static struct snd_soc_dai_ops twl4030_dai_ops = {
1448 .set_fmt = twl4030_set_dai_fmt, 1633 .set_fmt = twl4030_set_dai_fmt,
1449}; 1634};
1450 1635
1451struct snd_soc_dai twl4030_dai = { 1636static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
1637 .startup = twl4030_voice_startup,
1638 .hw_params = twl4030_voice_hw_params,
1639 .set_sysclk = twl4030_voice_set_dai_sysclk,
1640 .set_fmt = twl4030_voice_set_dai_fmt,
1641};
1642
1643struct snd_soc_dai twl4030_dai[] = {
1644{
1452 .name = "twl4030", 1645 .name = "twl4030",
1453 .playback = { 1646 .playback = {
1454 .stream_name = "Playback", 1647 .stream_name = "Playback",
@@ -1463,6 +1656,23 @@ struct snd_soc_dai twl4030_dai = {
1463 .rates = TWL4030_RATES, 1656 .rates = TWL4030_RATES,
1464 .formats = TWL4030_FORMATS,}, 1657 .formats = TWL4030_FORMATS,},
1465 .ops = &twl4030_dai_ops, 1658 .ops = &twl4030_dai_ops,
1659},
1660{
1661 .name = "twl4030 Voice",
1662 .playback = {
1663 .stream_name = "Playback",
1664 .channels_min = 1,
1665 .channels_max = 1,
1666 .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
1667 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
1668 .capture = {
1669 .stream_name = "Capture",
1670 .channels_min = 1,
1671 .channels_max = 2,
1672 .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
1673 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
1674 .ops = &twl4030_dai_voice_ops,
1675},
1466}; 1676};
1467EXPORT_SYMBOL_GPL(twl4030_dai); 1677EXPORT_SYMBOL_GPL(twl4030_dai);
1468 1678
@@ -1503,8 +1713,8 @@ static int twl4030_init(struct snd_soc_device *socdev)
1503 codec->read = twl4030_read_reg_cache; 1713 codec->read = twl4030_read_reg_cache;
1504 codec->write = twl4030_write; 1714 codec->write = twl4030_write;
1505 codec->set_bias_level = twl4030_set_bias_level; 1715 codec->set_bias_level = twl4030_set_bias_level;
1506 codec->dai = &twl4030_dai; 1716 codec->dai = twl4030_dai;
1507 codec->num_dai = 1; 1717 codec->num_dai = ARRAY_SIZE(twl4030_dai),
1508 codec->reg_cache_size = sizeof(twl4030_reg); 1718 codec->reg_cache_size = sizeof(twl4030_reg);
1509 codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), 1719 codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
1510 GFP_KERNEL); 1720 GFP_KERNEL);
@@ -1598,13 +1808,13 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
1598 1808
1599static int __init twl4030_modinit(void) 1809static int __init twl4030_modinit(void)
1600{ 1810{
1601 return snd_soc_register_dai(&twl4030_dai); 1811 return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
1602} 1812}
1603module_init(twl4030_modinit); 1813module_init(twl4030_modinit);
1604 1814
1605static void __exit twl4030_exit(void) 1815static void __exit twl4030_exit(void)
1606{ 1816{
1607 snd_soc_unregister_dai(&twl4030_dai); 1817 snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
1608} 1818}
1609module_exit(twl4030_exit); 1819module_exit(twl4030_exit);
1610 1820
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index cb63765db1df..981ec609495b 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -113,6 +113,8 @@
113#define TWL4030_SEL_16K 0x04 113#define TWL4030_SEL_16K 0x04
114#define TWL4030_CODECPDZ 0x02 114#define TWL4030_CODECPDZ 0x02
115#define TWL4030_OPT_MODE 0x01 115#define TWL4030_OPT_MODE 0x01
116#define TWL4030_OPTION_1 (1 << 0)
117#define TWL4030_OPTION_2 (0 << 0)
116 118
117/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */ 119/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
118 120
@@ -171,6 +173,17 @@
171#define TWL4030_CLK256FS_EN 0x02 173#define TWL4030_CLK256FS_EN 0x02
172#define TWL4030_AIF_EN 0x01 174#define TWL4030_AIF_EN 0x01
173 175
176/* VOICE_IF (0x0F) Fields */
177
178#define TWL4030_VIF_SLAVE_EN 0x80
179#define TWL4030_VIF_DIN_EN 0x40
180#define TWL4030_VIF_DOUT_EN 0x20
181#define TWL4030_VIF_SWAP 0x10
182#define TWL4030_VIF_FORMAT 0x08
183#define TWL4030_VIF_TRI_EN 0x04
184#define TWL4030_VIF_SUB_EN 0x02
185#define TWL4030_VIF_EN 0x01
186
174/* EAR_CTL (0x21) */ 187/* EAR_CTL (0x21) */
175#define TWL4030_EAR_GAIN 0x30 188#define TWL4030_EAR_GAIN 0x30
176 189
@@ -236,7 +249,10 @@
236#define TWL4030_SMOOTH_ANAVOL_EN 0x02 249#define TWL4030_SMOOTH_ANAVOL_EN 0x02
237#define TWL4030_DIGMIC_LR_SWAP_EN 0x01 250#define TWL4030_DIGMIC_LR_SWAP_EN 0x01
238 251
239extern struct snd_soc_dai twl4030_dai; 252#define TWL4030_DAI_HIFI 0
253#define TWL4030_DAI_VOICE 1
254
255extern struct snd_soc_dai twl4030_dai[2];
240extern struct snd_soc_codec_device soc_codec_dev_twl4030; 256extern struct snd_soc_codec_device soc_codec_dev_twl4030;
241 257
242#endif /* End of __TWL4030_AUDIO_H__ */ 258#endif /* End of __TWL4030_AUDIO_H__ */
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 8cf571f1a803..c5391841d41f 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1523,6 +1523,7 @@ struct snd_soc_dai wm8903_dai = {
1523 .formats = WM8903_FORMATS, 1523 .formats = WM8903_FORMATS,
1524 }, 1524 },
1525 .ops = &wm8903_dai_ops, 1525 .ops = &wm8903_dai_ops,
1526 .symmetric_rates = 1,
1526}; 1527};
1527EXPORT_SYMBOL_GPL(wm8903_dai); 1528EXPORT_SYMBOL_GPL(wm8903_dai);
1528 1529
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
new file mode 100644
index 000000000000..e224d8add170
--- /dev/null
+++ b/sound/soc/codecs/wm8960.c
@@ -0,0 +1,969 @@
1/*
2 * wm8960.c -- WM8960 ALSA SoC Audio driver
3 *
4 * Author: Liam Girdwood
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/module.h>
12#include <linux/moduleparam.h>
13#include <linux/init.h>
14#include <linux/delay.h>
15#include <linux/pm.h>
16#include <linux/i2c.h>
17#include <linux/platform_device.h>
18#include <sound/core.h>
19#include <sound/pcm.h>
20#include <sound/pcm_params.h>
21#include <sound/soc.h>
22#include <sound/soc-dapm.h>
23#include <sound/initval.h>
24#include <sound/tlv.h>
25
26#include "wm8960.h"
27
28#define AUDIO_NAME "wm8960"
29
30struct snd_soc_codec_device soc_codec_dev_wm8960;
31
32/* R25 - Power 1 */
33#define WM8960_VREF 0x40
34
35/* R28 - Anti-pop 1 */
36#define WM8960_POBCTRL 0x80
37#define WM8960_BUFDCOPEN 0x10
38#define WM8960_BUFIOEN 0x08
39#define WM8960_SOFT_ST 0x04
40#define WM8960_HPSTBY 0x01
41
42/* R29 - Anti-pop 2 */
43#define WM8960_DISOP 0x40
44
45/*
46 * wm8960 register cache
47 * We can't read the WM8960 register space when we are
48 * using 2 wire for device control, so we cache them instead.
49 */
50static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
51 0x0097, 0x0097, 0x0000, 0x0000,
52 0x0000, 0x0008, 0x0000, 0x000a,
53 0x01c0, 0x0000, 0x00ff, 0x00ff,
54 0x0000, 0x0000, 0x0000, 0x0000,
55 0x0000, 0x007b, 0x0100, 0x0032,
56 0x0000, 0x00c3, 0x00c3, 0x01c0,
57 0x0000, 0x0000, 0x0000, 0x0000,
58 0x0000, 0x0000, 0x0000, 0x0000,
59 0x0100, 0x0100, 0x0050, 0x0050,
60 0x0050, 0x0050, 0x0000, 0x0000,
61 0x0000, 0x0000, 0x0040, 0x0000,
62 0x0000, 0x0050, 0x0050, 0x0000,
63 0x0002, 0x0037, 0x004d, 0x0080,
64 0x0008, 0x0031, 0x0026, 0x00e9,
65};
66
67struct wm8960_priv {
68 u16 reg_cache[WM8960_CACHEREGNUM];
69 struct snd_soc_codec codec;
70};
71
72/*
73 * read wm8960 register cache
74 */
75static inline unsigned int wm8960_read_reg_cache(struct snd_soc_codec *codec,
76 unsigned int reg)
77{
78 u16 *cache = codec->reg_cache;
79 if (reg == WM8960_RESET)
80 return 0;
81 if (reg >= WM8960_CACHEREGNUM)
82 return -1;
83 return cache[reg];
84}
85
86/*
87 * write wm8960 register cache
88 */
89static inline void wm8960_write_reg_cache(struct snd_soc_codec *codec,
90 u16 reg, unsigned int value)
91{
92 u16 *cache = codec->reg_cache;
93 if (reg >= WM8960_CACHEREGNUM)
94 return;
95 cache[reg] = value;
96}
97
98static inline unsigned int wm8960_read(struct snd_soc_codec *codec,
99 unsigned int reg)
100{
101 return wm8960_read_reg_cache(codec, reg);
102}
103
104/*
105 * write to the WM8960 register space
106 */
107static int wm8960_write(struct snd_soc_codec *codec, unsigned int reg,
108 unsigned int value)
109{
110 u8 data[2];
111
112 /* data is
113 * D15..D9 WM8960 register offset
114 * D8...D0 register data
115 */
116 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
117 data[1] = value & 0x00ff;
118
119 wm8960_write_reg_cache(codec, reg, value);
120 if (codec->hw_write(codec->control_data, data, 2) == 2)
121 return 0;
122 else
123 return -EIO;
124}
125
126#define wm8960_reset(c) wm8960_write(c, WM8960_RESET, 0)
127
128/* enumerated controls */
129static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
130static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
131 "Right Inverted", "Stereo Inversion"};
132static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
133static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
134static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
135static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
136
137static const struct soc_enum wm8960_enum[] = {
138 SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
139 SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
140 SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
141 SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
142 SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
143 SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
144 SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
145};
146
147static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
148static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
149static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
150static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
151
152static const struct snd_kcontrol_new wm8960_snd_controls[] = {
153SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
154 0, 63, 0, adc_tlv),
155SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
156 6, 1, 0),
157SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
158 7, 1, 0),
159
160SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
161 0, 255, 0, dac_tlv),
162
163SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
164 0, 127, 0, out_tlv),
165SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
166 7, 1, 0),
167
168SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
169 0, 127, 0, out_tlv),
170SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
171 7, 1, 0),
172SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
173SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
174
175SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
176SOC_ENUM("ADC Polarity", wm8960_enum[1]),
177SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
178SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
179
180SOC_ENUM("DAC Polarity", wm8960_enum[2]),
181
182SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
183SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
184SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
185SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
186
187SOC_ENUM("ALC Function", wm8960_enum[5]),
188SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
189SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
190SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
191SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
192SOC_ENUM("ALC Mode", wm8960_enum[6]),
193SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
194SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
195
196SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
197SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
198
199SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
200 0, 127, 0),
201
202SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
203 WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
204SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
205 WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
206SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
207 WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
208SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
209 WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
210};
211
212static const struct snd_kcontrol_new wm8960_lin_boost[] = {
213SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
214SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
215SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
216};
217
218static const struct snd_kcontrol_new wm8960_lin[] = {
219SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
220};
221
222static const struct snd_kcontrol_new wm8960_rin_boost[] = {
223SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
224SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
225SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
226};
227
228static const struct snd_kcontrol_new wm8960_rin[] = {
229SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
230};
231
232static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
233SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
234SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
235SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
236};
237
238static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
239SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
240SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
241SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
242};
243
244static const struct snd_kcontrol_new wm8960_mono_out[] = {
245SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
246SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
247};
248
249static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
250SND_SOC_DAPM_INPUT("LINPUT1"),
251SND_SOC_DAPM_INPUT("RINPUT1"),
252SND_SOC_DAPM_INPUT("LINPUT2"),
253SND_SOC_DAPM_INPUT("RINPUT2"),
254SND_SOC_DAPM_INPUT("LINPUT3"),
255SND_SOC_DAPM_INPUT("RINPUT3"),
256
257SND_SOC_DAPM_MICBIAS("MICB", WM8960_POWER1, 1, 0),
258
259SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
260 wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
261SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
262 wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
263
264SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
265 wm8960_lin, ARRAY_SIZE(wm8960_lin)),
266SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
267 wm8960_rin, ARRAY_SIZE(wm8960_rin)),
268
269SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0),
270SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0),
271
272SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
273SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
274
275SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
276 &wm8960_loutput_mixer[0],
277 ARRAY_SIZE(wm8960_loutput_mixer)),
278SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
279 &wm8960_routput_mixer[0],
280 ARRAY_SIZE(wm8960_routput_mixer)),
281
282SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
283 &wm8960_mono_out[0],
284 ARRAY_SIZE(wm8960_mono_out)),
285
286SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
287SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
288
289SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
290SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
291
292SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
293SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
294
295SND_SOC_DAPM_OUTPUT("SPK_LP"),
296SND_SOC_DAPM_OUTPUT("SPK_LN"),
297SND_SOC_DAPM_OUTPUT("HP_L"),
298SND_SOC_DAPM_OUTPUT("HP_R"),
299SND_SOC_DAPM_OUTPUT("SPK_RP"),
300SND_SOC_DAPM_OUTPUT("SPK_RN"),
301SND_SOC_DAPM_OUTPUT("OUT3"),
302};
303
304static const struct snd_soc_dapm_route audio_paths[] = {
305 { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
306 { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
307 { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
308
309 { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
310 { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */
311 { "Left Input Mixer", NULL, "LINPUT2" },
312 { "Left Input Mixer", NULL, "LINPUT3" },
313
314 { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
315 { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
316 { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
317
318 { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
319 { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */
320 { "Right Input Mixer", NULL, "RINPUT2" },
321 { "Right Input Mixer", NULL, "LINPUT3" },
322
323 { "Left ADC", NULL, "Left Input Mixer" },
324 { "Right ADC", NULL, "Right Input Mixer" },
325
326 { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
327 { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
328 { "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
329
330 { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
331 { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
332 { "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
333
334 { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
335 { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
336
337 { "LOUT1 PGA", NULL, "Left Output Mixer" },
338 { "ROUT1 PGA", NULL, "Right Output Mixer" },
339
340 { "HP_L", NULL, "LOUT1 PGA" },
341 { "HP_R", NULL, "ROUT1 PGA" },
342
343 { "Left Speaker PGA", NULL, "Left Output Mixer" },
344 { "Right Speaker PGA", NULL, "Right Output Mixer" },
345
346 { "Left Speaker Output", NULL, "Left Speaker PGA" },
347 { "Right Speaker Output", NULL, "Right Speaker PGA" },
348
349 { "SPK_LN", NULL, "Left Speaker Output" },
350 { "SPK_LP", NULL, "Left Speaker Output" },
351 { "SPK_RN", NULL, "Right Speaker Output" },
352 { "SPK_RP", NULL, "Right Speaker Output" },
353
354 { "OUT3", NULL, "Mono Output Mixer", }
355};
356
357static int wm8960_add_widgets(struct snd_soc_codec *codec)
358{
359 snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
360 ARRAY_SIZE(wm8960_dapm_widgets));
361
362 snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
363
364 snd_soc_dapm_new_widgets(codec);
365 return 0;
366}
367
368static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
369 unsigned int fmt)
370{
371 struct snd_soc_codec *codec = codec_dai->codec;
372 u16 iface = 0;
373
374 /* set master/slave audio interface */
375 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
376 case SND_SOC_DAIFMT_CBM_CFM:
377 iface |= 0x0040;
378 break;
379 case SND_SOC_DAIFMT_CBS_CFS:
380 break;
381 default:
382 return -EINVAL;
383 }
384
385 /* interface format */
386 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
387 case SND_SOC_DAIFMT_I2S:
388 iface |= 0x0002;
389 break;
390 case SND_SOC_DAIFMT_RIGHT_J:
391 break;
392 case SND_SOC_DAIFMT_LEFT_J:
393 iface |= 0x0001;
394 break;
395 case SND_SOC_DAIFMT_DSP_A:
396 iface |= 0x0003;
397 break;
398 case SND_SOC_DAIFMT_DSP_B:
399 iface |= 0x0013;
400 break;
401 default:
402 return -EINVAL;
403 }
404
405 /* clock inversion */
406 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
407 case SND_SOC_DAIFMT_NB_NF:
408 break;
409 case SND_SOC_DAIFMT_IB_IF:
410 iface |= 0x0090;
411 break;
412 case SND_SOC_DAIFMT_IB_NF:
413 iface |= 0x0080;
414 break;
415 case SND_SOC_DAIFMT_NB_IF:
416 iface |= 0x0010;
417 break;
418 default:
419 return -EINVAL;
420 }
421
422 /* set iface */
423 wm8960_write(codec, WM8960_IFACE1, iface);
424 return 0;
425}
426
427static int wm8960_hw_params(struct snd_pcm_substream *substream,
428 struct snd_pcm_hw_params *params,
429 struct snd_soc_dai *dai)
430{
431 struct snd_soc_pcm_runtime *rtd = substream->private_data;
432 struct snd_soc_device *socdev = rtd->socdev;
433 struct snd_soc_codec *codec = socdev->card->codec;
434 u16 iface = wm8960_read(codec, WM8960_IFACE1) & 0xfff3;
435
436 /* bit size */
437 switch (params_format(params)) {
438 case SNDRV_PCM_FORMAT_S16_LE:
439 break;
440 case SNDRV_PCM_FORMAT_S20_3LE:
441 iface |= 0x0004;
442 break;
443 case SNDRV_PCM_FORMAT_S24_LE:
444 iface |= 0x0008;
445 break;
446 }
447
448 /* set iface */
449 wm8960_write(codec, WM8960_IFACE1, iface);
450 return 0;
451}
452
453static int wm8960_mute(struct snd_soc_dai *dai, int mute)
454{
455 struct snd_soc_codec *codec = dai->codec;
456 u16 mute_reg = wm8960_read(codec, WM8960_DACCTL1) & 0xfff7;
457
458 if (mute)
459 wm8960_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
460 else
461 wm8960_write(codec, WM8960_DACCTL1, mute_reg);
462 return 0;
463}
464
465static int wm8960_set_bias_level(struct snd_soc_codec *codec,
466 enum snd_soc_bias_level level)
467{
468 struct wm8960_data *pdata = codec->dev->platform_data;
469 u16 reg;
470
471 switch (level) {
472 case SND_SOC_BIAS_ON:
473 break;
474
475 case SND_SOC_BIAS_PREPARE:
476 /* Set VMID to 2x50k */
477 reg = wm8960_read(codec, WM8960_POWER1);
478 reg &= ~0x180;
479 reg |= 0x80;
480 wm8960_write(codec, WM8960_POWER1, reg);
481 break;
482
483 case SND_SOC_BIAS_STANDBY:
484 if (codec->bias_level == SND_SOC_BIAS_OFF) {
485 /* Enable anti-pop features */
486 wm8960_write(codec, WM8960_APOP1,
487 WM8960_POBCTRL | WM8960_SOFT_ST |
488 WM8960_BUFDCOPEN | WM8960_BUFIOEN);
489
490 /* Discharge HP output */
491 reg = WM8960_DISOP;
492 if (pdata)
493 reg |= pdata->dres << 4;
494 wm8960_write(codec, WM8960_APOP2, reg);
495
496 msleep(400);
497
498 wm8960_write(codec, WM8960_APOP2, 0);
499
500 /* Enable & ramp VMID at 2x50k */
501 reg = wm8960_read(codec, WM8960_POWER1);
502 reg |= 0x80;
503 wm8960_write(codec, WM8960_POWER1, reg);
504 msleep(100);
505
506 /* Enable VREF */
507 wm8960_write(codec, WM8960_POWER1, reg | WM8960_VREF);
508
509 /* Disable anti-pop features */
510 wm8960_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
511 }
512
513 /* Set VMID to 2x250k */
514 reg = wm8960_read(codec, WM8960_POWER1);
515 reg &= ~0x180;
516 reg |= 0x100;
517 wm8960_write(codec, WM8960_POWER1, reg);
518 break;
519
520 case SND_SOC_BIAS_OFF:
521 /* Enable anti-pop features */
522 wm8960_write(codec, WM8960_APOP1,
523 WM8960_POBCTRL | WM8960_SOFT_ST |
524 WM8960_BUFDCOPEN | WM8960_BUFIOEN);
525
526 /* Disable VMID and VREF, let them discharge */
527 wm8960_write(codec, WM8960_POWER1, 0);
528 msleep(600);
529
530 wm8960_write(codec, WM8960_APOP1, 0);
531 break;
532 }
533
534 codec->bias_level = level;
535
536 return 0;
537}
538
539/* PLL divisors */
540struct _pll_div {
541 u32 pre_div:1;
542 u32 n:4;
543 u32 k:24;
544};
545
546/* The size in bits of the pll divide multiplied by 10
547 * to allow rounding later */
548#define FIXED_PLL_SIZE ((1 << 24) * 10)
549
550static int pll_factors(unsigned int source, unsigned int target,
551 struct _pll_div *pll_div)
552{
553 unsigned long long Kpart;
554 unsigned int K, Ndiv, Nmod;
555
556 pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
557
558 /* Scale up target to PLL operating frequency */
559 target *= 4;
560
561 Ndiv = target / source;
562 if (Ndiv < 6) {
563 source >>= 1;
564 pll_div->pre_div = 1;
565 Ndiv = target / source;
566 } else
567 pll_div->pre_div = 0;
568
569 if ((Ndiv < 6) || (Ndiv > 12)) {
570 pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
571 return -EINVAL;
572 }
573
574 pll_div->n = Ndiv;
575 Nmod = target % source;
576 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
577
578 do_div(Kpart, source);
579
580 K = Kpart & 0xFFFFFFFF;
581
582 /* Check if we need to round */
583 if ((K % 10) >= 5)
584 K += 5;
585
586 /* Move down to proper range now rounding is done */
587 K /= 10;
588
589 pll_div->k = K;
590
591 pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
592 pll_div->n, pll_div->k, pll_div->pre_div);
593
594 return 0;
595}
596
597static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
598 int pll_id, unsigned int freq_in, unsigned int freq_out)
599{
600 struct snd_soc_codec *codec = codec_dai->codec;
601 u16 reg;
602 static struct _pll_div pll_div;
603 int ret;
604
605 if (freq_in && freq_out) {
606 ret = pll_factors(freq_in, freq_out, &pll_div);
607 if (ret != 0)
608 return ret;
609 }
610
611 /* Disable the PLL: even if we are changing the frequency the
612 * PLL needs to be disabled while we do so. */
613 wm8960_write(codec, WM8960_CLOCK1,
614 wm8960_read(codec, WM8960_CLOCK1) & ~1);
615 wm8960_write(codec, WM8960_POWER2,
616 wm8960_read(codec, WM8960_POWER2) & ~1);
617
618 if (!freq_in || !freq_out)
619 return 0;
620
621 reg = wm8960_read(codec, WM8960_PLL1) & ~0x3f;
622 reg |= pll_div.pre_div << 4;
623 reg |= pll_div.n;
624
625 if (pll_div.k) {
626 reg |= 0x20;
627
628 wm8960_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
629 wm8960_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
630 wm8960_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
631 }
632 wm8960_write(codec, WM8960_PLL1, reg);
633
634 /* Turn it on */
635 wm8960_write(codec, WM8960_POWER2,
636 wm8960_read(codec, WM8960_POWER2) | 1);
637 msleep(250);
638 wm8960_write(codec, WM8960_CLOCK1,
639 wm8960_read(codec, WM8960_CLOCK1) | 1);
640
641 return 0;
642}
643
644static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
645 int div_id, int div)
646{
647 struct snd_soc_codec *codec = codec_dai->codec;
648 u16 reg;
649
650 switch (div_id) {
651 case WM8960_SYSCLKSEL:
652 reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1fe;
653 wm8960_write(codec, WM8960_CLOCK1, reg | div);
654 break;
655 case WM8960_SYSCLKDIV:
656 reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1f9;
657 wm8960_write(codec, WM8960_CLOCK1, reg | div);
658 break;
659 case WM8960_DACDIV:
660 reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1c7;
661 wm8960_write(codec, WM8960_CLOCK1, reg | div);
662 break;
663 case WM8960_OPCLKDIV:
664 reg = wm8960_read(codec, WM8960_PLL1) & 0x03f;
665 wm8960_write(codec, WM8960_PLL1, reg | div);
666 break;
667 case WM8960_DCLKDIV:
668 reg = wm8960_read(codec, WM8960_CLOCK2) & 0x03f;
669 wm8960_write(codec, WM8960_CLOCK2, reg | div);
670 break;
671 case WM8960_TOCLKSEL:
672 reg = wm8960_read(codec, WM8960_ADDCTL1) & 0x1fd;
673 wm8960_write(codec, WM8960_ADDCTL1, reg | div);
674 break;
675 default:
676 return -EINVAL;
677 }
678
679 return 0;
680}
681
682#define WM8960_RATES SNDRV_PCM_RATE_8000_48000
683
684#define WM8960_FORMATS \
685 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
686 SNDRV_PCM_FMTBIT_S24_LE)
687
688static struct snd_soc_dai_ops wm8960_dai_ops = {
689 .hw_params = wm8960_hw_params,
690 .digital_mute = wm8960_mute,
691 .set_fmt = wm8960_set_dai_fmt,
692 .set_clkdiv = wm8960_set_dai_clkdiv,
693 .set_pll = wm8960_set_dai_pll,
694};
695
696struct snd_soc_dai wm8960_dai = {
697 .name = "WM8960",
698 .playback = {
699 .stream_name = "Playback",
700 .channels_min = 1,
701 .channels_max = 2,
702 .rates = WM8960_RATES,
703 .formats = WM8960_FORMATS,},
704 .capture = {
705 .stream_name = "Capture",
706 .channels_min = 1,
707 .channels_max = 2,
708 .rates = WM8960_RATES,
709 .formats = WM8960_FORMATS,},
710 .ops = &wm8960_dai_ops,
711 .symmetric_rates = 1,
712};
713EXPORT_SYMBOL_GPL(wm8960_dai);
714
715static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
716{
717 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
718 struct snd_soc_codec *codec = socdev->card->codec;
719
720 wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF);
721 return 0;
722}
723
724static int wm8960_resume(struct platform_device *pdev)
725{
726 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
727 struct snd_soc_codec *codec = socdev->card->codec;
728 int i;
729 u8 data[2];
730 u16 *cache = codec->reg_cache;
731
732 /* Sync reg_cache with the hardware */
733 for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
734 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
735 data[1] = cache[i] & 0x00ff;
736 codec->hw_write(codec->control_data, data, 2);
737 }
738
739 wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
740 wm8960_set_bias_level(codec, codec->suspend_bias_level);
741 return 0;
742}
743
744static struct snd_soc_codec *wm8960_codec;
745
746static int wm8960_probe(struct platform_device *pdev)
747{
748 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
749 struct snd_soc_codec *codec;
750 int ret = 0;
751
752 if (wm8960_codec == NULL) {
753 dev_err(&pdev->dev, "Codec device not registered\n");
754 return -ENODEV;
755 }
756
757 socdev->card->codec = wm8960_codec;
758 codec = wm8960_codec;
759
760 /* register pcms */
761 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
762 if (ret < 0) {
763 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
764 goto pcm_err;
765 }
766
767 snd_soc_add_controls(codec, wm8960_snd_controls,
768 ARRAY_SIZE(wm8960_snd_controls));
769 wm8960_add_widgets(codec);
770 ret = snd_soc_init_card(socdev);
771 if (ret < 0) {
772 dev_err(codec->dev, "failed to register card: %d\n", ret);
773 goto card_err;
774 }
775
776 return ret;
777
778card_err:
779 snd_soc_free_pcms(socdev);
780 snd_soc_dapm_free(socdev);
781pcm_err:
782 return ret;
783}
784
785/* power down chip */
786static int wm8960_remove(struct platform_device *pdev)
787{
788 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
789
790 snd_soc_free_pcms(socdev);
791 snd_soc_dapm_free(socdev);
792
793 return 0;
794}
795
796struct snd_soc_codec_device soc_codec_dev_wm8960 = {
797 .probe = wm8960_probe,
798 .remove = wm8960_remove,
799 .suspend = wm8960_suspend,
800 .resume = wm8960_resume,
801};
802EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
803
804static int wm8960_register(struct wm8960_priv *wm8960)
805{
806 struct wm8960_data *pdata = wm8960->codec.dev->platform_data;
807 struct snd_soc_codec *codec = &wm8960->codec;
808 int ret;
809 u16 reg;
810
811 if (wm8960_codec) {
812 dev_err(codec->dev, "Another WM8960 is registered\n");
813 return -EINVAL;
814 }
815
816 if (!pdata) {
817 dev_warn(codec->dev, "No platform data supplied\n");
818 } else {
819 if (pdata->dres > WM8960_DRES_MAX) {
820 dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
821 pdata->dres = 0;
822 }
823 }
824
825 mutex_init(&codec->mutex);
826 INIT_LIST_HEAD(&codec->dapm_widgets);
827 INIT_LIST_HEAD(&codec->dapm_paths);
828
829 codec->private_data = wm8960;
830 codec->name = "WM8960";
831 codec->owner = THIS_MODULE;
832 codec->read = wm8960_read_reg_cache;
833 codec->write = wm8960_write;
834 codec->bias_level = SND_SOC_BIAS_OFF;
835 codec->set_bias_level = wm8960_set_bias_level;
836 codec->dai = &wm8960_dai;
837 codec->num_dai = 1;
838 codec->reg_cache_size = WM8960_CACHEREGNUM;
839 codec->reg_cache = &wm8960->reg_cache;
840
841 memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
842
843 ret = wm8960_reset(codec);
844 if (ret < 0) {
845 dev_err(codec->dev, "Failed to issue reset\n");
846 return ret;
847 }
848
849 wm8960_dai.dev = codec->dev;
850
851 wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
852
853 /* Latch the update bits */
854 reg = wm8960_read(codec, WM8960_LINVOL);
855 wm8960_write(codec, WM8960_LINVOL, reg | 0x100);
856 reg = wm8960_read(codec, WM8960_RINVOL);
857 wm8960_write(codec, WM8960_RINVOL, reg | 0x100);
858 reg = wm8960_read(codec, WM8960_LADC);
859 wm8960_write(codec, WM8960_LADC, reg | 0x100);
860 reg = wm8960_read(codec, WM8960_RADC);
861 wm8960_write(codec, WM8960_RADC, reg | 0x100);
862 reg = wm8960_read(codec, WM8960_LDAC);
863 wm8960_write(codec, WM8960_LDAC, reg | 0x100);
864 reg = wm8960_read(codec, WM8960_RDAC);
865 wm8960_write(codec, WM8960_RDAC, reg | 0x100);
866 reg = wm8960_read(codec, WM8960_LOUT1);
867 wm8960_write(codec, WM8960_LOUT1, reg | 0x100);
868 reg = wm8960_read(codec, WM8960_ROUT1);
869 wm8960_write(codec, WM8960_ROUT1, reg | 0x100);
870 reg = wm8960_read(codec, WM8960_LOUT2);
871 wm8960_write(codec, WM8960_LOUT2, reg | 0x100);
872 reg = wm8960_read(codec, WM8960_ROUT2);
873 wm8960_write(codec, WM8960_ROUT2, reg | 0x100);
874
875 wm8960_codec = codec;
876
877 ret = snd_soc_register_codec(codec);
878 if (ret != 0) {
879 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
880 return ret;
881 }
882
883 ret = snd_soc_register_dai(&wm8960_dai);
884 if (ret != 0) {
885 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
886 snd_soc_unregister_codec(codec);
887 return ret;
888 }
889
890 return 0;
891}
892
893static void wm8960_unregister(struct wm8960_priv *wm8960)
894{
895 wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
896 snd_soc_unregister_dai(&wm8960_dai);
897 snd_soc_unregister_codec(&wm8960->codec);
898 kfree(wm8960);
899 wm8960_codec = NULL;
900}
901
902static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
903 const struct i2c_device_id *id)
904{
905 struct wm8960_priv *wm8960;
906 struct snd_soc_codec *codec;
907
908 wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);
909 if (wm8960 == NULL)
910 return -ENOMEM;
911
912 codec = &wm8960->codec;
913 codec->hw_write = (hw_write_t)i2c_master_send;
914
915 i2c_set_clientdata(i2c, wm8960);
916 codec->control_data = i2c;
917
918 codec->dev = &i2c->dev;
919
920 return wm8960_register(wm8960);
921}
922
923static __devexit int wm8960_i2c_remove(struct i2c_client *client)
924{
925 struct wm8960_priv *wm8960 = i2c_get_clientdata(client);
926 wm8960_unregister(wm8960);
927 return 0;
928}
929
930static const struct i2c_device_id wm8960_i2c_id[] = {
931 { "wm8960", 0 },
932 { }
933};
934MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
935
936static struct i2c_driver wm8960_i2c_driver = {
937 .driver = {
938 .name = "WM8960 I2C Codec",
939 .owner = THIS_MODULE,
940 },
941 .probe = wm8960_i2c_probe,
942 .remove = __devexit_p(wm8960_i2c_remove),
943 .id_table = wm8960_i2c_id,
944};
945
946static int __init wm8960_modinit(void)
947{
948 int ret;
949
950 ret = i2c_add_driver(&wm8960_i2c_driver);
951 if (ret != 0) {
952 printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
953 ret);
954 }
955
956 return ret;
957}
958module_init(wm8960_modinit);
959
960static void __exit wm8960_exit(void)
961{
962 i2c_del_driver(&wm8960_i2c_driver);
963}
964module_exit(wm8960_exit);
965
966
967MODULE_DESCRIPTION("ASoC WM8960 driver");
968MODULE_AUTHOR("Liam Girdwood");
969MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h
new file mode 100644
index 000000000000..c9af56c9d9d4
--- /dev/null
+++ b/sound/soc/codecs/wm8960.h
@@ -0,0 +1,127 @@
1/*
2 * wm8960.h -- WM8960 Soc Audio driver
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#ifndef _WM8960_H
10#define _WM8960_H
11
12/* WM8960 register space */
13
14
15#define WM8960_CACHEREGNUM 56
16
17#define WM8960_LINVOL 0x0
18#define WM8960_RINVOL 0x1
19#define WM8960_LOUT1 0x2
20#define WM8960_ROUT1 0x3
21#define WM8960_CLOCK1 0x4
22#define WM8960_DACCTL1 0x5
23#define WM8960_DACCTL2 0x6
24#define WM8960_IFACE1 0x7
25#define WM8960_CLOCK2 0x8
26#define WM8960_IFACE2 0x9
27#define WM8960_LDAC 0xa
28#define WM8960_RDAC 0xb
29
30#define WM8960_RESET 0xf
31#define WM8960_3D 0x10
32#define WM8960_ALC1 0x11
33#define WM8960_ALC2 0x12
34#define WM8960_ALC3 0x13
35#define WM8960_NOISEG 0x14
36#define WM8960_LADC 0x15
37#define WM8960_RADC 0x16
38#define WM8960_ADDCTL1 0x17
39#define WM8960_ADDCTL2 0x18
40#define WM8960_POWER1 0x19
41#define WM8960_POWER2 0x1a
42#define WM8960_ADDCTL3 0x1b
43#define WM8960_APOP1 0x1c
44#define WM8960_APOP2 0x1d
45
46#define WM8960_LINPATH 0x20
47#define WM8960_RINPATH 0x21
48#define WM8960_LOUTMIX 0x22
49
50#define WM8960_ROUTMIX 0x25
51#define WM8960_MONOMIX1 0x26
52#define WM8960_MONOMIX2 0x27
53#define WM8960_LOUT2 0x28
54#define WM8960_ROUT2 0x29
55#define WM8960_MONO 0x2a
56#define WM8960_INBMIX1 0x2b
57#define WM8960_INBMIX2 0x2c
58#define WM8960_BYPASS1 0x2d
59#define WM8960_BYPASS2 0x2e
60#define WM8960_POWER3 0x2f
61#define WM8960_ADDCTL4 0x30
62#define WM8960_CLASSD1 0x31
63
64#define WM8960_CLASSD3 0x33
65#define WM8960_PLL1 0x34
66#define WM8960_PLL2 0x35
67#define WM8960_PLL3 0x36
68#define WM8960_PLL4 0x37
69
70
71/*
72 * WM8960 Clock dividers
73 */
74#define WM8960_SYSCLKDIV 0
75#define WM8960_DACDIV 1
76#define WM8960_OPCLKDIV 2
77#define WM8960_DCLKDIV 3
78#define WM8960_TOCLKSEL 4
79#define WM8960_SYSCLKSEL 5
80
81#define WM8960_SYSCLK_DIV_1 (0 << 1)
82#define WM8960_SYSCLK_DIV_2 (2 << 1)
83
84#define WM8960_SYSCLK_MCLK (0 << 0)
85#define WM8960_SYSCLK_PLL (1 << 0)
86
87#define WM8960_DAC_DIV_1 (0 << 3)
88#define WM8960_DAC_DIV_1_5 (1 << 3)
89#define WM8960_DAC_DIV_2 (2 << 3)
90#define WM8960_DAC_DIV_3 (3 << 3)
91#define WM8960_DAC_DIV_4 (4 << 3)
92#define WM8960_DAC_DIV_5_5 (5 << 3)
93#define WM8960_DAC_DIV_6 (6 << 3)
94
95#define WM8960_DCLK_DIV_1_5 (0 << 6)
96#define WM8960_DCLK_DIV_2 (1 << 6)
97#define WM8960_DCLK_DIV_3 (2 << 6)
98#define WM8960_DCLK_DIV_4 (3 << 6)
99#define WM8960_DCLK_DIV_6 (4 << 6)
100#define WM8960_DCLK_DIV_8 (5 << 6)
101#define WM8960_DCLK_DIV_12 (6 << 6)
102#define WM8960_DCLK_DIV_16 (7 << 6)
103
104#define WM8960_TOCLK_F19 (0 << 1)
105#define WM8960_TOCLK_F21 (1 << 1)
106
107#define WM8960_OPCLK_DIV_1 (0 << 0)
108#define WM8960_OPCLK_DIV_2 (1 << 0)
109#define WM8960_OPCLK_DIV_3 (2 << 0)
110#define WM8960_OPCLK_DIV_4 (3 << 0)
111#define WM8960_OPCLK_DIV_5_5 (4 << 0)
112#define WM8960_OPCLK_DIV_6 (5 << 0)
113
114extern struct snd_soc_dai wm8960_dai;
115extern struct snd_soc_codec_device soc_codec_dev_wm8960;
116
117#define WM8960_DRES_400R 0
118#define WM8960_DRES_200R 1
119#define WM8960_DRES_600R 2
120#define WM8960_DRES_150R 3
121#define WM8960_DRES_MAX 3
122
123struct wm8960_data {
124 int dres;
125};
126
127#endif
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
new file mode 100644
index 000000000000..c05f71803aa8
--- /dev/null
+++ b/sound/soc/codecs/wm8988.c
@@ -0,0 +1,1097 @@
1/*
2 * wm8988.c -- WM8988 ALSA SoC audio driver
3 *
4 * Copyright 2009 Wolfson Microelectronics plc
5 * Copyright 2005 Openedhand Ltd.
6 *
7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/pm.h>
19#include <linux/i2c.h>
20#include <linux/spi/spi.h>
21#include <linux/platform_device.h>
22#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/tlv.h>
26#include <sound/soc.h>
27#include <sound/soc-dapm.h>
28#include <sound/initval.h>
29
30#include "wm8988.h"
31
32/*
33 * wm8988 register cache
34 * We can't read the WM8988 register space when we
35 * are using 2 wire for device control, so we cache them instead.
36 */
37static const u16 wm8988_reg[] = {
38 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
39 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
40 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
41 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
42 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
43 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
44 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
45 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
46 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
47 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
48 0x0079, 0x0079, 0x0079, /* 40 */
49};
50
51/* codec private data */
52struct wm8988_priv {
53 unsigned int sysclk;
54 struct snd_soc_codec codec;
55 struct snd_pcm_hw_constraint_list *sysclk_constraints;
56 u16 reg_cache[WM8988_NUM_REG];
57};
58
59
60/*
61 * read wm8988 register cache
62 */
63static inline unsigned int wm8988_read_reg_cache(struct snd_soc_codec *codec,
64 unsigned int reg)
65{
66 u16 *cache = codec->reg_cache;
67 if (reg > WM8988_NUM_REG)
68 return -1;
69 return cache[reg];
70}
71
72/*
73 * write wm8988 register cache
74 */
75static inline void wm8988_write_reg_cache(struct snd_soc_codec *codec,
76 unsigned int reg, unsigned int value)
77{
78 u16 *cache = codec->reg_cache;
79 if (reg > WM8988_NUM_REG)
80 return;
81 cache[reg] = value;
82}
83
84static int wm8988_write(struct snd_soc_codec *codec, unsigned int reg,
85 unsigned int value)
86{
87 u8 data[2];
88
89 /* data is
90 * D15..D9 WM8753 register offset
91 * D8...D0 register data
92 */
93 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
94 data[1] = value & 0x00ff;
95
96 wm8988_write_reg_cache(codec, reg, value);
97 if (codec->hw_write(codec->control_data, data, 2) == 2)
98 return 0;
99 else
100 return -EIO;
101}
102
103#define wm8988_reset(c) wm8988_write(c, WM8988_RESET, 0)
104
105/*
106 * WM8988 Controls
107 */
108
109static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"};
110static const struct soc_enum bass_boost =
111 SOC_ENUM_SINGLE(WM8988_BASS, 7, 2, bass_boost_txt);
112
113static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
114static const struct soc_enum bass_filter =
115 SOC_ENUM_SINGLE(WM8988_BASS, 6, 2, bass_filter_txt);
116
117static const char *treble_txt[] = {"8kHz", "4kHz"};
118static const struct soc_enum treble =
119 SOC_ENUM_SINGLE(WM8988_TREBLE, 6, 2, treble_txt);
120
121static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"};
122static const struct soc_enum stereo_3d_lc =
123 SOC_ENUM_SINGLE(WM8988_3D, 5, 2, stereo_3d_lc_txt);
124
125static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"};
126static const struct soc_enum stereo_3d_uc =
127 SOC_ENUM_SINGLE(WM8988_3D, 6, 2, stereo_3d_uc_txt);
128
129static const char *stereo_3d_func_txt[] = {"Capture", "Playback"};
130static const struct soc_enum stereo_3d_func =
131 SOC_ENUM_SINGLE(WM8988_3D, 7, 2, stereo_3d_func_txt);
132
133static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"};
134static const struct soc_enum alc_func =
135 SOC_ENUM_SINGLE(WM8988_ALC1, 7, 4, alc_func_txt);
136
137static const char *ng_type_txt[] = {"Constant PGA Gain",
138 "Mute ADC Output"};
139static const struct soc_enum ng_type =
140 SOC_ENUM_SINGLE(WM8988_NGATE, 1, 2, ng_type_txt);
141
142static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"};
143static const struct soc_enum deemph =
144 SOC_ENUM_SINGLE(WM8988_ADCDAC, 1, 4, deemph_txt);
145
146static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert",
147 "L + R Invert"};
148static const struct soc_enum adcpol =
149 SOC_ENUM_SINGLE(WM8988_ADCDAC, 5, 4, adcpol_txt);
150
151static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
152static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
153static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
154static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
155static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
156
157static const struct snd_kcontrol_new wm8988_snd_controls[] = {
158
159SOC_ENUM("Bass Boost", bass_boost),
160SOC_ENUM("Bass Filter", bass_filter),
161SOC_SINGLE("Bass Volume", WM8988_BASS, 0, 15, 1),
162
163SOC_SINGLE("Treble Volume", WM8988_TREBLE, 0, 15, 0),
164SOC_ENUM("Treble Cut-off", treble),
165
166SOC_SINGLE("3D Switch", WM8988_3D, 0, 1, 0),
167SOC_SINGLE("3D Volume", WM8988_3D, 1, 15, 0),
168SOC_ENUM("3D Lower Cut-off", stereo_3d_lc),
169SOC_ENUM("3D Upper Cut-off", stereo_3d_uc),
170SOC_ENUM("3D Mode", stereo_3d_func),
171
172SOC_SINGLE("ALC Capture Target Volume", WM8988_ALC1, 0, 7, 0),
173SOC_SINGLE("ALC Capture Max Volume", WM8988_ALC1, 4, 7, 0),
174SOC_ENUM("ALC Capture Function", alc_func),
175SOC_SINGLE("ALC Capture ZC Switch", WM8988_ALC2, 7, 1, 0),
176SOC_SINGLE("ALC Capture Hold Time", WM8988_ALC2, 0, 15, 0),
177SOC_SINGLE("ALC Capture Decay Time", WM8988_ALC3, 4, 15, 0),
178SOC_SINGLE("ALC Capture Attack Time", WM8988_ALC3, 0, 15, 0),
179SOC_SINGLE("ALC Capture NG Threshold", WM8988_NGATE, 3, 31, 0),
180SOC_ENUM("ALC Capture NG Type", ng_type),
181SOC_SINGLE("ALC Capture NG Switch", WM8988_NGATE, 0, 1, 0),
182
183SOC_SINGLE("ZC Timeout Switch", WM8988_ADCTL1, 0, 1, 0),
184
185SOC_DOUBLE_R_TLV("Capture Digital Volume", WM8988_LADC, WM8988_RADC,
186 0, 255, 0, adc_tlv),
187SOC_DOUBLE_R_TLV("Capture Volume", WM8988_LINVOL, WM8988_RINVOL,
188 0, 63, 0, pga_tlv),
189SOC_DOUBLE_R("Capture ZC Switch", WM8988_LINVOL, WM8988_RINVOL, 6, 1, 0),
190SOC_DOUBLE_R("Capture Switch", WM8988_LINVOL, WM8988_RINVOL, 7, 1, 1),
191
192SOC_ENUM("Playback De-emphasis", deemph),
193
194SOC_ENUM("Capture Polarity", adcpol),
195SOC_SINGLE("Playback 6dB Attenuate", WM8988_ADCDAC, 7, 1, 0),
196SOC_SINGLE("Capture 6dB Attenuate", WM8988_ADCDAC, 8, 1, 0),
197
198SOC_DOUBLE_R_TLV("PCM Volume", WM8988_LDAC, WM8988_RDAC, 0, 255, 0, dac_tlv),
199
200SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", WM8988_LOUTM1, 4, 7, 1,
201 bypass_tlv),
202SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", WM8988_LOUTM2, 4, 7, 1,
203 bypass_tlv),
204SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", WM8988_ROUTM1, 4, 7, 1,
205 bypass_tlv),
206SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", WM8988_ROUTM2, 4, 7, 1,
207 bypass_tlv),
208
209SOC_DOUBLE_R("Output 1 Playback ZC Switch", WM8988_LOUT1V,
210 WM8988_ROUT1V, 7, 1, 0),
211SOC_DOUBLE_R_TLV("Output 1 Playback Volume", WM8988_LOUT1V, WM8988_ROUT1V,
212 0, 127, 0, out_tlv),
213
214SOC_DOUBLE_R("Output 2 Playback ZC Switch", WM8988_LOUT2V,
215 WM8988_ROUT2V, 7, 1, 0),
216SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V,
217 0, 127, 0, out_tlv),
218
219};
220
221/*
222 * DAPM Controls
223 */
224
225static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
226 struct snd_kcontrol *kcontrol, int event)
227{
228 struct snd_soc_codec *codec = w->codec;
229 u16 adctl2 = wm8988_read_reg_cache(codec, WM8988_ADCTL2);
230
231 /* Use the DAC to gate LRC if active, otherwise use ADC */
232 if (wm8988_read_reg_cache(codec, WM8988_PWR2) & 0x180)
233 adctl2 &= ~0x4;
234 else
235 adctl2 |= 0x4;
236
237 return wm8988_write(codec, WM8988_ADCTL2, adctl2);
238}
239
240static const char *wm8988_line_texts[] = {
241 "Line 1", "Line 2", "PGA", "Differential"};
242
243static const unsigned int wm8988_line_values[] = {
244 0, 1, 3, 4};
245
246static const struct soc_enum wm8988_lline_enum =
247 SOC_VALUE_ENUM_SINGLE(WM8988_LOUTM1, 0, 7,
248 ARRAY_SIZE(wm8988_line_texts),
249 wm8988_line_texts,
250 wm8988_line_values);
251static const struct snd_kcontrol_new wm8988_left_line_controls =
252 SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
253
254static const struct soc_enum wm8988_rline_enum =
255 SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7,
256 ARRAY_SIZE(wm8988_line_texts),
257 wm8988_line_texts,
258 wm8988_line_values);
259static const struct snd_kcontrol_new wm8988_right_line_controls =
260 SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
261
262/* Left Mixer */
263static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = {
264 SOC_DAPM_SINGLE("Playback Switch", WM8988_LOUTM1, 8, 1, 0),
265 SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_LOUTM1, 7, 1, 0),
266 SOC_DAPM_SINGLE("Right Playback Switch", WM8988_LOUTM2, 8, 1, 0),
267 SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_LOUTM2, 7, 1, 0),
268};
269
270/* Right Mixer */
271static const struct snd_kcontrol_new wm8988_right_mixer_controls[] = {
272 SOC_DAPM_SINGLE("Left Playback Switch", WM8988_ROUTM1, 8, 1, 0),
273 SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_ROUTM1, 7, 1, 0),
274 SOC_DAPM_SINGLE("Playback Switch", WM8988_ROUTM2, 8, 1, 0),
275 SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_ROUTM2, 7, 1, 0),
276};
277
278static const char *wm8988_pga_sel[] = {"Line 1", "Line 2", "Differential"};
279static const unsigned int wm8988_pga_val[] = { 0, 1, 3 };
280
281/* Left PGA Mux */
282static const struct soc_enum wm8988_lpga_enum =
283 SOC_VALUE_ENUM_SINGLE(WM8988_LADCIN, 6, 3,
284 ARRAY_SIZE(wm8988_pga_sel),
285 wm8988_pga_sel,
286 wm8988_pga_val);
287static const struct snd_kcontrol_new wm8988_left_pga_controls =
288 SOC_DAPM_VALUE_ENUM("Route", wm8988_lpga_enum);
289
290/* Right PGA Mux */
291static const struct soc_enum wm8988_rpga_enum =
292 SOC_VALUE_ENUM_SINGLE(WM8988_RADCIN, 6, 3,
293 ARRAY_SIZE(wm8988_pga_sel),
294 wm8988_pga_sel,
295 wm8988_pga_val);
296static const struct snd_kcontrol_new wm8988_right_pga_controls =
297 SOC_DAPM_VALUE_ENUM("Route", wm8988_rpga_enum);
298
299/* Differential Mux */
300static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"};
301static const struct soc_enum diffmux =
302 SOC_ENUM_SINGLE(WM8988_ADCIN, 8, 2, wm8988_diff_sel);
303static const struct snd_kcontrol_new wm8988_diffmux_controls =
304 SOC_DAPM_ENUM("Route", diffmux);
305
306/* Mono ADC Mux */
307static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)",
308 "Mono (Right)", "Digital Mono"};
309static const struct soc_enum monomux =
310 SOC_ENUM_SINGLE(WM8988_ADCIN, 6, 4, wm8988_mono_mux);
311static const struct snd_kcontrol_new wm8988_monomux_controls =
312 SOC_DAPM_ENUM("Route", monomux);
313
314static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = {
315 SND_SOC_DAPM_MICBIAS("Mic Bias", WM8988_PWR1, 1, 0),
316
317 SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
318 &wm8988_diffmux_controls),
319 SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
320 &wm8988_monomux_controls),
321 SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
322 &wm8988_monomux_controls),
323
324 SND_SOC_DAPM_MUX("Left PGA Mux", WM8988_PWR1, 5, 0,
325 &wm8988_left_pga_controls),
326 SND_SOC_DAPM_MUX("Right PGA Mux", WM8988_PWR1, 4, 0,
327 &wm8988_right_pga_controls),
328
329 SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
330 &wm8988_left_line_controls),
331 SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
332 &wm8988_right_line_controls),
333
334 SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0),
335 SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0),
336
337 SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0),
338 SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0),
339
340 SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
341 &wm8988_left_mixer_controls[0],
342 ARRAY_SIZE(wm8988_left_mixer_controls)),
343 SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
344 &wm8988_right_mixer_controls[0],
345 ARRAY_SIZE(wm8988_right_mixer_controls)),
346
347 SND_SOC_DAPM_PGA("Right Out 2", WM8988_PWR2, 3, 0, NULL, 0),
348 SND_SOC_DAPM_PGA("Left Out 2", WM8988_PWR2, 4, 0, NULL, 0),
349 SND_SOC_DAPM_PGA("Right Out 1", WM8988_PWR2, 5, 0, NULL, 0),
350 SND_SOC_DAPM_PGA("Left Out 1", WM8988_PWR2, 6, 0, NULL, 0),
351
352 SND_SOC_DAPM_POST("LRC control", wm8988_lrc_control),
353
354 SND_SOC_DAPM_OUTPUT("LOUT1"),
355 SND_SOC_DAPM_OUTPUT("ROUT1"),
356 SND_SOC_DAPM_OUTPUT("LOUT2"),
357 SND_SOC_DAPM_OUTPUT("ROUT2"),
358 SND_SOC_DAPM_OUTPUT("VREF"),
359
360 SND_SOC_DAPM_INPUT("LINPUT1"),
361 SND_SOC_DAPM_INPUT("LINPUT2"),
362 SND_SOC_DAPM_INPUT("RINPUT1"),
363 SND_SOC_DAPM_INPUT("RINPUT2"),
364};
365
366static const struct snd_soc_dapm_route audio_map[] = {
367
368 { "Left Line Mux", "Line 1", "LINPUT1" },
369 { "Left Line Mux", "Line 2", "LINPUT2" },
370 { "Left Line Mux", "PGA", "Left PGA Mux" },
371 { "Left Line Mux", "Differential", "Differential Mux" },
372
373 { "Right Line Mux", "Line 1", "RINPUT1" },
374 { "Right Line Mux", "Line 2", "RINPUT2" },
375 { "Right Line Mux", "PGA", "Right PGA Mux" },
376 { "Right Line Mux", "Differential", "Differential Mux" },
377
378 { "Left PGA Mux", "Line 1", "LINPUT1" },
379 { "Left PGA Mux", "Line 2", "LINPUT2" },
380 { "Left PGA Mux", "Differential", "Differential Mux" },
381
382 { "Right PGA Mux", "Line 1", "RINPUT1" },
383 { "Right PGA Mux", "Line 2", "RINPUT2" },
384 { "Right PGA Mux", "Differential", "Differential Mux" },
385
386 { "Differential Mux", "Line 1", "LINPUT1" },
387 { "Differential Mux", "Line 1", "RINPUT1" },
388 { "Differential Mux", "Line 2", "LINPUT2" },
389 { "Differential Mux", "Line 2", "RINPUT2" },
390
391 { "Left ADC Mux", "Stereo", "Left PGA Mux" },
392 { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
393 { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
394
395 { "Right ADC Mux", "Stereo", "Right PGA Mux" },
396 { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
397 { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
398
399 { "Left ADC", NULL, "Left ADC Mux" },
400 { "Right ADC", NULL, "Right ADC Mux" },
401
402 { "Left Line Mux", "Line 1", "LINPUT1" },
403 { "Left Line Mux", "Line 2", "LINPUT2" },
404 { "Left Line Mux", "PGA", "Left PGA Mux" },
405 { "Left Line Mux", "Differential", "Differential Mux" },
406
407 { "Right Line Mux", "Line 1", "RINPUT1" },
408 { "Right Line Mux", "Line 2", "RINPUT2" },
409 { "Right Line Mux", "PGA", "Right PGA Mux" },
410 { "Right Line Mux", "Differential", "Differential Mux" },
411
412 { "Left Mixer", "Playback Switch", "Left DAC" },
413 { "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
414 { "Left Mixer", "Right Playback Switch", "Right DAC" },
415 { "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
416
417 { "Right Mixer", "Left Playback Switch", "Left DAC" },
418 { "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
419 { "Right Mixer", "Playback Switch", "Right DAC" },
420 { "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
421
422 { "Left Out 1", NULL, "Left Mixer" },
423 { "LOUT1", NULL, "Left Out 1" },
424 { "Right Out 1", NULL, "Right Mixer" },
425 { "ROUT1", NULL, "Right Out 1" },
426
427 { "Left Out 2", NULL, "Left Mixer" },
428 { "LOUT2", NULL, "Left Out 2" },
429 { "Right Out 2", NULL, "Right Mixer" },
430 { "ROUT2", NULL, "Right Out 2" },
431};
432
433struct _coeff_div {
434 u32 mclk;
435 u32 rate;
436 u16 fs;
437 u8 sr:5;
438 u8 usb:1;
439};
440
441/* codec hifi mclk clock divider coefficients */
442static const struct _coeff_div coeff_div[] = {
443 /* 8k */
444 {12288000, 8000, 1536, 0x6, 0x0},
445 {11289600, 8000, 1408, 0x16, 0x0},
446 {18432000, 8000, 2304, 0x7, 0x0},
447 {16934400, 8000, 2112, 0x17, 0x0},
448 {12000000, 8000, 1500, 0x6, 0x1},
449
450 /* 11.025k */
451 {11289600, 11025, 1024, 0x18, 0x0},
452 {16934400, 11025, 1536, 0x19, 0x0},
453 {12000000, 11025, 1088, 0x19, 0x1},
454
455 /* 16k */
456 {12288000, 16000, 768, 0xa, 0x0},
457 {18432000, 16000, 1152, 0xb, 0x0},
458 {12000000, 16000, 750, 0xa, 0x1},
459
460 /* 22.05k */
461 {11289600, 22050, 512, 0x1a, 0x0},
462 {16934400, 22050, 768, 0x1b, 0x0},
463 {12000000, 22050, 544, 0x1b, 0x1},
464
465 /* 32k */
466 {12288000, 32000, 384, 0xc, 0x0},
467 {18432000, 32000, 576, 0xd, 0x0},
468 {12000000, 32000, 375, 0xa, 0x1},
469
470 /* 44.1k */
471 {11289600, 44100, 256, 0x10, 0x0},
472 {16934400, 44100, 384, 0x11, 0x0},
473 {12000000, 44100, 272, 0x11, 0x1},
474
475 /* 48k */
476 {12288000, 48000, 256, 0x0, 0x0},
477 {18432000, 48000, 384, 0x1, 0x0},
478 {12000000, 48000, 250, 0x0, 0x1},
479
480 /* 88.2k */
481 {11289600, 88200, 128, 0x1e, 0x0},
482 {16934400, 88200, 192, 0x1f, 0x0},
483 {12000000, 88200, 136, 0x1f, 0x1},
484
485 /* 96k */
486 {12288000, 96000, 128, 0xe, 0x0},
487 {18432000, 96000, 192, 0xf, 0x0},
488 {12000000, 96000, 125, 0xe, 0x1},
489};
490
491static inline int get_coeff(int mclk, int rate)
492{
493 int i;
494
495 for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
496 if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
497 return i;
498 }
499
500 return -EINVAL;
501}
502
503/* The set of rates we can generate from the above for each SYSCLK */
504
505static unsigned int rates_12288[] = {
506 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
507};
508
509static struct snd_pcm_hw_constraint_list constraints_12288 = {
510 .count = ARRAY_SIZE(rates_12288),
511 .list = rates_12288,
512};
513
514static unsigned int rates_112896[] = {
515 8000, 11025, 22050, 44100,
516};
517
518static struct snd_pcm_hw_constraint_list constraints_112896 = {
519 .count = ARRAY_SIZE(rates_112896),
520 .list = rates_112896,
521};
522
523static unsigned int rates_12[] = {
524 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
525 48000, 88235, 96000,
526};
527
528static struct snd_pcm_hw_constraint_list constraints_12 = {
529 .count = ARRAY_SIZE(rates_12),
530 .list = rates_12,
531};
532
533/*
534 * Note that this should be called from init rather than from hw_params.
535 */
536static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
537 int clk_id, unsigned int freq, int dir)
538{
539 struct snd_soc_codec *codec = codec_dai->codec;
540 struct wm8988_priv *wm8988 = codec->private_data;
541
542 switch (freq) {
543 case 11289600:
544 case 18432000:
545 case 22579200:
546 case 36864000:
547 wm8988->sysclk_constraints = &constraints_112896;
548 wm8988->sysclk = freq;
549 return 0;
550
551 case 12288000:
552 case 16934400:
553 case 24576000:
554 case 33868800:
555 wm8988->sysclk_constraints = &constraints_12288;
556 wm8988->sysclk = freq;
557 return 0;
558
559 case 12000000:
560 case 24000000:
561 wm8988->sysclk_constraints = &constraints_12;
562 wm8988->sysclk = freq;
563 return 0;
564 }
565 return -EINVAL;
566}
567
568static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
569 unsigned int fmt)
570{
571 struct snd_soc_codec *codec = codec_dai->codec;
572 u16 iface = 0;
573
574 /* set master/slave audio interface */
575 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
576 case SND_SOC_DAIFMT_CBM_CFM:
577 iface = 0x0040;
578 break;
579 case SND_SOC_DAIFMT_CBS_CFS:
580 break;
581 default:
582 return -EINVAL;
583 }
584
585 /* interface format */
586 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
587 case SND_SOC_DAIFMT_I2S:
588 iface |= 0x0002;
589 break;
590 case SND_SOC_DAIFMT_RIGHT_J:
591 break;
592 case SND_SOC_DAIFMT_LEFT_J:
593 iface |= 0x0001;
594 break;
595 case SND_SOC_DAIFMT_DSP_A:
596 iface |= 0x0003;
597 break;
598 case SND_SOC_DAIFMT_DSP_B:
599 iface |= 0x0013;
600 break;
601 default:
602 return -EINVAL;
603 }
604
605 /* clock inversion */
606 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
607 case SND_SOC_DAIFMT_NB_NF:
608 break;
609 case SND_SOC_DAIFMT_IB_IF:
610 iface |= 0x0090;
611 break;
612 case SND_SOC_DAIFMT_IB_NF:
613 iface |= 0x0080;
614 break;
615 case SND_SOC_DAIFMT_NB_IF:
616 iface |= 0x0010;
617 break;
618 default:
619 return -EINVAL;
620 }
621
622 wm8988_write(codec, WM8988_IFACE, iface);
623 return 0;
624}
625
626static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
627 struct snd_soc_dai *dai)
628{
629 struct snd_soc_codec *codec = dai->codec;
630 struct wm8988_priv *wm8988 = codec->private_data;
631
632 /* The set of sample rates that can be supported depends on the
633 * MCLK supplied to the CODEC - enforce this.
634 */
635 if (!wm8988->sysclk) {
636 dev_err(codec->dev,
637 "No MCLK configured, call set_sysclk() on init\n");
638 return -EINVAL;
639 }
640
641 snd_pcm_hw_constraint_list(substream->runtime, 0,
642 SNDRV_PCM_HW_PARAM_RATE,
643 wm8988->sysclk_constraints);
644
645 return 0;
646}
647
648static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
649 struct snd_pcm_hw_params *params,
650 struct snd_soc_dai *dai)
651{
652 struct snd_soc_pcm_runtime *rtd = substream->private_data;
653 struct snd_soc_device *socdev = rtd->socdev;
654 struct snd_soc_codec *codec = socdev->card->codec;
655 struct wm8988_priv *wm8988 = codec->private_data;
656 u16 iface = wm8988_read_reg_cache(codec, WM8988_IFACE) & 0x1f3;
657 u16 srate = wm8988_read_reg_cache(codec, WM8988_SRATE) & 0x180;
658 int coeff;
659
660 coeff = get_coeff(wm8988->sysclk, params_rate(params));
661 if (coeff < 0) {
662 coeff = get_coeff(wm8988->sysclk / 2, params_rate(params));
663 srate |= 0x40;
664 }
665 if (coeff < 0) {
666 dev_err(codec->dev,
667 "Unable to configure sample rate %dHz with %dHz MCLK\n",
668 params_rate(params), wm8988->sysclk);
669 return coeff;
670 }
671
672 /* bit size */
673 switch (params_format(params)) {
674 case SNDRV_PCM_FORMAT_S16_LE:
675 break;
676 case SNDRV_PCM_FORMAT_S20_3LE:
677 iface |= 0x0004;
678 break;
679 case SNDRV_PCM_FORMAT_S24_LE:
680 iface |= 0x0008;
681 break;
682 case SNDRV_PCM_FORMAT_S32_LE:
683 iface |= 0x000c;
684 break;
685 }
686
687 /* set iface & srate */
688 wm8988_write(codec, WM8988_IFACE, iface);
689 if (coeff >= 0)
690 wm8988_write(codec, WM8988_SRATE, srate |
691 (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
692
693 return 0;
694}
695
696static int wm8988_mute(struct snd_soc_dai *dai, int mute)
697{
698 struct snd_soc_codec *codec = dai->codec;
699 u16 mute_reg = wm8988_read_reg_cache(codec, WM8988_ADCDAC) & 0xfff7;
700
701 if (mute)
702 wm8988_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
703 else
704 wm8988_write(codec, WM8988_ADCDAC, mute_reg);
705 return 0;
706}
707
708static int wm8988_set_bias_level(struct snd_soc_codec *codec,
709 enum snd_soc_bias_level level)
710{
711 u16 pwr_reg = wm8988_read_reg_cache(codec, WM8988_PWR1) & ~0x1c1;
712
713 switch (level) {
714 case SND_SOC_BIAS_ON:
715 break;
716
717 case SND_SOC_BIAS_PREPARE:
718 /* VREF, VMID=2x50k, digital enabled */
719 wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
720 break;
721
722 case SND_SOC_BIAS_STANDBY:
723 if (codec->bias_level == SND_SOC_BIAS_OFF) {
724 /* VREF, VMID=2x5k */
725 wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
726
727 /* Charge caps */
728 msleep(100);
729 }
730
731 /* VREF, VMID=2*500k, digital stopped */
732 wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
733 break;
734
735 case SND_SOC_BIAS_OFF:
736 wm8988_write(codec, WM8988_PWR1, 0x0000);
737 break;
738 }
739 codec->bias_level = level;
740 return 0;
741}
742
743#define WM8988_RATES SNDRV_PCM_RATE_8000_96000
744
745#define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
746 SNDRV_PCM_FMTBIT_S24_LE)
747
748static struct snd_soc_dai_ops wm8988_ops = {
749 .startup = wm8988_pcm_startup,
750 .hw_params = wm8988_pcm_hw_params,
751 .set_fmt = wm8988_set_dai_fmt,
752 .set_sysclk = wm8988_set_dai_sysclk,
753 .digital_mute = wm8988_mute,
754};
755
756struct snd_soc_dai wm8988_dai = {
757 .name = "WM8988",
758 .playback = {
759 .stream_name = "Playback",
760 .channels_min = 1,
761 .channels_max = 2,
762 .rates = WM8988_RATES,
763 .formats = WM8988_FORMATS,
764 },
765 .capture = {
766 .stream_name = "Capture",
767 .channels_min = 1,
768 .channels_max = 2,
769 .rates = WM8988_RATES,
770 .formats = WM8988_FORMATS,
771 },
772 .ops = &wm8988_ops,
773 .symmetric_rates = 1,
774};
775EXPORT_SYMBOL_GPL(wm8988_dai);
776
777static int wm8988_suspend(struct platform_device *pdev, pm_message_t state)
778{
779 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
780 struct snd_soc_codec *codec = socdev->card->codec;
781
782 wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
783 return 0;
784}
785
786static int wm8988_resume(struct platform_device *pdev)
787{
788 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
789 struct snd_soc_codec *codec = socdev->card->codec;
790 int i;
791 u8 data[2];
792 u16 *cache = codec->reg_cache;
793
794 /* Sync reg_cache with the hardware */
795 for (i = 0; i < WM8988_NUM_REG; i++) {
796 if (i == WM8988_RESET)
797 continue;
798 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
799 data[1] = cache[i] & 0x00ff;
800 codec->hw_write(codec->control_data, data, 2);
801 }
802
803 wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
804
805 return 0;
806}
807
808static struct snd_soc_codec *wm8988_codec;
809
810static int wm8988_probe(struct platform_device *pdev)
811{
812 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
813 struct snd_soc_codec *codec;
814 int ret = 0;
815
816 if (wm8988_codec == NULL) {
817 dev_err(&pdev->dev, "Codec device not registered\n");
818 return -ENODEV;
819 }
820
821 socdev->card->codec = wm8988_codec;
822 codec = wm8988_codec;
823
824 /* register pcms */
825 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
826 if (ret < 0) {
827 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
828 goto pcm_err;
829 }
830
831 snd_soc_add_controls(codec, wm8988_snd_controls,
832 ARRAY_SIZE(wm8988_snd_controls));
833 snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
834 ARRAY_SIZE(wm8988_dapm_widgets));
835 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
836 snd_soc_dapm_new_widgets(codec);
837
838 ret = snd_soc_init_card(socdev);
839 if (ret < 0) {
840 dev_err(codec->dev, "failed to register card: %d\n", ret);
841 goto card_err;
842 }
843
844 return ret;
845
846card_err:
847 snd_soc_free_pcms(socdev);
848 snd_soc_dapm_free(socdev);
849pcm_err:
850 return ret;
851}
852
853static int wm8988_remove(struct platform_device *pdev)
854{
855 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
856
857 snd_soc_free_pcms(socdev);
858 snd_soc_dapm_free(socdev);
859
860 return 0;
861}
862
863struct snd_soc_codec_device soc_codec_dev_wm8988 = {
864 .probe = wm8988_probe,
865 .remove = wm8988_remove,
866 .suspend = wm8988_suspend,
867 .resume = wm8988_resume,
868};
869EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988);
870
871static int wm8988_register(struct wm8988_priv *wm8988)
872{
873 struct snd_soc_codec *codec = &wm8988->codec;
874 int ret;
875 u16 reg;
876
877 if (wm8988_codec) {
878 dev_err(codec->dev, "Another WM8988 is registered\n");
879 ret = -EINVAL;
880 goto err;
881 }
882
883 mutex_init(&codec->mutex);
884 INIT_LIST_HEAD(&codec->dapm_widgets);
885 INIT_LIST_HEAD(&codec->dapm_paths);
886
887 codec->private_data = wm8988;
888 codec->name = "WM8988";
889 codec->owner = THIS_MODULE;
890 codec->read = wm8988_read_reg_cache;
891 codec->write = wm8988_write;
892 codec->dai = &wm8988_dai;
893 codec->num_dai = 1;
894 codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache);
895 codec->reg_cache = &wm8988->reg_cache;
896 codec->bias_level = SND_SOC_BIAS_OFF;
897 codec->set_bias_level = wm8988_set_bias_level;
898
899 memcpy(codec->reg_cache, wm8988_reg,
900 sizeof(wm8988_reg));
901
902 ret = wm8988_reset(codec);
903 if (ret < 0) {
904 dev_err(codec->dev, "Failed to issue reset\n");
905 return ret;
906 }
907
908 /* set the update bits (we always update left then right) */
909 reg = wm8988_read_reg_cache(codec, WM8988_RADC);
910 wm8988_write(codec, WM8988_RADC, reg | 0x100);
911 reg = wm8988_read_reg_cache(codec, WM8988_RDAC);
912 wm8988_write(codec, WM8988_RDAC, reg | 0x0100);
913 reg = wm8988_read_reg_cache(codec, WM8988_ROUT1V);
914 wm8988_write(codec, WM8988_ROUT1V, reg | 0x0100);
915 reg = wm8988_read_reg_cache(codec, WM8988_ROUT2V);
916 wm8988_write(codec, WM8988_ROUT2V, reg | 0x0100);
917 reg = wm8988_read_reg_cache(codec, WM8988_RINVOL);
918 wm8988_write(codec, WM8988_RINVOL, reg | 0x0100);
919
920 wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY);
921
922 wm8988_dai.dev = codec->dev;
923
924 wm8988_codec = codec;
925
926 ret = snd_soc_register_codec(codec);
927 if (ret != 0) {
928 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
929 return ret;
930 }
931
932 ret = snd_soc_register_dai(&wm8988_dai);
933 if (ret != 0) {
934 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
935 snd_soc_unregister_codec(codec);
936 return ret;
937 }
938
939 return 0;
940
941err:
942 kfree(wm8988);
943 return ret;
944}
945
946static void wm8988_unregister(struct wm8988_priv *wm8988)
947{
948 wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_OFF);
949 snd_soc_unregister_dai(&wm8988_dai);
950 snd_soc_unregister_codec(&wm8988->codec);
951 kfree(wm8988);
952 wm8988_codec = NULL;
953}
954
955#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
956static int wm8988_i2c_probe(struct i2c_client *i2c,
957 const struct i2c_device_id *id)
958{
959 struct wm8988_priv *wm8988;
960 struct snd_soc_codec *codec;
961
962 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
963 if (wm8988 == NULL)
964 return -ENOMEM;
965
966 codec = &wm8988->codec;
967 codec->hw_write = (hw_write_t)i2c_master_send;
968
969 i2c_set_clientdata(i2c, wm8988);
970 codec->control_data = i2c;
971
972 codec->dev = &i2c->dev;
973
974 return wm8988_register(wm8988);
975}
976
977static int wm8988_i2c_remove(struct i2c_client *client)
978{
979 struct wm8988_priv *wm8988 = i2c_get_clientdata(client);
980 wm8988_unregister(wm8988);
981 return 0;
982}
983
984static const struct i2c_device_id wm8988_i2c_id[] = {
985 { "wm8988", 0 },
986 { }
987};
988MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
989
990static struct i2c_driver wm8988_i2c_driver = {
991 .driver = {
992 .name = "WM8988",
993 .owner = THIS_MODULE,
994 },
995 .probe = wm8988_i2c_probe,
996 .remove = wm8988_i2c_remove,
997 .id_table = wm8988_i2c_id,
998};
999#endif
1000
1001#if defined(CONFIG_SPI_MASTER)
1002static int wm8988_spi_write(struct spi_device *spi, const char *data, int len)
1003{
1004 struct spi_transfer t;
1005 struct spi_message m;
1006 u8 msg[2];
1007
1008 if (len <= 0)
1009 return 0;
1010
1011 msg[0] = data[0];
1012 msg[1] = data[1];
1013
1014 spi_message_init(&m);
1015 memset(&t, 0, (sizeof t));
1016
1017 t.tx_buf = &msg[0];
1018 t.len = len;
1019
1020 spi_message_add_tail(&t, &m);
1021 spi_sync(spi, &m);
1022
1023 return len;
1024}
1025
1026static int __devinit wm8988_spi_probe(struct spi_device *spi)
1027{
1028 struct wm8988_priv *wm8988;
1029 struct snd_soc_codec *codec;
1030
1031 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
1032 if (wm8988 == NULL)
1033 return -ENOMEM;
1034
1035 codec = &wm8988->codec;
1036 codec->hw_write = (hw_write_t)wm8988_spi_write;
1037 codec->control_data = spi;
1038 codec->dev = &spi->dev;
1039
1040 spi->dev.driver_data = wm8988;
1041
1042 return wm8988_register(wm8988);
1043}
1044
1045static int __devexit wm8988_spi_remove(struct spi_device *spi)
1046{
1047 struct wm8988_priv *wm8988 = spi->dev.driver_data;
1048
1049 wm8988_unregister(wm8988);
1050
1051 return 0;
1052}
1053
1054static struct spi_driver wm8988_spi_driver = {
1055 .driver = {
1056 .name = "wm8988",
1057 .bus = &spi_bus_type,
1058 .owner = THIS_MODULE,
1059 },
1060 .probe = wm8988_spi_probe,
1061 .remove = __devexit_p(wm8988_spi_remove),
1062};
1063#endif
1064
1065static int __init wm8988_modinit(void)
1066{
1067 int ret;
1068
1069#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1070 ret = i2c_add_driver(&wm8988_i2c_driver);
1071 if (ret != 0)
1072 pr_err("WM8988: Unable to register I2C driver: %d\n", ret);
1073#endif
1074#if defined(CONFIG_SPI_MASTER)
1075 ret = spi_register_driver(&wm8988_spi_driver);
1076 if (ret != 0)
1077 pr_err("WM8988: Unable to register SPI driver: %d\n", ret);
1078#endif
1079 return ret;
1080}
1081module_init(wm8988_modinit);
1082
1083static void __exit wm8988_exit(void)
1084{
1085#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1086 i2c_del_driver(&wm8988_i2c_driver);
1087#endif
1088#if defined(CONFIG_SPI_MASTER)
1089 spi_unregister_driver(&wm8988_spi_driver);
1090#endif
1091}
1092module_exit(wm8988_exit);
1093
1094
1095MODULE_DESCRIPTION("ASoC WM8988 driver");
1096MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
1097MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8988.h b/sound/soc/codecs/wm8988.h
new file mode 100644
index 000000000000..4552d37fdd41
--- /dev/null
+++ b/sound/soc/codecs/wm8988.h
@@ -0,0 +1,60 @@
1/*
2 * Copyright 2005 Openedhand Ltd.
3 *
4 * Author: Richard Purdie <richard@openedhand.com>
5 *
6 * Based on WM8753.h
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#ifndef _WM8988_H
15#define _WM8988_H
16
17/* WM8988 register space */
18
19#define WM8988_LINVOL 0x00
20#define WM8988_RINVOL 0x01
21#define WM8988_LOUT1V 0x02
22#define WM8988_ROUT1V 0x03
23#define WM8988_ADCDAC 0x05
24#define WM8988_IFACE 0x07
25#define WM8988_SRATE 0x08
26#define WM8988_LDAC 0x0a
27#define WM8988_RDAC 0x0b
28#define WM8988_BASS 0x0c
29#define WM8988_TREBLE 0x0d
30#define WM8988_RESET 0x0f
31#define WM8988_3D 0x10
32#define WM8988_ALC1 0x11
33#define WM8988_ALC2 0x12
34#define WM8988_ALC3 0x13
35#define WM8988_NGATE 0x14
36#define WM8988_LADC 0x15
37#define WM8988_RADC 0x16
38#define WM8988_ADCTL1 0x17
39#define WM8988_ADCTL2 0x18
40#define WM8988_PWR1 0x19
41#define WM8988_PWR2 0x1a
42#define WM8988_ADCTL3 0x1b
43#define WM8988_ADCIN 0x1f
44#define WM8988_LADCIN 0x20
45#define WM8988_RADCIN 0x21
46#define WM8988_LOUTM1 0x22
47#define WM8988_LOUTM2 0x23
48#define WM8988_ROUTM1 0x24
49#define WM8988_ROUTM2 0x25
50#define WM8988_LOUT2V 0x28
51#define WM8988_ROUT2V 0x29
52#define WM8988_LPPB 0x43
53#define WM8988_NUM_REG 0x44
54
55#define WM8988_SYSCLK 0
56
57extern struct snd_soc_dai wm8988_dai;
58extern struct snd_soc_codec_device soc_codec_dev_wm8988;
59
60#endif
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 523bad077fa0..a6feb7842314 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -189,6 +189,26 @@ SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
189SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), 189SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
190}; 190};
191 191
192static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
193 struct snd_kcontrol *kcontrol, int event)
194{
195 struct snd_soc_codec *codec = w->codec;
196 u16 status, rate;
197
198 BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
199
200 /* Gracefully shut down the voice interface. */
201 status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
202 rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
203 ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
204 schedule_timeout_interruptible(msecs_to_jiffies(1));
205 ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
206 ac97_write(codec, AC97_EXTENDED_MID, status);
207
208 return 0;
209}
210
211
192/* We have to create a fake left and right HP mixers because 212/* We have to create a fake left and right HP mixers because
193 * the codec only has a single control that is shared by both channels. 213 * the codec only has a single control that is shared by both channels.
194 * This makes it impossible to determine the audio path using the current 214 * This makes it impossible to determine the audio path using the current
@@ -400,7 +420,8 @@ SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
400SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 420SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
401SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 421SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
402SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 422SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
403SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1), 423SND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1,
424 wm9713_voice_shutdown, SND_SOC_DAPM_PRE_PMD),
404SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), 425SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
405SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0), 426SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0),
406SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0), 427SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0),
@@ -936,21 +957,6 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
936 return 0; 957 return 0;
937} 958}
938 959
939static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
940 struct snd_soc_dai *dai)
941{
942 struct snd_soc_codec *codec = dai->codec;
943 u16 status, rate;
944
945 /* Gracefully shut down the voice interface. */
946 status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
947 rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
948 ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
949 schedule_timeout_interruptible(msecs_to_jiffies(1));
950 ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
951 ac97_write(codec, AC97_EXTENDED_MID, status);
952}
953
954static int ac97_hifi_prepare(struct snd_pcm_substream *substream, 960static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
955 struct snd_soc_dai *dai) 961 struct snd_soc_dai *dai)
956{ 962{
@@ -1019,7 +1025,6 @@ static struct snd_soc_dai_ops wm9713_dai_ops_aux = {
1019 1025
1020static struct snd_soc_dai_ops wm9713_dai_ops_voice = { 1026static struct snd_soc_dai_ops wm9713_dai_ops_voice = {
1021 .hw_params = wm9713_pcm_hw_params, 1027 .hw_params = wm9713_pcm_hw_params,
1022 .shutdown = wm9713_voiceshutdown,
1023 .set_clkdiv = wm9713_set_dai_clkdiv, 1028 .set_clkdiv = wm9713_set_dai_clkdiv,
1024 .set_pll = wm9713_set_dai_pll, 1029 .set_pll = wm9713_set_dai_pll,
1025 .set_fmt = wm9713_set_dai_fmt, 1030 .set_fmt = wm9713_set_dai_fmt,
@@ -1069,6 +1074,7 @@ struct snd_soc_dai wm9713_dai[] = {
1069 .rates = WM9713_PCM_RATES, 1074 .rates = WM9713_PCM_RATES,
1070 .formats = WM9713_PCM_FORMATS,}, 1075 .formats = WM9713_PCM_FORMATS,},
1071 .ops = &wm9713_dai_ops_voice, 1076 .ops = &wm9713_dai_ops_voice,
1077 .symmetric_rates = 1,
1072 }, 1078 },
1073}; 1079};
1074EXPORT_SYMBOL_GPL(wm9713_dai); 1080EXPORT_SYMBOL_GPL(wm9713_dai);
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 91ef17992de5..b60b1dfbc435 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -383,10 +383,9 @@ static int __init n810_soc_init(void)
383 clk_set_parent(sys_clkout2_src, func96m_clk); 383 clk_set_parent(sys_clkout2_src, func96m_clk);
384 clk_set_rate(sys_clkout2, 12000000); 384 clk_set_rate(sys_clkout2, 12000000);
385 385
386 if (gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) 386 BUG_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
387 BUG(); 387 (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0));
388 if (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0) 388
389 BUG();
390 gpio_direction_output(N810_HEADSET_AMP_GPIO, 0); 389 gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
391 gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0); 390 gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
392 391
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 912614283848..495192af8c2e 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -215,8 +215,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
215 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data); 215 struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
216 struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs; 216 struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
217 int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id; 217 int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
218 int wlen, channels; 218 int wlen, channels, wpf;
219 unsigned long port; 219 unsigned long port;
220 unsigned int format;
220 221
221 if (cpu_class_is_omap1()) { 222 if (cpu_class_is_omap1()) {
222 dma = omap1_dma_reqs[bus_id][substream->stream]; 223 dma = omap1_dma_reqs[bus_id][substream->stream];
@@ -244,18 +245,23 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
244 return 0; 245 return 0;
245 } 246 }
246 247
247 channels = params_channels(params); 248 format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
249 wpf = channels = params_channels(params);
248 switch (channels) { 250 switch (channels) {
249 case 2: 251 case 2:
250 /* Use dual-phase frames */ 252 if (format == SND_SOC_DAIFMT_I2S) {
251 regs->rcr2 |= RPHASE; 253 /* Use dual-phase frames */
252 regs->xcr2 |= XPHASE; 254 regs->rcr2 |= RPHASE;
255 regs->xcr2 |= XPHASE;
256 /* Set 1 word per (McBSP) frame for phase1 and phase2 */
257 wpf--;
258 regs->rcr2 |= RFRLEN2(wpf - 1);
259 regs->xcr2 |= XFRLEN2(wpf - 1);
260 }
253 case 1: 261 case 1:
254 /* Set 1 word per (McBSP) frame */ 262 /* Set word per (McBSP) frame for phase1 */
255 regs->rcr2 |= RFRLEN2(1 - 1); 263 regs->rcr1 |= RFRLEN1(wpf - 1);
256 regs->rcr1 |= RFRLEN1(1 - 1); 264 regs->xcr1 |= XFRLEN1(wpf - 1);
257 regs->xcr2 |= XFRLEN2(1 - 1);
258 regs->xcr1 |= XFRLEN1(1 - 1);
259 break; 265 break;
260 default: 266 default:
261 /* Unsupported number of channels */ 267 /* Unsupported number of channels */
@@ -277,11 +283,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
277 } 283 }
278 284
279 /* Set FS period and length in terms of bit clock periods */ 285 /* Set FS period and length in terms of bit clock periods */
280 switch (mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 286 switch (format) {
281 case SND_SOC_DAIFMT_I2S: 287 case SND_SOC_DAIFMT_I2S:
282 regs->srgr2 |= FPER(wlen * 2 - 1); 288 regs->srgr2 |= FPER(wlen * channels - 1);
283 regs->srgr1 |= FWID(wlen - 1); 289 regs->srgr1 |= FWID(wlen - 1);
284 break; 290 break;
291 case SND_SOC_DAIFMT_DSP_A:
285 case SND_SOC_DAIFMT_DSP_B: 292 case SND_SOC_DAIFMT_DSP_B:
286 regs->srgr2 |= FPER(wlen * channels - 1); 293 regs->srgr2 |= FPER(wlen * channels - 1);
287 regs->srgr1 |= FWID(0); 294 regs->srgr1 |= FWID(0);
@@ -326,6 +333,13 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
326 regs->rcr2 |= RDATDLY(1); 333 regs->rcr2 |= RDATDLY(1);
327 regs->xcr2 |= XDATDLY(1); 334 regs->xcr2 |= XDATDLY(1);
328 break; 335 break;
336 case SND_SOC_DAIFMT_DSP_A:
337 /* 1-bit data delay */
338 regs->rcr2 |= RDATDLY(1);
339 regs->xcr2 |= XDATDLY(1);
340 /* Invert FS polarity configuration */
341 temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
342 break;
329 case SND_SOC_DAIFMT_DSP_B: 343 case SND_SOC_DAIFMT_DSP_B:
330 /* 0-bit data delay */ 344 /* 0-bit data delay */
331 regs->rcr2 |= RDATDLY(0); 345 regs->rcr2 |= RDATDLY(0);
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index b078ed537bc3..07cf7f46b584 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -328,7 +328,7 @@ int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
328 if (!card->dev->dma_mask) 328 if (!card->dev->dma_mask)
329 card->dev->dma_mask = &omap_pcm_dmamask; 329 card->dev->dma_mask = &omap_pcm_dmamask;
330 if (!card->dev->coherent_dma_mask) 330 if (!card->dev->coherent_dma_mask)
331 card->dev->coherent_dma_mask = DMA_32BIT_MASK; 331 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
332 332
333 if (dai->playback.channels_min) { 333 if (dai->playback.channels_min) {
334 ret = omap_pcm_preallocate_dma_buffer(pcm, 334 ret = omap_pcm_preallocate_dma_buffer(pcm,
diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c
index 0c2322dcf02a..027e1a40f8a1 100644
--- a/sound/soc/omap/omap2evm.c
+++ b/sound/soc/omap/omap2evm.c
@@ -86,7 +86,7 @@ static struct snd_soc_dai_link omap2evm_dai = {
86 .name = "TWL4030", 86 .name = "TWL4030",
87 .stream_name = "TWL4030", 87 .stream_name = "TWL4030",
88 .cpu_dai = &omap_mcbsp_dai[0], 88 .cpu_dai = &omap_mcbsp_dai[0],
89 .codec_dai = &twl4030_dai, 89 .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
90 .ops = &omap2evm_ops, 90 .ops = &omap2evm_ops,
91}; 91};
92 92
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c
index fd24a4acd2f5..6aa428e07d86 100644
--- a/sound/soc/omap/omap3beagle.c
+++ b/sound/soc/omap/omap3beagle.c
@@ -83,7 +83,7 @@ static struct snd_soc_dai_link omap3beagle_dai = {
83 .name = "TWL4030", 83 .name = "TWL4030",
84 .stream_name = "TWL4030", 84 .stream_name = "TWL4030",
85 .cpu_dai = &omap_mcbsp_dai[0], 85 .cpu_dai = &omap_mcbsp_dai[0],
86 .codec_dai = &twl4030_dai, 86 .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
87 .ops = &omap3beagle_ops, 87 .ops = &omap3beagle_ops,
88}; 88};
89 89
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index fe282d4ef422..ad219aaf7cb8 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -228,14 +228,14 @@ static struct snd_soc_dai_link omap3pandora_dai[] = {
228 .name = "PCM1773", 228 .name = "PCM1773",
229 .stream_name = "HiFi Out", 229 .stream_name = "HiFi Out",
230 .cpu_dai = &omap_mcbsp_dai[0], 230 .cpu_dai = &omap_mcbsp_dai[0],
231 .codec_dai = &twl4030_dai, 231 .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
232 .ops = &omap3pandora_out_ops, 232 .ops = &omap3pandora_out_ops,
233 .init = omap3pandora_out_init, 233 .init = omap3pandora_out_init,
234 }, { 234 }, {
235 .name = "TWL4030", 235 .name = "TWL4030",
236 .stream_name = "Line/Mic In", 236 .stream_name = "Line/Mic In",
237 .cpu_dai = &omap_mcbsp_dai[1], 237 .cpu_dai = &omap_mcbsp_dai[1],
238 .codec_dai = &twl4030_dai, 238 .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
239 .ops = &omap3pandora_in_ops, 239 .ops = &omap3pandora_in_ops,
240 .init = omap3pandora_in_init, 240 .init = omap3pandora_in_init,
241 } 241 }
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c
index a72dc4e159e5..ec4f8fd8b3a2 100644
--- a/sound/soc/omap/overo.c
+++ b/sound/soc/omap/overo.c
@@ -83,7 +83,7 @@ static struct snd_soc_dai_link overo_dai = {
83 .name = "TWL4030", 83 .name = "TWL4030",
84 .stream_name = "TWL4030", 84 .stream_name = "TWL4030",
85 .cpu_dai = &omap_mcbsp_dai[0], 85 .cpu_dai = &omap_mcbsp_dai[0],
86 .codec_dai = &twl4030_dai, 86 .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
87 .ops = &overo_ops, 87 .ops = &overo_ops,
88}; 88};
89 89
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
index 10f1c867f11d..1c7974101a0b 100644
--- a/sound/soc/omap/sdp3430.c
+++ b/sound/soc/omap/sdp3430.c
@@ -197,7 +197,7 @@ static struct snd_soc_dai_link sdp3430_dai = {
197 .name = "TWL4030", 197 .name = "TWL4030",
198 .stream_name = "TWL4030", 198 .stream_name = "TWL4030",
199 .cpu_dai = &omap_mcbsp_dai[0], 199 .cpu_dai = &omap_mcbsp_dai[0],
200 .codec_dai = &twl4030_dai, 200 .codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
201 .init = sdp3430_twl4030_init, 201 .init = sdp3430_twl4030_init,
202 .ops = &sdp3430_ops, 202 .ops = &sdp3430_ops,
203}; 203};
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 02263e5d8f03..d5be2b30cda5 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -26,8 +26,6 @@
26#include <sound/soc-dapm.h> 26#include <sound/soc-dapm.h>
27 27
28#include <asm/mach-types.h> 28#include <asm/mach-types.h>
29#include <mach/pxa-regs.h>
30#include <mach/hardware.h>
31#include <mach/corgi.h> 29#include <mach/corgi.h>
32#include <mach/audio.h> 30#include <mach/audio.h>
33 31
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
index fe4a729ea648..949be9c2a01b 100644
--- a/sound/soc/pxa/em-x270.c
+++ b/sound/soc/pxa/em-x270.c
@@ -29,8 +29,6 @@
29#include <sound/soc-dapm.h> 29#include <sound/soc-dapm.h>
30 30
31#include <asm/mach-types.h> 31#include <asm/mach-types.h>
32#include <mach/pxa-regs.h>
33#include <mach/hardware.h>
34#include <mach/audio.h> 32#include <mach/audio.h>
35 33
36#include "../codecs/wm9712.h" 34#include "../codecs/wm9712.h"
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index ef7c6c8dc8f1..a51058f66747 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -27,8 +27,6 @@
27 27
28#include <asm/mach-types.h> 28#include <asm/mach-types.h>
29#include <asm/hardware/locomo.h> 29#include <asm/hardware/locomo.h>
30#include <mach/pxa-regs.h>
31#include <mach/hardware.h>
32#include <mach/poodle.h> 30#include <mach/poodle.h>
33#include <mach/audio.h> 31#include <mach/audio.h>
34 32
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 74ff69e3ce34..b9b61ddca6ba 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -30,7 +30,7 @@
30#include <sound/pxa2xx-lib.h> 30#include <sound/pxa2xx-lib.h>
31 31
32#include <mach/hardware.h> 32#include <mach/hardware.h>
33#include <mach/pxa-regs.h> 33#include <mach/dma.h>
34#include <mach/regs-ssp.h> 34#include <mach/regs-ssp.h>
35#include <mach/audio.h> 35#include <mach/audio.h>
36#include <mach/ssp.h> 36#include <mach/ssp.h>
@@ -589,7 +589,10 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
589 case SND_SOC_DAIFMT_NB_IF: 589 case SND_SOC_DAIFMT_NB_IF:
590 break; 590 break;
591 case SND_SOC_DAIFMT_IB_IF: 591 case SND_SOC_DAIFMT_IB_IF:
592 sspsp |= SSPSP_SCMODE(3); 592 sspsp |= SSPSP_SCMODE(2);
593 break;
594 case SND_SOC_DAIFMT_IB_NF:
595 sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
593 break; 596 break;
594 default: 597 default:
595 return -EINVAL; 598 return -EINVAL;
@@ -606,7 +609,13 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
606 case SND_SOC_DAIFMT_NB_NF: 609 case SND_SOC_DAIFMT_NB_NF:
607 sspsp |= SSPSP_SFRMP; 610 sspsp |= SSPSP_SFRMP;
608 break; 611 break;
612 case SND_SOC_DAIFMT_NB_IF:
613 break;
609 case SND_SOC_DAIFMT_IB_IF: 614 case SND_SOC_DAIFMT_IB_IF:
615 sspsp |= SSPSP_SCMODE(2);
616 break;
617 case SND_SOC_DAIFMT_IB_NF:
618 sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
610 break; 619 break;
611 default: 620 default:
612 return -EINVAL; 621 return -EINVAL;
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 01c21c6cdbbc..d9c94d71fa61 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -20,8 +20,8 @@
20#include <sound/pxa2xx-lib.h> 20#include <sound/pxa2xx-lib.h>
21 21
22#include <mach/hardware.h> 22#include <mach/hardware.h>
23#include <mach/pxa-regs.h>
24#include <mach/regs-ac97.h> 23#include <mach/regs-ac97.h>
24#include <mach/dma.h>
25 25
26#include "pxa2xx-pcm.h" 26#include "pxa2xx-pcm.h"
27#include "pxa2xx-ac97.h" 27#include "pxa2xx-ac97.h"
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index e6c24408c5f9..2f4b6e489b78 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -24,7 +24,7 @@
24#include <sound/pxa2xx-lib.h> 24#include <sound/pxa2xx-lib.h>
25 25
26#include <mach/hardware.h> 26#include <mach/hardware.h>
27#include <mach/pxa-regs.h> 27#include <mach/dma.h>
28#include <mach/audio.h> 28#include <mach/audio.h>
29 29
30#include "pxa2xx-pcm.h" 30#include "pxa2xx-pcm.h"
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 53b9fb127a6d..d38e39575f51 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -81,7 +81,7 @@ static struct snd_pcm_ops pxa2xx_pcm_ops = {
81 .mmap = pxa2xx_pcm_mmap, 81 .mmap = pxa2xx_pcm_mmap,
82}; 82};
83 83
84static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK; 84static u64 pxa2xx_pcm_dmamask = DMA_BIT_MASK(32);
85 85
86static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, 86static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
87 struct snd_pcm *pcm) 87 struct snd_pcm *pcm)
@@ -91,7 +91,7 @@ static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
91 if (!card->dev->dma_mask) 91 if (!card->dev->dma_mask)
92 card->dev->dma_mask = &pxa2xx_pcm_dmamask; 92 card->dev->dma_mask = &pxa2xx_pcm_dmamask;
93 if (!card->dev->coherent_dma_mask) 93 if (!card->dev->coherent_dma_mask)
94 card->dev->coherent_dma_mask = DMA_32BIT_MASK; 94 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
95 95
96 if (dai->playback.channels_min) { 96 if (dai->playback.channels_min) {
97 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, 97 ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 6ca9f53080c6..c4cd2acaacb4 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -26,8 +26,6 @@
26#include <sound/soc-dapm.h> 26#include <sound/soc-dapm.h>
27 27
28#include <asm/mach-types.h> 28#include <asm/mach-types.h>
29#include <mach/pxa-regs.h>
30#include <mach/hardware.h>
31#include <mach/spitz.h> 29#include <mach/spitz.h>
32#include "../codecs/wm8750.h" 30#include "../codecs/wm8750.h"
33#include "pxa2xx-pcm.h" 31#include "pxa2xx-pcm.h"
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index fc781374b1bf..dbbd3e9d1637 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -30,8 +30,6 @@
30 30
31#include <asm/mach-types.h> 31#include <asm/mach-types.h>
32#include <mach/tosa.h> 32#include <mach/tosa.h>
33#include <mach/pxa-regs.h>
34#include <mach/hardware.h>
35#include <mach/audio.h> 33#include <mach/audio.h>
36 34
37#include "../codecs/wm9712.h" 35#include "../codecs/wm9712.h"
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index a9d68fa2b34a..169ddad31575 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -419,7 +419,7 @@ static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
419 } 419 }
420} 420}
421 421
422static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK; 422static u64 s3c24xx_pcm_dmamask = DMA_BIT_MASK(32);
423 423
424static int s3c24xx_pcm_new(struct snd_card *card, 424static int s3c24xx_pcm_new(struct snd_card *card,
425 struct snd_soc_dai *dai, struct snd_pcm *pcm) 425 struct snd_soc_dai *dai, struct snd_pcm *pcm)
diff --git a/sound/soc/s6000/Kconfig b/sound/soc/s6000/Kconfig
new file mode 100644
index 000000000000..c74eb3d4a47c
--- /dev/null
+++ b/sound/soc/s6000/Kconfig
@@ -0,0 +1,19 @@
1config SND_S6000_SOC
2 tristate "SoC Audio for the Stretch s6000 family"
3 depends on XTENSA_VARIANT_S6000
4 help
5 Say Y or M if you want to add support for codecs attached to
6 s6000 family chips. You will also need to select the platform
7 to support below.
8
9config SND_S6000_SOC_I2S
10 tristate
11
12config SND_S6000_SOC_S6IPCAM
13 tristate "SoC Audio support for Stretch 6105 IP Camera"
14 depends on SND_S6000_SOC && XTENSA_PLATFORM_S6105
15 select SND_S6000_SOC_I2S
16 select SND_SOC_TLV320AIC3X
17 help
18 Say Y if you want to add support for SoC audio on the
19 Stretch s6105 IP Camera Reference Design.
diff --git a/sound/soc/s6000/Makefile b/sound/soc/s6000/Makefile
new file mode 100644
index 000000000000..7a613612e010
--- /dev/null
+++ b/sound/soc/s6000/Makefile
@@ -0,0 +1,11 @@
1# s6000 Platform Support
2snd-soc-s6000-objs := s6000-pcm.o
3snd-soc-s6000-i2s-objs := s6000-i2s.o
4
5obj-$(CONFIG_SND_S6000_SOC) += snd-soc-s6000.o
6obj-$(CONFIG_SND_S6000_SOC_I2S) += snd-soc-s6000-i2s.o
7
8# s6105 Machine Support
9snd-soc-s6ipcam-objs := s6105-ipcam.o
10
11obj-$(CONFIG_SND_S6000_SOC_S6IPCAM) += snd-soc-s6ipcam.o
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c
new file mode 100644
index 000000000000..c5cda187ecab
--- /dev/null
+++ b/sound/soc/s6000/s6000-i2s.c
@@ -0,0 +1,629 @@
1/*
2 * ALSA SoC I2S Audio Layer for the Stretch S6000 family
3 *
4 * Author: Daniel Gloeckner, <dg@emlix.com>
5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/device.h>
15#include <linux/delay.h>
16#include <linux/clk.h>
17#include <linux/interrupt.h>
18#include <linux/io.h>
19
20#include <sound/core.h>
21#include <sound/pcm.h>
22#include <sound/pcm_params.h>
23#include <sound/initval.h>
24#include <sound/soc.h>
25
26#include "s6000-i2s.h"
27#include "s6000-pcm.h"
28
29struct s6000_i2s_dev {
30 dma_addr_t sifbase;
31 u8 __iomem *scbbase;
32 unsigned int wide;
33 unsigned int channel_in;
34 unsigned int channel_out;
35 unsigned int lines_in;
36 unsigned int lines_out;
37 struct s6000_pcm_dma_params dma_params;
38};
39
40#define S6_I2S_INTERRUPT_STATUS 0x00
41#define S6_I2S_INT_OVERRUN 1
42#define S6_I2S_INT_UNDERRUN 2
43#define S6_I2S_INT_ALIGNMENT 4
44#define S6_I2S_INTERRUPT_ENABLE 0x04
45#define S6_I2S_INTERRUPT_RAW 0x08
46#define S6_I2S_INTERRUPT_CLEAR 0x0C
47#define S6_I2S_INTERRUPT_SET 0x10
48#define S6_I2S_MODE 0x20
49#define S6_I2S_DUAL 0
50#define S6_I2S_WIDE 1
51#define S6_I2S_TX_DEFAULT 0x24
52#define S6_I2S_DATA_CFG(c) (0x40 + 0x10 * (c))
53#define S6_I2S_IN 0
54#define S6_I2S_OUT 1
55#define S6_I2S_UNUSED 2
56#define S6_I2S_INTERFACE_CFG(c) (0x44 + 0x10 * (c))
57#define S6_I2S_DIV_MASK 0x001fff
58#define S6_I2S_16BIT 0x000000
59#define S6_I2S_20BIT 0x002000
60#define S6_I2S_24BIT 0x004000
61#define S6_I2S_32BIT 0x006000
62#define S6_I2S_BITS_MASK 0x006000
63#define S6_I2S_MEM_16BIT 0x000000
64#define S6_I2S_MEM_32BIT 0x008000
65#define S6_I2S_MEM_MASK 0x008000
66#define S6_I2S_CHANNELS_SHIFT 16
67#define S6_I2S_CHANNELS_MASK 0x030000
68#define S6_I2S_SCK_IN 0x000000
69#define S6_I2S_SCK_OUT 0x040000
70#define S6_I2S_SCK_DIR 0x040000
71#define S6_I2S_WS_IN 0x000000
72#define S6_I2S_WS_OUT 0x080000
73#define S6_I2S_WS_DIR 0x080000
74#define S6_I2S_LEFT_FIRST 0x000000
75#define S6_I2S_RIGHT_FIRST 0x100000
76#define S6_I2S_FIRST 0x100000
77#define S6_I2S_CUR_SCK 0x200000
78#define S6_I2S_CUR_WS 0x400000
79#define S6_I2S_ENABLE(c) (0x48 + 0x10 * (c))
80#define S6_I2S_DISABLE_IF 0x02
81#define S6_I2S_ENABLE_IF 0x03
82#define S6_I2S_IS_BUSY 0x04
83#define S6_I2S_DMA_ACTIVE 0x08
84#define S6_I2S_IS_ENABLED 0x10
85
86#define S6_I2S_NUM_LINES 4
87
88#define S6_I2S_SIF_PORT0 0x0000000
89#define S6_I2S_SIF_PORT1 0x0000080 /* docs say 0x0000010 */
90
91static inline void s6_i2s_write_reg(struct s6000_i2s_dev *dev, int reg, u32 val)
92{
93 writel(val, dev->scbbase + reg);
94}
95
96static inline u32 s6_i2s_read_reg(struct s6000_i2s_dev *dev, int reg)
97{
98 return readl(dev->scbbase + reg);
99}
100
101static inline void s6_i2s_mod_reg(struct s6000_i2s_dev *dev, int reg,
102 u32 mask, u32 val)
103{
104 val ^= s6_i2s_read_reg(dev, reg) & ~mask;
105 s6_i2s_write_reg(dev, reg, val);
106}
107
108static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel)
109{
110 int i, j, cur, prev;
111
112 /*
113 * Wait for WCLK to toggle 5 times before enabling the channel
114 * s6000 Family Datasheet 3.6.4:
115 * "At least two cycles of WS must occur between commands
116 * to disable or enable the interface"
117 */
118 j = 0;
119 prev = ~S6_I2S_CUR_WS;
120 for (i = 1000000; --i && j < 6; ) {
121 cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel))
122 & S6_I2S_CUR_WS;
123 if (prev != cur) {
124 prev = cur;
125 j++;
126 }
127 }
128 if (j < 6)
129 printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n");
130
131 s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF);
132}
133
134static void s6000_i2s_stop_channel(struct s6000_i2s_dev *dev, int channel)
135{
136 s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_DISABLE_IF);
137}
138
139static void s6000_i2s_start(struct snd_pcm_substream *substream)
140{
141 struct snd_soc_pcm_runtime *rtd = substream->private_data;
142 struct s6000_i2s_dev *dev = rtd->dai->cpu_dai->private_data;
143 int channel;
144
145 channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
146 dev->channel_out : dev->channel_in;
147
148 s6000_i2s_start_channel(dev, channel);
149}
150
151static void s6000_i2s_stop(struct snd_pcm_substream *substream)
152{
153 struct snd_soc_pcm_runtime *rtd = substream->private_data;
154 struct s6000_i2s_dev *dev = rtd->dai->cpu_dai->private_data;
155 int channel;
156
157 channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
158 dev->channel_out : dev->channel_in;
159
160 s6000_i2s_stop_channel(dev, channel);
161}
162
163static int s6000_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
164 int after)
165{
166 switch (cmd) {
167 case SNDRV_PCM_TRIGGER_START:
168 case SNDRV_PCM_TRIGGER_RESUME:
169 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
170 if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ^ !after)
171 s6000_i2s_start(substream);
172 break;
173 case SNDRV_PCM_TRIGGER_STOP:
174 case SNDRV_PCM_TRIGGER_SUSPEND:
175 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
176 if (!after)
177 s6000_i2s_stop(substream);
178 }
179 return 0;
180}
181
182static unsigned int s6000_i2s_int_sources(struct s6000_i2s_dev *dev)
183{
184 unsigned int pending;
185 pending = s6_i2s_read_reg(dev, S6_I2S_INTERRUPT_RAW);
186 pending &= S6_I2S_INT_ALIGNMENT |
187 S6_I2S_INT_UNDERRUN |
188 S6_I2S_INT_OVERRUN;
189 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, pending);
190
191 return pending;
192}
193
194static unsigned int s6000_i2s_check_xrun(struct snd_soc_dai *cpu_dai)
195{
196 struct s6000_i2s_dev *dev = cpu_dai->private_data;
197 unsigned int errors;
198 unsigned int ret;
199
200 errors = s6000_i2s_int_sources(dev);
201 if (likely(!errors))
202 return 0;
203
204 ret = 0;
205 if (errors & S6_I2S_INT_ALIGNMENT)
206 printk(KERN_ERR "s6000-i2s: WCLK misaligned\n");
207 if (errors & S6_I2S_INT_UNDERRUN)
208 ret |= 1 << SNDRV_PCM_STREAM_PLAYBACK;
209 if (errors & S6_I2S_INT_OVERRUN)
210 ret |= 1 << SNDRV_PCM_STREAM_CAPTURE;
211 return ret;
212}
213
214static void s6000_i2s_wait_disabled(struct s6000_i2s_dev *dev)
215{
216 int channel;
217 int n = 50;
218 for (channel = 0; channel < 2; channel++) {
219 while (--n >= 0) {
220 int v = s6_i2s_read_reg(dev, S6_I2S_ENABLE(channel));
221 if ((v & S6_I2S_IS_ENABLED)
222 || !(v & (S6_I2S_DMA_ACTIVE | S6_I2S_IS_BUSY)))
223 break;
224 udelay(20);
225 }
226 }
227 if (n < 0)
228 printk(KERN_WARNING "s6000-i2s: timeout disabling interfaces");
229}
230
231static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
232 unsigned int fmt)
233{
234 struct s6000_i2s_dev *dev = cpu_dai->private_data;
235 u32 w;
236
237 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
238 case SND_SOC_DAIFMT_CBM_CFM:
239 w = S6_I2S_SCK_IN | S6_I2S_WS_IN;
240 break;
241 case SND_SOC_DAIFMT_CBS_CFM:
242 w = S6_I2S_SCK_OUT | S6_I2S_WS_IN;
243 break;
244 case SND_SOC_DAIFMT_CBM_CFS:
245 w = S6_I2S_SCK_IN | S6_I2S_WS_OUT;
246 break;
247 case SND_SOC_DAIFMT_CBS_CFS:
248 w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT;
249 break;
250 default:
251 return -EINVAL;
252 }
253
254 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
255 case SND_SOC_DAIFMT_NB_NF:
256 w |= S6_I2S_LEFT_FIRST;
257 break;
258 case SND_SOC_DAIFMT_NB_IF:
259 w |= S6_I2S_RIGHT_FIRST;
260 break;
261 default:
262 return -EINVAL;
263 }
264
265 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0),
266 S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
267 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1),
268 S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
269
270 return 0;
271}
272
273static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
274{
275 struct s6000_i2s_dev *dev = dai->private_data;
276
277 if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2)
278 return -EINVAL;
279
280 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id),
281 S6_I2S_DIV_MASK, div / 2 - 1);
282 return 0;
283}
284
285static int s6000_i2s_hw_params(struct snd_pcm_substream *substream,
286 struct snd_pcm_hw_params *params,
287 struct snd_soc_dai *dai)
288{
289 struct s6000_i2s_dev *dev = dai->private_data;
290 int interf;
291 u32 w = 0;
292
293 if (dev->wide)
294 interf = 0;
295 else {
296 w |= (((params_channels(params) - 2) / 2)
297 << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK;
298 interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
299 ? dev->channel_out : dev->channel_in;
300 }
301
302 switch (params_format(params)) {
303 case SNDRV_PCM_FORMAT_S16_LE:
304 w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT;
305 break;
306 case SNDRV_PCM_FORMAT_S32_LE:
307 w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT;
308 break;
309 default:
310 printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n",
311 params_format(params));
312 return -EINVAL;
313 }
314
315 if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf))
316 & S6_I2S_IS_ENABLED) {
317 printk(KERN_ERR "s6000-i2s: interface already enabled\n");
318 return -EBUSY;
319 }
320
321 s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf),
322 S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK,
323 w);
324
325 return 0;
326}
327
328static int s6000_i2s_dai_probe(struct platform_device *pdev,
329 struct snd_soc_dai *dai)
330{
331 struct s6000_i2s_dev *dev = dai->private_data;
332 struct s6000_snd_platform_data *pdata = pdev->dev.platform_data;
333
334 if (!pdata)
335 return -EINVAL;
336
337 dev->wide = pdata->wide;
338 dev->channel_in = pdata->channel_in;
339 dev->channel_out = pdata->channel_out;
340 dev->lines_in = pdata->lines_in;
341 dev->lines_out = pdata->lines_out;
342
343 s6_i2s_write_reg(dev, S6_I2S_MODE,
344 dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL);
345
346 if (dev->wide) {
347 int i;
348
349 if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES)
350 return -EINVAL;
351
352 dev->channel_in = 0;
353 dev->channel_out = 1;
354 dai->capture.channels_min = 2 * dev->lines_in;
355 dai->capture.channels_max = dai->capture.channels_min;
356 dai->playback.channels_min = 2 * dev->lines_out;
357 dai->playback.channels_max = dai->playback.channels_min;
358
359 for (i = 0; i < dev->lines_out; i++)
360 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT);
361
362 for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++)
363 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i),
364 S6_I2S_UNUSED);
365
366 for (; i < S6_I2S_NUM_LINES; i++)
367 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN);
368 } else {
369 unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED};
370
371 if (dev->lines_in > 1 || dev->lines_out > 1)
372 return -EINVAL;
373
374 dai->capture.channels_min = 2 * dev->lines_in;
375 dai->capture.channels_max = 8 * dev->lines_in;
376 dai->playback.channels_min = 2 * dev->lines_out;
377 dai->playback.channels_max = 8 * dev->lines_out;
378
379 if (dev->lines_in)
380 cfg[dev->channel_in] = S6_I2S_IN;
381 if (dev->lines_out)
382 cfg[dev->channel_out] = S6_I2S_OUT;
383
384 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]);
385 s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]);
386 }
387
388 if (dev->lines_out) {
389 if (dev->lines_in) {
390 if (!dev->dma_params.dma_out)
391 return -ENODEV;
392 } else {
393 dev->dma_params.dma_out = dev->dma_params.dma_in;
394 dev->dma_params.dma_in = 0;
395 }
396 }
397 dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ?
398 S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
399 dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ?
400 S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
401 dev->dma_params.same_rate = pdata->same_rate | pdata->wide;
402 return 0;
403}
404
405#define S6000_I2S_RATES (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
406 SNDRV_PCM_RATE_8000_192000)
407#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
408
409static struct snd_soc_dai_ops s6000_i2s_dai_ops = {
410 .set_fmt = s6000_i2s_set_dai_fmt,
411 .set_clkdiv = s6000_i2s_set_clkdiv,
412 .hw_params = s6000_i2s_hw_params,
413};
414
415struct snd_soc_dai s6000_i2s_dai = {
416 .name = "s6000-i2s",
417 .id = 0,
418 .probe = s6000_i2s_dai_probe,
419 .playback = {
420 .channels_min = 2,
421 .channels_max = 8,
422 .formats = S6000_I2S_FORMATS,
423 .rates = S6000_I2S_RATES,
424 .rate_min = 0,
425 .rate_max = 1562500,
426 },
427 .capture = {
428 .channels_min = 2,
429 .channels_max = 8,
430 .formats = S6000_I2S_FORMATS,
431 .rates = S6000_I2S_RATES,
432 .rate_min = 0,
433 .rate_max = 1562500,
434 },
435 .ops = &s6000_i2s_dai_ops,
436}
437EXPORT_SYMBOL_GPL(s6000_i2s_dai);
438
439static int __devinit s6000_i2s_probe(struct platform_device *pdev)
440{
441 struct s6000_i2s_dev *dev;
442 struct resource *scbmem, *sifmem, *region, *dma1, *dma2;
443 u8 __iomem *mmio;
444 int ret;
445
446 scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
447 if (!scbmem) {
448 dev_err(&pdev->dev, "no mem resource?\n");
449 ret = -ENODEV;
450 goto err_release_none;
451 }
452
453 region = request_mem_region(scbmem->start,
454 scbmem->end - scbmem->start + 1,
455 pdev->name);
456 if (!region) {
457 dev_err(&pdev->dev, "I2S SCB region already claimed\n");
458 ret = -EBUSY;
459 goto err_release_none;
460 }
461
462 mmio = ioremap(scbmem->start, scbmem->end - scbmem->start + 1);
463 if (!mmio) {
464 dev_err(&pdev->dev, "can't ioremap SCB region\n");
465 ret = -ENOMEM;
466 goto err_release_scb;
467 }
468
469 sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
470 if (!sifmem) {
471 dev_err(&pdev->dev, "no second mem resource?\n");
472 ret = -ENODEV;
473 goto err_release_map;
474 }
475
476 region = request_mem_region(sifmem->start,
477 sifmem->end - sifmem->start + 1,
478 pdev->name);
479 if (!region) {
480 dev_err(&pdev->dev, "I2S SIF region already claimed\n");
481 ret = -EBUSY;
482 goto err_release_map;
483 }
484
485 dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0);
486 if (!dma1) {
487 dev_err(&pdev->dev, "no dma resource?\n");
488 ret = -ENODEV;
489 goto err_release_sif;
490 }
491
492 region = request_mem_region(dma1->start, dma1->end - dma1->start + 1,
493 pdev->name);
494 if (!region) {
495 dev_err(&pdev->dev, "I2S DMA region already claimed\n");
496 ret = -EBUSY;
497 goto err_release_sif;
498 }
499
500 dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1);
501 if (dma2) {
502 region = request_mem_region(dma2->start,
503 dma2->end - dma2->start + 1,
504 pdev->name);
505 if (!region) {
506 dev_err(&pdev->dev,
507 "I2S DMA region already claimed\n");
508 ret = -EBUSY;
509 goto err_release_dma1;
510 }
511 }
512
513 dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL);
514 if (!dev) {
515 ret = -ENOMEM;
516 goto err_release_dma2;
517 }
518
519 s6000_i2s_dai.dev = &pdev->dev;
520 s6000_i2s_dai.private_data = dev;
521 s6000_i2s_dai.dma_data = &dev->dma_params;
522
523 dev->sifbase = sifmem->start;
524 dev->scbbase = mmio;
525
526 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
527 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR,
528 S6_I2S_INT_ALIGNMENT |
529 S6_I2S_INT_UNDERRUN |
530 S6_I2S_INT_OVERRUN);
531
532 s6000_i2s_stop_channel(dev, 0);
533 s6000_i2s_stop_channel(dev, 1);
534 s6000_i2s_wait_disabled(dev);
535
536 dev->dma_params.check_xrun = s6000_i2s_check_xrun;
537 dev->dma_params.trigger = s6000_i2s_trigger;
538 dev->dma_params.dma_in = dma1->start;
539 dev->dma_params.dma_out = dma2 ? dma2->start : 0;
540 dev->dma_params.irq = platform_get_irq(pdev, 0);
541 if (dev->dma_params.irq < 0) {
542 dev_err(&pdev->dev, "no irq resource?\n");
543 ret = -ENODEV;
544 goto err_release_dev;
545 }
546
547 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE,
548 S6_I2S_INT_ALIGNMENT |
549 S6_I2S_INT_UNDERRUN |
550 S6_I2S_INT_OVERRUN);
551
552 ret = snd_soc_register_dai(&s6000_i2s_dai);
553 if (ret)
554 goto err_release_dev;
555
556 return 0;
557
558err_release_dev:
559 kfree(dev);
560err_release_dma2:
561 if (dma2)
562 release_mem_region(dma2->start, dma2->end - dma2->start + 1);
563err_release_dma1:
564 release_mem_region(dma1->start, dma1->end - dma1->start + 1);
565err_release_sif:
566 release_mem_region(sifmem->start, (sifmem->end - sifmem->start) + 1);
567err_release_map:
568 iounmap(mmio);
569err_release_scb:
570 release_mem_region(scbmem->start, (scbmem->end - scbmem->start) + 1);
571err_release_none:
572 return ret;
573}
574
575static void __devexit s6000_i2s_remove(struct platform_device *pdev)
576{
577 struct s6000_i2s_dev *dev = s6000_i2s_dai.private_data;
578 struct resource *region;
579 void __iomem *mmio = dev->scbbase;
580
581 snd_soc_unregister_dai(&s6000_i2s_dai);
582
583 s6000_i2s_stop_channel(dev, 0);
584 s6000_i2s_stop_channel(dev, 1);
585
586 s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
587 s6000_i2s_dai.private_data = 0;
588 kfree(dev);
589
590 region = platform_get_resource(pdev, IORESOURCE_DMA, 0);
591 release_mem_region(region->start, region->end - region->start + 1);
592
593 region = platform_get_resource(pdev, IORESOURCE_DMA, 1);
594 if (region)
595 release_mem_region(region->start,
596 region->end - region->start + 1);
597
598 region = platform_get_resource(pdev, IORESOURCE_MEM, 0);
599 release_mem_region(region->start, (region->end - region->start) + 1);
600
601 iounmap(mmio);
602 region = platform_get_resource(pdev, IORESOURCE_IO, 0);
603 release_mem_region(region->start, (region->end - region->start) + 1);
604}
605
606static struct platform_driver s6000_i2s_driver = {
607 .probe = s6000_i2s_probe,
608 .remove = __devexit_p(s6000_i2s_remove),
609 .driver = {
610 .name = "s6000-i2s",
611 .owner = THIS_MODULE,
612 },
613};
614
615static int __init s6000_i2s_init(void)
616{
617 return platform_driver_register(&s6000_i2s_driver);
618}
619module_init(s6000_i2s_init);
620
621static void __exit s6000_i2s_exit(void)
622{
623 platform_driver_unregister(&s6000_i2s_driver);
624}
625module_exit(s6000_i2s_exit);
626
627MODULE_AUTHOR("Daniel Gloeckner");
628MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface");
629MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-i2s.h b/sound/soc/s6000/s6000-i2s.h
new file mode 100644
index 000000000000..2375fdfe6dba
--- /dev/null
+++ b/sound/soc/s6000/s6000-i2s.h
@@ -0,0 +1,25 @@
1/*
2 * ALSA SoC I2S Audio Layer for the Stretch s6000 family
3 *
4 * Author: Daniel Gloeckner, <dg@emlix.com>
5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#ifndef _S6000_I2S_H
13#define _S6000_I2S_H
14
15extern struct snd_soc_dai s6000_i2s_dai;
16
17struct s6000_snd_platform_data {
18 int lines_in;
19 int lines_out;
20 int channel_in;
21 int channel_out;
22 int wide;
23 int same_rate;
24};
25#endif
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c
new file mode 100644
index 000000000000..83b8028e209d
--- /dev/null
+++ b/sound/soc/s6000/s6000-pcm.c
@@ -0,0 +1,497 @@
1/*
2 * ALSA PCM interface for the Stetch s6000 family
3 *
4 * Author: Daniel Gloeckner, <dg@emlix.com>
5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
16#include <linux/dma-mapping.h>
17#include <linux/interrupt.h>
18
19#include <sound/core.h>
20#include <sound/pcm.h>
21#include <sound/pcm_params.h>
22#include <sound/soc.h>
23
24#include <asm/dma.h>
25#include <variant/dmac.h>
26
27#include "s6000-pcm.h"
28
29#define S6_PCM_PREALLOCATE_SIZE (96 * 1024)
30#define S6_PCM_PREALLOCATE_MAX (2048 * 1024)
31
32static struct snd_pcm_hardware s6000_pcm_hardware = {
33 .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
34 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
35 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX),
36 .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
37 .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
38 SNDRV_PCM_RATE_8000_192000),
39 .rate_min = 0,
40 .rate_max = 1562500,
41 .channels_min = 2,
42 .channels_max = 8,
43 .buffer_bytes_max = 0x7ffffff0,
44 .period_bytes_min = 16,
45 .period_bytes_max = 0xfffff0,
46 .periods_min = 2,
47 .periods_max = 1024, /* no limit */
48 .fifo_size = 0,
49};
50
51struct s6000_runtime_data {
52 spinlock_t lock;
53 int period; /* current DMA period */
54};
55
56static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream)
57{
58 struct snd_pcm_runtime *runtime = substream->runtime;
59 struct s6000_runtime_data *prtd = runtime->private_data;
60 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
61 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
62 int channel;
63 unsigned int period_size;
64 unsigned int dma_offset;
65 dma_addr_t dma_pos;
66 dma_addr_t src, dst;
67
68 period_size = snd_pcm_lib_period_bytes(substream);
69 dma_offset = prtd->period * period_size;
70 dma_pos = runtime->dma_addr + dma_offset;
71
72 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
73 src = dma_pos;
74 dst = par->sif_out;
75 channel = par->dma_out;
76 } else {
77 src = par->sif_in;
78 dst = dma_pos;
79 channel = par->dma_in;
80 }
81
82 if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
83 DMA_INDEX_CHNL(channel)))
84 return;
85
86 if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
87 printk(KERN_ERR "s6000-pcm: fifo full\n");
88 return;
89 }
90
91 BUG_ON(period_size & 15);
92 s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
93 src, dst, period_size);
94
95 prtd->period++;
96 if (unlikely(prtd->period >= runtime->periods))
97 prtd->period = 0;
98}
99
100static irqreturn_t s6000_pcm_irq(int irq, void *data)
101{
102 struct snd_pcm *pcm = data;
103 struct snd_soc_pcm_runtime *runtime = pcm->private_data;
104 struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
105 struct s6000_runtime_data *prtd;
106 unsigned int has_xrun;
107 int i, ret = IRQ_NONE;
108 u32 channel[2] = {
109 [SNDRV_PCM_STREAM_PLAYBACK] = params->dma_out,
110 [SNDRV_PCM_STREAM_CAPTURE] = params->dma_in
111 };
112
113 has_xrun = params->check_xrun(runtime->dai->cpu_dai);
114
115 for (i = 0; i < ARRAY_SIZE(channel); ++i) {
116 struct snd_pcm_substream *substream = pcm->streams[i].substream;
117 unsigned int pending;
118
119 if (!channel[i])
120 continue;
121
122 if (unlikely(has_xrun & (1 << i)) &&
123 substream->runtime &&
124 snd_pcm_running(substream)) {
125 dev_dbg(pcm->dev, "xrun\n");
126 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
127 ret = IRQ_HANDLED;
128 }
129
130 pending = s6dmac_int_sources(DMA_MASK_DMAC(channel[i]),
131 DMA_INDEX_CHNL(channel[i]));
132
133 if (pending & 1) {
134 ret = IRQ_HANDLED;
135 if (likely(substream->runtime &&
136 snd_pcm_running(substream))) {
137 snd_pcm_period_elapsed(substream);
138 dev_dbg(pcm->dev, "period elapsed %x %x\n",
139 s6dmac_cur_src(DMA_MASK_DMAC(channel[i]),
140 DMA_INDEX_CHNL(channel[i])),
141 s6dmac_cur_dst(DMA_MASK_DMAC(channel[i]),
142 DMA_INDEX_CHNL(channel[i])));
143 prtd = substream->runtime->private_data;
144 spin_lock(&prtd->lock);
145 s6000_pcm_enqueue_dma(substream);
146 spin_unlock(&prtd->lock);
147 }
148 }
149
150 if (unlikely(pending & ~7)) {
151 if (pending & (1 << 3))
152 printk(KERN_WARNING
153 "s6000-pcm: DMA %x Underflow\n",
154 channel[i]);
155 if (pending & (1 << 4))
156 printk(KERN_WARNING
157 "s6000-pcm: DMA %x Overflow\n",
158 channel[i]);
159 if (pending & 0x1e0)
160 printk(KERN_WARNING
161 "s6000-pcm: DMA %x Master Error "
162 "(mask %x)\n",
163 channel[i], pending >> 5);
164
165 }
166 }
167
168 return ret;
169}
170
171static int s6000_pcm_start(struct snd_pcm_substream *substream)
172{
173 struct s6000_runtime_data *prtd = substream->runtime->private_data;
174 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
175 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
176 unsigned long flags;
177 int srcinc;
178 u32 dma;
179
180 spin_lock_irqsave(&prtd->lock, flags);
181
182 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
183 srcinc = 1;
184 dma = par->dma_out;
185 } else {
186 srcinc = 0;
187 dma = par->dma_in;
188 }
189 s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma),
190 1 /* priority 1 (0 is max) */,
191 0 /* peripheral requests w/o xfer length mode */,
192 srcinc /* source address increment */,
193 srcinc^1 /* destination address increment */,
194 0 /* chunksize 0 (skip impossible on this dma) */,
195 0 /* source skip after chunk (impossible) */,
196 0 /* destination skip after chunk (impossible) */,
197 4 /* 16 byte burst size */,
198 -1 /* don't conserve bandwidth */,
199 0 /* low watermark irq descriptor theshold */,
200 0 /* disable hardware timestamps */,
201 1 /* enable channel */);
202
203 s6000_pcm_enqueue_dma(substream);
204 s6000_pcm_enqueue_dma(substream);
205
206 spin_unlock_irqrestore(&prtd->lock, flags);
207
208 return 0;
209}
210
211static int s6000_pcm_stop(struct snd_pcm_substream *substream)
212{
213 struct s6000_runtime_data *prtd = substream->runtime->private_data;
214 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
215 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
216 unsigned long flags;
217 u32 channel;
218
219 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
220 channel = par->dma_out;
221 else
222 channel = par->dma_in;
223
224 s6dmac_set_terminal_count(DMA_MASK_DMAC(channel),
225 DMA_INDEX_CHNL(channel), 0);
226
227 spin_lock_irqsave(&prtd->lock, flags);
228
229 s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel));
230
231 spin_unlock_irqrestore(&prtd->lock, flags);
232
233 return 0;
234}
235
236static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
237{
238 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
239 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
240 int ret;
241
242 ret = par->trigger(substream, cmd, 0);
243 if (ret < 0)
244 return ret;
245
246 switch (cmd) {
247 case SNDRV_PCM_TRIGGER_START:
248 case SNDRV_PCM_TRIGGER_RESUME:
249 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250 ret = s6000_pcm_start(substream);
251 break;
252 case SNDRV_PCM_TRIGGER_STOP:
253 case SNDRV_PCM_TRIGGER_SUSPEND:
254 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
255 ret = s6000_pcm_stop(substream);
256 break;
257 default:
258 ret = -EINVAL;
259 }
260 if (ret < 0)
261 return ret;
262
263 return par->trigger(substream, cmd, 1);
264}
265
266static int s6000_pcm_prepare(struct snd_pcm_substream *substream)
267{
268 struct s6000_runtime_data *prtd = substream->runtime->private_data;
269
270 prtd->period = 0;
271
272 return 0;
273}
274
275static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream)
276{
277 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
278 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
279 struct snd_pcm_runtime *runtime = substream->runtime;
280 struct s6000_runtime_data *prtd = runtime->private_data;
281 unsigned long flags;
282 unsigned int offset;
283 dma_addr_t count;
284
285 spin_lock_irqsave(&prtd->lock, flags);
286
287 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
288 count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out),
289 DMA_INDEX_CHNL(par->dma_out));
290 else
291 count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in),
292 DMA_INDEX_CHNL(par->dma_in));
293
294 count -= runtime->dma_addr;
295
296 spin_unlock_irqrestore(&prtd->lock, flags);
297
298 offset = bytes_to_frames(runtime, count);
299 if (unlikely(offset >= runtime->buffer_size))
300 offset = 0;
301
302 return offset;
303}
304
305static int s6000_pcm_open(struct snd_pcm_substream *substream)
306{
307 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
308 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
309 struct snd_pcm_runtime *runtime = substream->runtime;
310 struct s6000_runtime_data *prtd;
311 int ret;
312
313 snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware);
314
315 ret = snd_pcm_hw_constraint_step(runtime, 0,
316 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
317 if (ret < 0)
318 return ret;
319 ret = snd_pcm_hw_constraint_step(runtime, 0,
320 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
321 if (ret < 0)
322 return ret;
323 ret = snd_pcm_hw_constraint_integer(runtime,
324 SNDRV_PCM_HW_PARAM_PERIODS);
325 if (ret < 0)
326 return ret;
327
328 if (par->same_rate) {
329 int rate;
330 spin_lock(&par->lock); /* needed? */
331 rate = par->rate;
332 spin_unlock(&par->lock);
333 if (rate != -1) {
334 ret = snd_pcm_hw_constraint_minmax(runtime,
335 SNDRV_PCM_HW_PARAM_RATE,
336 rate, rate);
337 if (ret < 0)
338 return ret;
339 }
340 }
341
342 prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
343 if (prtd == NULL)
344 return -ENOMEM;
345
346 spin_lock_init(&prtd->lock);
347
348 runtime->private_data = prtd;
349
350 return 0;
351}
352
353static int s6000_pcm_close(struct snd_pcm_substream *substream)
354{
355 struct snd_pcm_runtime *runtime = substream->runtime;
356 struct s6000_runtime_data *prtd = runtime->private_data;
357
358 kfree(prtd);
359
360 return 0;
361}
362
363static int s6000_pcm_hw_params(struct snd_pcm_substream *substream,
364 struct snd_pcm_hw_params *hw_params)
365{
366 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
367 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
368 int ret;
369 ret = snd_pcm_lib_malloc_pages(substream,
370 params_buffer_bytes(hw_params));
371 if (ret < 0) {
372 printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
373 return ret;
374 }
375
376 if (par->same_rate) {
377 spin_lock(&par->lock);
378 if (par->rate == -1 ||
379 !(par->in_use & ~(1 << substream->stream))) {
380 par->rate = params_rate(hw_params);
381 par->in_use |= 1 << substream->stream;
382 } else if (params_rate(hw_params) != par->rate) {
383 snd_pcm_lib_free_pages(substream);
384 par->in_use &= ~(1 << substream->stream);
385 ret = -EBUSY;
386 }
387 spin_unlock(&par->lock);
388 }
389 return ret;
390}
391
392static int s6000_pcm_hw_free(struct snd_pcm_substream *substream)
393{
394 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
395 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
396
397 spin_lock(&par->lock);
398 par->in_use &= ~(1 << substream->stream);
399 if (!par->in_use)
400 par->rate = -1;
401 spin_unlock(&par->lock);
402
403 return snd_pcm_lib_free_pages(substream);
404}
405
406static struct snd_pcm_ops s6000_pcm_ops = {
407 .open = s6000_pcm_open,
408 .close = s6000_pcm_close,
409 .ioctl = snd_pcm_lib_ioctl,
410 .hw_params = s6000_pcm_hw_params,
411 .hw_free = s6000_pcm_hw_free,
412 .trigger = s6000_pcm_trigger,
413 .prepare = s6000_pcm_prepare,
414 .pointer = s6000_pcm_pointer,
415};
416
417static void s6000_pcm_free(struct snd_pcm *pcm)
418{
419 struct snd_soc_pcm_runtime *runtime = pcm->private_data;
420 struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
421
422 free_irq(params->irq, pcm);
423 snd_pcm_lib_preallocate_free_for_all(pcm);
424}
425
426static u64 s6000_pcm_dmamask = DMA_32BIT_MASK;
427
428static int s6000_pcm_new(struct snd_card *card,
429 struct snd_soc_dai *dai, struct snd_pcm *pcm)
430{
431 struct snd_soc_pcm_runtime *runtime = pcm->private_data;
432 struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
433 int res;
434
435 if (!card->dev->dma_mask)
436 card->dev->dma_mask = &s6000_pcm_dmamask;
437 if (!card->dev->coherent_dma_mask)
438 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
439
440 if (params->dma_in) {
441 s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
442 DMA_INDEX_CHNL(params->dma_in));
443 s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in),
444 DMA_INDEX_CHNL(params->dma_in));
445 }
446
447 if (params->dma_out) {
448 s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out),
449 DMA_INDEX_CHNL(params->dma_out));
450 s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out),
451 DMA_INDEX_CHNL(params->dma_out));
452 }
453
454 res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED,
455 s6000_soc_platform.name, pcm);
456 if (res) {
457 printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
458 return res;
459 }
460
461 res = snd_pcm_lib_preallocate_pages_for_all(pcm,
462 SNDRV_DMA_TYPE_DEV,
463 card->dev,
464 S6_PCM_PREALLOCATE_SIZE,
465 S6_PCM_PREALLOCATE_MAX);
466 if (res)
467 printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
468
469 spin_lock_init(&params->lock);
470 params->in_use = 0;
471 params->rate = -1;
472 return 0;
473}
474
475struct snd_soc_platform s6000_soc_platform = {
476 .name = "s6000-audio",
477 .pcm_ops = &s6000_pcm_ops,
478 .pcm_new = s6000_pcm_new,
479 .pcm_free = s6000_pcm_free,
480};
481EXPORT_SYMBOL_GPL(s6000_soc_platform);
482
483static int __init s6000_pcm_init(void)
484{
485 return snd_soc_register_platform(&s6000_soc_platform);
486}
487module_init(s6000_pcm_init);
488
489static void __exit s6000_pcm_exit(void)
490{
491 snd_soc_unregister_platform(&s6000_soc_platform);
492}
493module_exit(s6000_pcm_exit);
494
495MODULE_AUTHOR("Daniel Gloeckner");
496MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
497MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-pcm.h b/sound/soc/s6000/s6000-pcm.h
new file mode 100644
index 000000000000..96f23f6f52bf
--- /dev/null
+++ b/sound/soc/s6000/s6000-pcm.h
@@ -0,0 +1,35 @@
1/*
2 * ALSA PCM interface for the Stretch s6000 family
3 *
4 * Author: Daniel Gloeckner, <dg@emlix.com>
5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#ifndef _S6000_PCM_H
13#define _S6000_PCM_H
14
15struct snd_soc_dai;
16struct snd_pcm_substream;
17
18struct s6000_pcm_dma_params {
19 unsigned int (*check_xrun)(struct snd_soc_dai *cpu_dai);
20 int (*trigger)(struct snd_pcm_substream *substream, int cmd, int after);
21 dma_addr_t sif_in;
22 dma_addr_t sif_out;
23 u32 dma_in;
24 u32 dma_out;
25 int irq;
26 int same_rate;
27
28 spinlock_t lock;
29 int in_use;
30 int rate;
31};
32
33extern struct snd_soc_platform s6000_soc_platform;
34
35#endif
diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c
new file mode 100644
index 000000000000..b5f95f9781c1
--- /dev/null
+++ b/sound/soc/s6000/s6105-ipcam.c
@@ -0,0 +1,244 @@
1/*
2 * ASoC driver for Stretch s6105 IP camera platform
3 *
4 * Author: Daniel Gloeckner, <dg@emlix.com>
5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/timer.h>
15#include <linux/interrupt.h>
16#include <linux/platform_device.h>
17#include <sound/core.h>
18#include <sound/pcm.h>
19#include <sound/soc.h>
20#include <sound/soc-dapm.h>
21
22#include <variant/dmac.h>
23
24#include "../codecs/tlv320aic3x.h"
25#include "s6000-pcm.h"
26#include "s6000-i2s.h"
27
28#define S6105_CAM_CODEC_CLOCK 12288000
29
30static int s6105_hw_params(struct snd_pcm_substream *substream,
31 struct snd_pcm_hw_params *params)
32{
33 struct snd_soc_pcm_runtime *rtd = substream->private_data;
34 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
35 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
36 int ret = 0;
37
38 /* set codec DAI configuration */
39 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
40 SND_SOC_DAIFMT_CBM_CFM);
41 if (ret < 0)
42 return ret;
43
44 /* set cpu DAI configuration */
45 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
46 SND_SOC_DAIFMT_NB_NF);
47 if (ret < 0)
48 return ret;
49
50 /* set the codec system clock */
51 ret = snd_soc_dai_set_sysclk(codec_dai, 0, S6105_CAM_CODEC_CLOCK,
52 SND_SOC_CLOCK_OUT);
53 if (ret < 0)
54 return ret;
55
56 return 0;
57}
58
59static struct snd_soc_ops s6105_ops = {
60 .hw_params = s6105_hw_params,
61};
62
63/* s6105 machine dapm widgets */
64static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
65 SND_SOC_DAPM_LINE("Audio Out Differential", NULL),
66 SND_SOC_DAPM_LINE("Audio Out Stereo", NULL),
67 SND_SOC_DAPM_LINE("Audio In", NULL),
68};
69
70/* s6105 machine audio_mapnections to the codec pins */
71static const struct snd_soc_dapm_route audio_map[] = {
72 /* Audio Out connected to HPLOUT, HPLCOM, HPROUT */
73 {"Audio Out Differential", NULL, "HPLOUT"},
74 {"Audio Out Differential", NULL, "HPLCOM"},
75 {"Audio Out Stereo", NULL, "HPLOUT"},
76 {"Audio Out Stereo", NULL, "HPROUT"},
77
78 /* Audio In connected to LINE1L, LINE1R */
79 {"LINE1L", NULL, "Audio In"},
80 {"LINE1R", NULL, "Audio In"},
81};
82
83static int output_type_info(struct snd_kcontrol *kcontrol,
84 struct snd_ctl_elem_info *uinfo)
85{
86 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
87 uinfo->count = 1;
88 uinfo->value.enumerated.items = 2;
89 if (uinfo->value.enumerated.item) {
90 uinfo->value.enumerated.item = 1;
91 strcpy(uinfo->value.enumerated.name, "HPLOUT/HPROUT");
92 } else {
93 strcpy(uinfo->value.enumerated.name, "HPLOUT/HPLCOM");
94 }
95 return 0;
96}
97
98static int output_type_get(struct snd_kcontrol *kcontrol,
99 struct snd_ctl_elem_value *ucontrol)
100{
101 ucontrol->value.enumerated.item[0] = kcontrol->private_value;
102 return 0;
103}
104
105static int output_type_put(struct snd_kcontrol *kcontrol,
106 struct snd_ctl_elem_value *ucontrol)
107{
108 struct snd_soc_codec *codec = kcontrol->private_data;
109 unsigned int val = (ucontrol->value.enumerated.item[0] != 0);
110 char *differential = "Audio Out Differential";
111 char *stereo = "Audio Out Stereo";
112
113 if (kcontrol->private_value == val)
114 return 0;
115 kcontrol->private_value = val;
116 snd_soc_dapm_disable_pin(codec, val ? differential : stereo);
117 snd_soc_dapm_sync(codec);
118 snd_soc_dapm_enable_pin(codec, val ? stereo : differential);
119 snd_soc_dapm_sync(codec);
120
121 return 1;
122}
123
124static const struct snd_kcontrol_new audio_out_mux = {
125 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
126 .name = "Master Output Mux",
127 .index = 0,
128 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
129 .info = output_type_info,
130 .get = output_type_get,
131 .put = output_type_put,
132 .private_value = 1 /* default to stereo */
133};
134
135/* Logic for a aic3x as connected on the s6105 ip camera ref design */
136static int s6105_aic3x_init(struct snd_soc_codec *codec)
137{
138 /* Add s6105 specific widgets */
139 snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
140 ARRAY_SIZE(aic3x_dapm_widgets));
141
142 /* Set up s6105 specific audio path audio_map */
143 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
144
145 /* not present */
146 snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
147 snd_soc_dapm_nc_pin(codec, "LINE2L");
148 snd_soc_dapm_nc_pin(codec, "LINE2R");
149
150 /* not connected */
151 snd_soc_dapm_nc_pin(codec, "MIC3L"); /* LINE2L on this chip */
152 snd_soc_dapm_nc_pin(codec, "MIC3R"); /* LINE2R on this chip */
153 snd_soc_dapm_nc_pin(codec, "LLOUT");
154 snd_soc_dapm_nc_pin(codec, "RLOUT");
155 snd_soc_dapm_nc_pin(codec, "HPRCOM");
156
157 /* always connected */
158 snd_soc_dapm_enable_pin(codec, "Audio In");
159
160 /* must correspond to audio_out_mux.private_value initializer */
161 snd_soc_dapm_disable_pin(codec, "Audio Out Differential");
162 snd_soc_dapm_sync(codec);
163 snd_soc_dapm_enable_pin(codec, "Audio Out Stereo");
164
165 snd_soc_dapm_sync(codec);
166
167 snd_ctl_add(codec->card, snd_ctl_new1(&audio_out_mux, codec));
168
169 return 0;
170}
171
172/* s6105 digital audio interface glue - connects codec <--> CPU */
173static struct snd_soc_dai_link s6105_dai = {
174 .name = "TLV320AIC31",
175 .stream_name = "AIC31",
176 .cpu_dai = &s6000_i2s_dai,
177 .codec_dai = &aic3x_dai,
178 .init = s6105_aic3x_init,
179 .ops = &s6105_ops,
180};
181
182/* s6105 audio machine driver */
183static struct snd_soc_card snd_soc_card_s6105 = {
184 .name = "Stretch IP Camera",
185 .platform = &s6000_soc_platform,
186 .dai_link = &s6105_dai,
187 .num_links = 1,
188};
189
190/* s6105 audio private data */
191static struct aic3x_setup_data s6105_aic3x_setup = {
192 .i2c_bus = 0,
193 .i2c_address = 0x18,
194};
195
196/* s6105 audio subsystem */
197static struct snd_soc_device s6105_snd_devdata = {
198 .card = &snd_soc_card_s6105,
199 .codec_dev = &soc_codec_dev_aic3x,
200 .codec_data = &s6105_aic3x_setup,
201};
202
203static struct s6000_snd_platform_data __initdata s6105_snd_data = {
204 .wide = 0,
205 .channel_in = 0,
206 .channel_out = 1,
207 .lines_in = 1,
208 .lines_out = 1,
209 .same_rate = 1,
210};
211
212static struct platform_device *s6105_snd_device;
213
214static int __init s6105_init(void)
215{
216 int ret;
217
218 s6105_snd_device = platform_device_alloc("soc-audio", -1);
219 if (!s6105_snd_device)
220 return -ENOMEM;
221
222 platform_set_drvdata(s6105_snd_device, &s6105_snd_devdata);
223 s6105_snd_devdata.dev = &s6105_snd_device->dev;
224 platform_device_add_data(s6105_snd_device, &s6105_snd_data,
225 sizeof(s6105_snd_data));
226
227 ret = platform_device_add(s6105_snd_device);
228 if (ret)
229 platform_device_put(s6105_snd_device);
230
231 return ret;
232}
233
234static void __exit s6105_exit(void)
235{
236 platform_device_unregister(s6105_snd_device);
237}
238
239module_init(s6105_init);
240module_exit(s6105_exit);
241
242MODULE_AUTHOR("Daniel Gloeckner");
243MODULE_DESCRIPTION("Stretch s6105 IP camera ASoC driver");
244MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 99712f652d0d..af11791a3b8c 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -113,6 +113,35 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
113} 113}
114#endif 114#endif
115 115
116static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
117{
118 struct snd_soc_pcm_runtime *rtd = substream->private_data;
119 struct snd_soc_device *socdev = rtd->socdev;
120 struct snd_soc_card *card = socdev->card;
121 struct snd_soc_dai_link *machine = rtd->dai;
122 struct snd_soc_dai *cpu_dai = machine->cpu_dai;
123 struct snd_soc_dai *codec_dai = machine->codec_dai;
124 int ret;
125
126 if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates ||
127 machine->symmetric_rates) {
128 dev_dbg(card->dev, "Symmetry forces %dHz rate\n",
129 machine->rate);
130
131 ret = snd_pcm_hw_constraint_minmax(substream->runtime,
132 SNDRV_PCM_HW_PARAM_RATE,
133 machine->rate,
134 machine->rate);
135 if (ret < 0) {
136 dev_err(card->dev,
137 "Unable to apply rate symmetry constraint: %d\n", ret);
138 return ret;
139 }
140 }
141
142 return 0;
143}
144
116/* 145/*
117 * Called by ALSA when a PCM substream is opened, the runtime->hw record is 146 * Called by ALSA when a PCM substream is opened, the runtime->hw record is
118 * then initialized and any private data can be allocated. This also calls 147 * then initialized and any private data can be allocated. This also calls
@@ -221,6 +250,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
221 goto machine_err; 250 goto machine_err;
222 } 251 }
223 252
253 /* Symmetry only applies if we've already got an active stream. */
254 if (cpu_dai->active || codec_dai->active) {
255 ret = soc_pcm_apply_symmetry(substream);
256 if (ret != 0)
257 goto machine_err;
258 }
259
224 pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name); 260 pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
225 pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); 261 pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
226 pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, 262 pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
@@ -521,6 +557,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
521 } 557 }
522 } 558 }
523 559
560 machine->rate = params_rate(params);
561
524out: 562out:
525 mutex_unlock(&pcm_mutex); 563 mutex_unlock(&pcm_mutex);
526 return ret; 564 return ret;
@@ -1741,7 +1779,7 @@ int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
1741{ 1779{
1742 int max = kcontrol->private_value; 1780 int max = kcontrol->private_value;
1743 1781
1744 if (max == 1) 1782 if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
1745 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 1783 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1746 else 1784 else
1747 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1785 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1771,7 +1809,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
1771 unsigned int shift = mc->shift; 1809 unsigned int shift = mc->shift;
1772 unsigned int rshift = mc->rshift; 1810 unsigned int rshift = mc->rshift;
1773 1811
1774 if (max == 1) 1812 if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
1775 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 1813 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1776 else 1814 else
1777 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1815 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1878,7 +1916,7 @@ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
1878 (struct soc_mixer_control *)kcontrol->private_value; 1916 (struct soc_mixer_control *)kcontrol->private_value;
1879 int max = mc->max; 1917 int max = mc->max;
1880 1918
1881 if (max == 1) 1919 if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
1882 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 1920 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1883 else 1921 else
1884 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1922 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -2062,7 +2100,7 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
2062int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, 2100int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
2063 unsigned int freq, int dir) 2101 unsigned int freq, int dir)
2064{ 2102{
2065 if (dai->ops->set_sysclk) 2103 if (dai->ops && dai->ops->set_sysclk)
2066 return dai->ops->set_sysclk(dai, clk_id, freq, dir); 2104 return dai->ops->set_sysclk(dai, clk_id, freq, dir);
2067 else 2105 else
2068 return -EINVAL; 2106 return -EINVAL;
@@ -2082,7 +2120,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
2082int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, 2120int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
2083 int div_id, int div) 2121 int div_id, int div)
2084{ 2122{
2085 if (dai->ops->set_clkdiv) 2123 if (dai->ops && dai->ops->set_clkdiv)
2086 return dai->ops->set_clkdiv(dai, div_id, div); 2124 return dai->ops->set_clkdiv(dai, div_id, div);
2087 else 2125 else
2088 return -EINVAL; 2126 return -EINVAL;
@@ -2101,7 +2139,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
2101int snd_soc_dai_set_pll(struct snd_soc_dai *dai, 2139int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
2102 int pll_id, unsigned int freq_in, unsigned int freq_out) 2140 int pll_id, unsigned int freq_in, unsigned int freq_out)
2103{ 2141{
2104 if (dai->ops->set_pll) 2142 if (dai->ops && dai->ops->set_pll)
2105 return dai->ops->set_pll(dai, pll_id, freq_in, freq_out); 2143 return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
2106 else 2144 else
2107 return -EINVAL; 2145 return -EINVAL;
@@ -2117,7 +2155,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
2117 */ 2155 */
2118int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 2156int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
2119{ 2157{
2120 if (dai->ops->set_fmt) 2158 if (dai->ops && dai->ops->set_fmt)
2121 return dai->ops->set_fmt(dai, fmt); 2159 return dai->ops->set_fmt(dai, fmt);
2122 else 2160 else
2123 return -EINVAL; 2161 return -EINVAL;
@@ -2136,7 +2174,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
2136int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, 2174int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
2137 unsigned int mask, int slots) 2175 unsigned int mask, int slots)
2138{ 2176{
2139 if (dai->ops->set_sysclk) 2177 if (dai->ops && dai->ops->set_tdm_slot)
2140 return dai->ops->set_tdm_slot(dai, mask, slots); 2178 return dai->ops->set_tdm_slot(dai, mask, slots);
2141 else 2179 else
2142 return -EINVAL; 2180 return -EINVAL;
@@ -2152,7 +2190,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
2152 */ 2190 */
2153int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) 2191int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
2154{ 2192{
2155 if (dai->ops->set_sysclk) 2193 if (dai->ops && dai->ops->set_tristate)
2156 return dai->ops->set_tristate(dai, tristate); 2194 return dai->ops->set_tristate(dai, tristate);
2157 else 2195 else
2158 return -EINVAL; 2196 return -EINVAL;
@@ -2168,7 +2206,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
2168 */ 2206 */
2169int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) 2207int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
2170{ 2208{
2171 if (dai->ops->digital_mute) 2209 if (dai->ops && dai->ops->digital_mute)
2172 return dai->ops->digital_mute(dai, mute); 2210 return dai->ops->digital_mute(dai, mute);
2173 else 2211 else
2174 return -EINVAL; 2212 return -EINVAL;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 735903a74675..a6d73379ab32 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -357,8 +357,9 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
357 path->long_name); 357 path->long_name);
358 ret = snd_ctl_add(codec->card, path->kcontrol); 358 ret = snd_ctl_add(codec->card, path->kcontrol);
359 if (ret < 0) { 359 if (ret < 0) {
360 printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n", 360 printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n",
361 path->long_name); 361 path->long_name,
362 ret);
362 kfree(path->long_name); 363 kfree(path->long_name);
363 path->long_name = NULL; 364 path->long_name = NULL;
364 return ret; 365 return ret;
@@ -521,6 +522,65 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
521} 522}
522EXPORT_SYMBOL_GPL(dapm_reg_event); 523EXPORT_SYMBOL_GPL(dapm_reg_event);
523 524
525/* Standard power change method, used to apply power changes to most
526 * widgets.
527 */
528static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
529{
530 int ret;
531
532 /* call any power change event handlers */
533 if (w->event)
534 pr_debug("power %s event for %s flags %x\n",
535 w->power ? "on" : "off",
536 w->name, w->event_flags);
537
538 /* power up pre event */
539 if (w->power && w->event &&
540 (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
541 ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
542 if (ret < 0)
543 return ret;
544 }
545
546 /* power down pre event */
547 if (!w->power && w->event &&
548 (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
549 ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
550 if (ret < 0)
551 return ret;
552 }
553
554 /* Lower PGA volume to reduce pops */
555 if (w->id == snd_soc_dapm_pga && !w->power)
556 dapm_set_pga(w, w->power);
557
558 dapm_update_bits(w);
559
560 /* Raise PGA volume to reduce pops */
561 if (w->id == snd_soc_dapm_pga && w->power)
562 dapm_set_pga(w, w->power);
563
564 /* power up post event */
565 if (w->power && w->event &&
566 (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
567 ret = w->event(w,
568 NULL, SND_SOC_DAPM_POST_PMU);
569 if (ret < 0)
570 return ret;
571 }
572
573 /* power down post event */
574 if (!w->power && w->event &&
575 (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
576 ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
577 if (ret < 0)
578 return ret;
579 }
580
581 return 0;
582}
583
524/* 584/*
525 * Scan a single DAPM widget for a complete audio path and update the 585 * Scan a single DAPM widget for a complete audio path and update the
526 * power status appropriately. 586 * power status appropriately.
@@ -538,18 +598,22 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
538 if (w->id == snd_soc_dapm_adc && w->active) { 598 if (w->id == snd_soc_dapm_adc && w->active) {
539 in = is_connected_input_ep(w); 599 in = is_connected_input_ep(w);
540 dapm_clear_walk(w->codec); 600 dapm_clear_walk(w->codec);
541 w->power = (in != 0) ? 1 : 0; 601 power = (in != 0) ? 1 : 0;
542 dapm_update_bits(w); 602 if (power == w->power)
543 return 0; 603 return 0;
604 w->power = power;
605 return dapm_generic_apply_power(w);
544 } 606 }
545 607
546 /* active DAC */ 608 /* active DAC */
547 if (w->id == snd_soc_dapm_dac && w->active) { 609 if (w->id == snd_soc_dapm_dac && w->active) {
548 out = is_connected_output_ep(w); 610 out = is_connected_output_ep(w);
549 dapm_clear_walk(w->codec); 611 dapm_clear_walk(w->codec);
550 w->power = (out != 0) ? 1 : 0; 612 power = (out != 0) ? 1 : 0;
551 dapm_update_bits(w); 613 if (power == w->power)
552 return 0; 614 return 0;
615 w->power = power;
616 return dapm_generic_apply_power(w);
553 } 617 }
554 618
555 /* pre and post event widgets */ 619 /* pre and post event widgets */
@@ -600,56 +664,7 @@ static int dapm_power_widget(struct snd_soc_codec *codec, int event,
600 if (!power_change) 664 if (!power_change)
601 return 0; 665 return 0;
602 666
603 /* call any power change event handlers */ 667 return dapm_generic_apply_power(w);
604 if (w->event)
605 pr_debug("power %s event for %s flags %x\n",
606 w->power ? "on" : "off",
607 w->name, w->event_flags);
608
609 /* power up pre event */
610 if (power && w->event &&
611 (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
612 ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
613 if (ret < 0)
614 return ret;
615 }
616
617 /* power down pre event */
618 if (!power && w->event &&
619 (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
620 ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
621 if (ret < 0)
622 return ret;
623 }
624
625 /* Lower PGA volume to reduce pops */
626 if (w->id == snd_soc_dapm_pga && !power)
627 dapm_set_pga(w, power);
628
629 dapm_update_bits(w);
630
631 /* Raise PGA volume to reduce pops */
632 if (w->id == snd_soc_dapm_pga && power)
633 dapm_set_pga(w, power);
634
635 /* power up post event */
636 if (power && w->event &&
637 (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
638 ret = w->event(w,
639 NULL, SND_SOC_DAPM_POST_PMU);
640 if (ret < 0)
641 return ret;
642 }
643
644 /* power down post event */
645 if (!power && w->event &&
646 (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
647 ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
648 if (ret < 0)
649 return ret;
650 }
651
652 return 0;
653} 668}
654 669
655/* 670/*