aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ad193x.c41
-rw-r--r--sound/soc/codecs/ad193x.h5
-rw-r--r--sound/soc/codecs/cs42l51.c763
-rw-r--r--sound/soc/codecs/cs42l51.h163
-rw-r--r--sound/soc/codecs/spdif_transciever.c94
-rw-r--r--sound/soc/codecs/spdif_transciever.h1
-rw-r--r--sound/soc/codecs/wm8990.c4
-rw-r--r--sound/soc/kirkwood/Kconfig20
-rw-r--r--sound/soc/kirkwood/Makefile9
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c383
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.h17
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c485
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.h17
-rw-r--r--sound/soc/kirkwood/kirkwood-openrd.c126
-rw-r--r--sound/soc/kirkwood/kirkwood.h126
-rw-r--r--sound/soc/nuc900/Kconfig27
-rw-r--r--sound/soc/nuc900/Makefile11
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c442
-rw-r--r--sound/soc/nuc900/nuc900-audio.c81
-rw-r--r--sound/soc/nuc900/nuc900-auido.h121
-rw-r--r--sound/soc/nuc900/nuc900-pcm.c352
-rw-r--r--sound/soc/s3c24xx/Kconfig4
-rw-r--r--sound/soc/s3c24xx/smdk_wm9713.c3
-rw-r--r--sound/soc/soc-core.c95
28 files changed, 3392 insertions, 8 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index b1749bc67979..5e68ac880832 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -30,7 +30,9 @@ source "sound/soc/blackfin/Kconfig"
30source "sound/soc/davinci/Kconfig" 30source "sound/soc/davinci/Kconfig"
31source "sound/soc/fsl/Kconfig" 31source "sound/soc/fsl/Kconfig"
32source "sound/soc/imx/Kconfig" 32source "sound/soc/imx/Kconfig"
33source "sound/soc/nuc900/Kconfig"
33source "sound/soc/omap/Kconfig" 34source "sound/soc/omap/Kconfig"
35source "sound/soc/kirkwood/Kconfig"
34source "sound/soc/pxa/Kconfig" 36source "sound/soc/pxa/Kconfig"
35source "sound/soc/s3c24xx/Kconfig" 37source "sound/soc/s3c24xx/Kconfig"
36source "sound/soc/s6000/Kconfig" 38source "sound/soc/s6000/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 1470141d4167..05d5d340968e 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -8,7 +8,9 @@ obj-$(CONFIG_SND_SOC) += blackfin/
8obj-$(CONFIG_SND_SOC) += davinci/ 8obj-$(CONFIG_SND_SOC) += davinci/
9obj-$(CONFIG_SND_SOC) += fsl/ 9obj-$(CONFIG_SND_SOC) += fsl/
10obj-$(CONFIG_SND_SOC) += imx/ 10obj-$(CONFIG_SND_SOC) += imx/
11obj-$(CONFIG_SND_SOC) += nuc900/
11obj-$(CONFIG_SND_SOC) += omap/ 12obj-$(CONFIG_SND_SOC) += omap/
13obj-$(CONFIG_SND_SOC) += kirkwood/
12obj-$(CONFIG_SND_SOC) += pxa/ 14obj-$(CONFIG_SND_SOC) += pxa/
13obj-$(CONFIG_SND_SOC) += s3c24xx/ 15obj-$(CONFIG_SND_SOC) += s3c24xx/
14obj-$(CONFIG_SND_SOC) += s6000/ 16obj-$(CONFIG_SND_SOC) += s6000/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 31ac5538fe7e..c37c84458b58 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -22,6 +22,7 @@ config SND_SOC_ALL_CODECS
22 select SND_SOC_AK4642 if I2C 22 select SND_SOC_AK4642 if I2C
23 select SND_SOC_AK4671 if I2C 23 select SND_SOC_AK4671 if I2C
24 select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC 24 select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
25 select SND_SOC_CS42L51 if I2C
25 select SND_SOC_CS4270 if I2C 26 select SND_SOC_CS4270 if I2C
26 select SND_SOC_MAX9877 if I2C 27 select SND_SOC_MAX9877 if I2C
27 select SND_SOC_DA7210 if I2C 28 select SND_SOC_DA7210 if I2C
@@ -120,6 +121,9 @@ config SND_SOC_AK4671
120config SND_SOC_CQ0093VC 121config SND_SOC_CQ0093VC
121 tristate 122 tristate
122 123
124config SND_SOC_CS42L51
125 tristate
126
123# Cirrus Logic CS4270 Codec 127# Cirrus Logic CS4270 Codec
124config SND_SOC_CS4270 128config SND_SOC_CS4270
125 tristate 129 tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 91429eab0707..4a9c205caf56 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -9,6 +9,7 @@ snd-soc-ak4535-objs := ak4535.o
9snd-soc-ak4642-objs := ak4642.o 9snd-soc-ak4642-objs := ak4642.o
10snd-soc-ak4671-objs := ak4671.o 10snd-soc-ak4671-objs := ak4671.o
11snd-soc-cq93vc-objs := cq93vc.o 11snd-soc-cq93vc-objs := cq93vc.o
12snd-soc-cs42l51-objs := cs42l51.o
12snd-soc-cs4270-objs := cs4270.o 13snd-soc-cs4270-objs := cs4270.o
13snd-soc-cx20442-objs := cx20442.o 14snd-soc-cx20442-objs := cx20442.o
14snd-soc-da7210-objs := da7210.o 15snd-soc-da7210-objs := da7210.o
@@ -74,6 +75,7 @@ obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
74obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o 75obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
75obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o 76obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
76obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o 77obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
78obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
77obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o 79obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
78obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o 80obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
79obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o 81obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index c8ca1142b2f4..1def75e4862f 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -24,6 +24,7 @@
24 24
25/* codec private data */ 25/* codec private data */
26struct ad193x_priv { 26struct ad193x_priv {
27 unsigned int sysclk;
27 struct snd_soc_codec codec; 28 struct snd_soc_codec codec;
28 u8 reg_cache[AD193X_NUM_REGS]; 29 u8 reg_cache[AD193X_NUM_REGS];
29}; 30};
@@ -251,15 +252,32 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
251 return 0; 252 return 0;
252} 253}
253 254
255static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
256 int clk_id, unsigned int freq, int dir)
257{
258 struct snd_soc_codec *codec = codec_dai->codec;
259 struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
260 switch (freq) {
261 case 12288000:
262 case 18432000:
263 case 24576000:
264 case 36864000:
265 ad193x->sysclk = freq;
266 return 0;
267 }
268 return -EINVAL;
269}
270
254static int ad193x_hw_params(struct snd_pcm_substream *substream, 271static int ad193x_hw_params(struct snd_pcm_substream *substream,
255 struct snd_pcm_hw_params *params, 272 struct snd_pcm_hw_params *params,
256 struct snd_soc_dai *dai) 273 struct snd_soc_dai *dai)
257{ 274{
258 int word_len = 0, reg = 0; 275 int word_len = 0, reg = 0, master_rate = 0;
259 276
260 struct snd_soc_pcm_runtime *rtd = substream->private_data; 277 struct snd_soc_pcm_runtime *rtd = substream->private_data;
261 struct snd_soc_device *socdev = rtd->socdev; 278 struct snd_soc_device *socdev = rtd->socdev;
262 struct snd_soc_codec *codec = socdev->card->codec; 279 struct snd_soc_codec *codec = socdev->card->codec;
280 struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
263 281
264 /* bit size */ 282 /* bit size */
265 switch (params_format(params)) { 283 switch (params_format(params)) {
@@ -275,6 +293,25 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
275 break; 293 break;
276 } 294 }
277 295
296 switch (ad193x->sysclk) {
297 case 12288000:
298 master_rate = AD193X_PLL_INPUT_256;
299 break;
300 case 18432000:
301 master_rate = AD193X_PLL_INPUT_384;
302 break;
303 case 24576000:
304 master_rate = AD193X_PLL_INPUT_512;
305 break;
306 case 36864000:
307 master_rate = AD193X_PLL_INPUT_768;
308 break;
309 }
310
311 reg = snd_soc_read(codec, AD193X_PLL_CLK_CTRL0);
312 reg = (reg & AD193X_PLL_INPUT_MASK) | master_rate;
313 snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, reg);
314
278 reg = snd_soc_read(codec, AD193X_DAC_CTRL2); 315 reg = snd_soc_read(codec, AD193X_DAC_CTRL2);
279 reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len; 316 reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len;
280 snd_soc_write(codec, AD193X_DAC_CTRL2, reg); 317 snd_soc_write(codec, AD193X_DAC_CTRL2, reg);
@@ -348,6 +385,7 @@ static int ad193x_bus_probe(struct device *dev, void *ctrl_data, int bus_type)
348 /* pll input: mclki/xi */ 385 /* pll input: mclki/xi */
349 snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ 386 snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
350 snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04); 387 snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04);
388 ad193x->sysclk = 12288000;
351 389
352 ret = snd_soc_register_codec(codec); 390 ret = snd_soc_register_codec(codec);
353 if (ret != 0) { 391 if (ret != 0) {
@@ -383,6 +421,7 @@ static struct snd_soc_dai_ops ad193x_dai_ops = {
383 .hw_params = ad193x_hw_params, 421 .hw_params = ad193x_hw_params,
384 .digital_mute = ad193x_mute, 422 .digital_mute = ad193x_mute,
385 .set_tdm_slot = ad193x_set_tdm_slot, 423 .set_tdm_slot = ad193x_set_tdm_slot,
424 .set_sysclk = ad193x_set_dai_sysclk,
386 .set_fmt = ad193x_set_dai_fmt, 425 .set_fmt = ad193x_set_dai_fmt,
387}; 426};
388 427
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index a03c880d52f9..654ba64ae04c 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -11,6 +11,11 @@
11 11
12#define AD193X_PLL_CLK_CTRL0 0x800 12#define AD193X_PLL_CLK_CTRL0 0x800
13#define AD193X_PLL_POWERDOWN 0x01 13#define AD193X_PLL_POWERDOWN 0x01
14#define AD193X_PLL_INPUT_MASK (~0x6)
15#define AD193X_PLL_INPUT_256 (0 << 1)
16#define AD193X_PLL_INPUT_384 (1 << 1)
17#define AD193X_PLL_INPUT_512 (2 << 1)
18#define AD193X_PLL_INPUT_768 (3 << 1)
14#define AD193X_PLL_CLK_CTRL1 0x801 19#define AD193X_PLL_CLK_CTRL1 0x801
15#define AD193X_DAC_CTRL0 0x802 20#define AD193X_DAC_CTRL0 0x802
16#define AD193X_DAC_POWERDOWN 0x01 21#define AD193X_DAC_POWERDOWN 0x01
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
new file mode 100644
index 000000000000..dd9b8550c402
--- /dev/null
+++ b/sound/soc/codecs/cs42l51.c
@@ -0,0 +1,763 @@
1/*
2 * cs42l51.c
3 *
4 * ASoC Driver for Cirrus Logic CS42L51 codecs
5 *
6 * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
7 *
8 * Based on cs4270.c - Copyright (c) Freescale Semiconductor
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * For now:
20 * - Only I2C is support. Not SPI
21 * - master mode *NOT* supported
22 */
23
24#include <linux/module.h>
25#include <linux/platform_device.h>
26#include <linux/slab.h>
27#include <sound/core.h>
28#include <sound/soc.h>
29#include <sound/soc-dapm.h>
30#include <sound/tlv.h>
31#include <sound/initval.h>
32#include <sound/pcm_params.h>
33#include <sound/pcm.h>
34#include <linux/i2c.h>
35
36#include "cs42l51.h"
37
38enum master_slave_mode {
39 MODE_SLAVE,
40 MODE_SLAVE_AUTO,
41 MODE_MASTER,
42};
43
44struct cs42l51_private {
45 unsigned int mclk;
46 unsigned int audio_mode; /* The mode (I2S or left-justified) */
47 enum master_slave_mode func;
48 struct snd_soc_codec codec;
49 u8 reg_cache[CS42L51_NUMREGS];
50};
51
52static struct snd_soc_codec *cs42l51_codec;
53
54#define CS42L51_FORMATS ( \
55 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
56 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
57 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
58 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
59
60static int cs42l51_fill_cache(struct snd_soc_codec *codec)
61{
62 u8 *cache = codec->reg_cache + 1;
63 struct i2c_client *i2c_client = codec->control_data;
64 s32 length;
65
66 length = i2c_smbus_read_i2c_block_data(i2c_client,
67 CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache);
68 if (length != CS42L51_NUMREGS) {
69 dev_err(&i2c_client->dev,
70 "I2C read failure, addr=0x%x (ret=%d vs %d)\n",
71 i2c_client->addr, length, CS42L51_NUMREGS);
72 return -EIO;
73 }
74
75 return 0;
76}
77
78static int cs42l51_i2c_probe(struct i2c_client *i2c_client,
79 const struct i2c_device_id *id)
80{
81 struct snd_soc_codec *codec;
82 struct cs42l51_private *cs42l51;
83 int ret = 0;
84 int reg;
85
86 if (cs42l51_codec)
87 return -EBUSY;
88
89 /* Verify that we have a CS42L51 */
90 ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID);
91 if (ret < 0) {
92 dev_err(&i2c_client->dev, "failed to read I2C\n");
93 goto error;
94 }
95
96 if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
97 (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
98 dev_err(&i2c_client->dev, "Invalid chip id\n");
99 ret = -ENODEV;
100 goto error;
101 }
102
103 dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n",
104 ret & 7);
105
106 cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL);
107 if (!cs42l51) {
108 dev_err(&i2c_client->dev, "could not allocate codec\n");
109 return -ENOMEM;
110 }
111 codec = &cs42l51->codec;
112
113 mutex_init(&codec->mutex);
114 INIT_LIST_HEAD(&codec->dapm_widgets);
115 INIT_LIST_HEAD(&codec->dapm_paths);
116
117 codec->dev = &i2c_client->dev;
118 codec->name = "CS42L51";
119 codec->owner = THIS_MODULE;
120 codec->dai = &cs42l51_dai;
121 codec->num_dai = 1;
122 snd_soc_codec_set_drvdata(codec, cs42l51);
123
124 codec->control_data = i2c_client;
125 codec->reg_cache = cs42l51->reg_cache;
126 codec->reg_cache_size = CS42L51_NUMREGS;
127 i2c_set_clientdata(i2c_client, codec);
128
129 ret = cs42l51_fill_cache(codec);
130 if (ret < 0) {
131 dev_err(&i2c_client->dev, "failed to fill register cache\n");
132 goto error_alloc;
133 }
134
135 ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
136 if (ret < 0) {
137 dev_err(&i2c_client->dev, "Failed to set cache I/O: %d\n", ret);
138 goto error_alloc;
139 }
140
141 /*
142 * DAC configuration
143 * - Use signal processor
144 * - auto mute
145 * - vol changes immediate
146 * - no de-emphasize
147 */
148 reg = CS42L51_DAC_CTL_DATA_SEL(1)
149 | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
150 ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
151 if (ret < 0)
152 goto error_alloc;
153
154 cs42l51_dai.dev = codec->dev;
155 cs42l51_codec = codec;
156
157 ret = snd_soc_register_codec(codec);
158 if (ret != 0) {
159 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
160 goto error_alloc;
161 }
162
163 ret = snd_soc_register_dai(&cs42l51_dai);
164 if (ret < 0) {
165 dev_err(&i2c_client->dev, "failed to register DAIe\n");
166 goto error_reg;
167 }
168
169 return 0;
170
171error_reg:
172 snd_soc_unregister_codec(codec);
173error_alloc:
174 kfree(cs42l51);
175error:
176 return ret;
177}
178
179static int cs42l51_i2c_remove(struct i2c_client *client)
180{
181 struct cs42l51_private *cs42l51 = i2c_get_clientdata(client);
182 snd_soc_unregister_dai(&cs42l51_dai);
183 snd_soc_unregister_codec(cs42l51_codec);
184 cs42l51_codec = NULL;
185 kfree(cs42l51);
186 return 0;
187}
188
189
190static const struct i2c_device_id cs42l51_id[] = {
191 {"cs42l51", 0},
192 {}
193};
194MODULE_DEVICE_TABLE(i2c, cs42l51_id);
195
196static struct i2c_driver cs42l51_i2c_driver = {
197 .driver = {
198 .name = "CS42L51 I2C",
199 .owner = THIS_MODULE,
200 },
201 .id_table = cs42l51_id,
202 .probe = cs42l51_i2c_probe,
203 .remove = cs42l51_i2c_remove,
204};
205
206static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
207 struct snd_ctl_elem_value *ucontrol)
208{
209 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
210 unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
211
212 switch (value) {
213 default:
214 case 0:
215 ucontrol->value.integer.value[0] = 0;
216 break;
217 /* same value : (L+R)/2 and (R+L)/2 */
218 case 1:
219 case 2:
220 ucontrol->value.integer.value[0] = 1;
221 break;
222 case 3:
223 ucontrol->value.integer.value[0] = 2;
224 break;
225 }
226
227 return 0;
228}
229
230#define CHAN_MIX_NORMAL 0x00
231#define CHAN_MIX_BOTH 0x55
232#define CHAN_MIX_SWAP 0xFF
233
234static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
235 struct snd_ctl_elem_value *ucontrol)
236{
237 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
238 unsigned char val;
239
240 switch (ucontrol->value.integer.value[0]) {
241 default:
242 case 0:
243 val = CHAN_MIX_NORMAL;
244 break;
245 case 1:
246 val = CHAN_MIX_BOTH;
247 break;
248 case 2:
249 val = CHAN_MIX_SWAP;
250 break;
251 }
252
253 snd_soc_write(codec, CS42L51_PCM_MIXER, val);
254
255 return 1;
256}
257
258static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
259static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
260/* This is a lie. after -102 db, it stays at -102 */
261/* maybe a range would be better */
262static const DECLARE_TLV_DB_SCALE(aout_tlv, -11550, 50, 0);
263
264static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
265static const char *chan_mix[] = {
266 "L R",
267 "L+R",
268 "R L",
269};
270
271static const struct soc_enum cs42l51_chan_mix =
272 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix);
273
274static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
275 SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
276 CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
277 7, 0xffffff99, 0x18, adc_pcm_tlv),
278 SOC_DOUBLE_R("PCM Playback Switch",
279 CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
280 SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
281 CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
282 8, 0xffffff19, 0x18, aout_tlv),
283 SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
284 CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
285 7, 0xffffff99, 0x18, adc_pcm_tlv),
286 SOC_DOUBLE_R("ADC Mixer Switch",
287 CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
288 SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
289 SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
290 SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
291 SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
292 SOC_DOUBLE_TLV("Mic Boost Volume",
293 CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
294 SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
295 SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
296 SOC_ENUM_EXT("PCM channel mixer",
297 cs42l51_chan_mix,
298 cs42l51_get_chan_mix, cs42l51_set_chan_mix),
299};
300
301/*
302 * to power down, one must:
303 * 1.) Enable the PDN bit
304 * 2.) enable power-down for the select channels
305 * 3.) disable the PDN bit.
306 */
307static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
308 struct snd_kcontrol *kcontrol, int event)
309{
310 unsigned long value;
311
312 value = snd_soc_read(w->codec, CS42L51_POWER_CTL1);
313 value &= ~CS42L51_POWER_CTL1_PDN;
314
315 switch (event) {
316 case SND_SOC_DAPM_PRE_PMD:
317 value |= CS42L51_POWER_CTL1_PDN;
318 break;
319 default:
320 case SND_SOC_DAPM_POST_PMD:
321 break;
322 }
323 snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
324 CS42L51_POWER_CTL1_PDN, value);
325
326 return 0;
327}
328
329static const char *cs42l51_dac_names[] = {"Direct PCM",
330 "DSP PCM", "ADC"};
331static const struct soc_enum cs42l51_dac_mux_enum =
332 SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names);
333static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
334 SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
335
336static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
337 "MIC Left", "MIC+preamp Left"};
338static const struct soc_enum cs42l51_adcl_mux_enum =
339 SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names);
340static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
341 SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
342
343static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
344 "MIC Right", "MIC+preamp Right"};
345static const struct soc_enum cs42l51_adcr_mux_enum =
346 SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names);
347static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
348 SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
349
350static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
351 SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
352 SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
353 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
354 SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
355 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
356 SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
357 CS42L51_POWER_CTL1, 1, 1,
358 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
359 SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
360 CS42L51_POWER_CTL1, 2, 1,
361 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
362 SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
363 CS42L51_POWER_CTL1, 5, 1,
364 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
365 SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
366 CS42L51_POWER_CTL1, 6, 1,
367 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
368
369 /* analog/mic */
370 SND_SOC_DAPM_INPUT("AIN1L"),
371 SND_SOC_DAPM_INPUT("AIN1R"),
372 SND_SOC_DAPM_INPUT("AIN2L"),
373 SND_SOC_DAPM_INPUT("AIN2R"),
374 SND_SOC_DAPM_INPUT("MICL"),
375 SND_SOC_DAPM_INPUT("MICR"),
376
377 SND_SOC_DAPM_MIXER("Mic Preamp Left",
378 CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
379 SND_SOC_DAPM_MIXER("Mic Preamp Right",
380 CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
381
382 /* HP */
383 SND_SOC_DAPM_OUTPUT("HPL"),
384 SND_SOC_DAPM_OUTPUT("HPR"),
385
386 /* mux */
387 SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
388 &cs42l51_dac_mux_controls),
389 SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
390 &cs42l51_adcl_mux_controls),
391 SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
392 &cs42l51_adcr_mux_controls),
393};
394
395static const struct snd_soc_dapm_route cs42l51_routes[] = {
396 {"HPL", NULL, "Left DAC"},
397 {"HPR", NULL, "Right DAC"},
398
399 {"Left ADC", NULL, "Left PGA"},
400 {"Right ADC", NULL, "Right PGA"},
401
402 {"Mic Preamp Left", NULL, "MICL"},
403 {"Mic Preamp Right", NULL, "MICR"},
404
405 {"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" },
406 {"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" },
407 {"PGA-ADC Mux Left", "MIC Left", "MICL" },
408 {"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" },
409 {"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" },
410 {"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" },
411 {"PGA-ADC Mux Right", "MIC Right", "MICR" },
412 {"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
413
414 {"Left PGA", NULL, "PGA-ADC Mux Left"},
415 {"Right PGA", NULL, "PGA-ADC Mux Right"},
416};
417
418static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
419 unsigned int format)
420{
421 struct snd_soc_codec *codec = codec_dai->codec;
422 struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
423 int ret = 0;
424
425 switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
426 case SND_SOC_DAIFMT_I2S:
427 case SND_SOC_DAIFMT_LEFT_J:
428 case SND_SOC_DAIFMT_RIGHT_J:
429 cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
430 break;
431 default:
432 dev_err(codec->dev, "invalid DAI format\n");
433 ret = -EINVAL;
434 }
435
436 switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
437 case SND_SOC_DAIFMT_CBM_CFM:
438 cs42l51->func = MODE_MASTER;
439 break;
440 case SND_SOC_DAIFMT_CBS_CFS:
441 cs42l51->func = MODE_SLAVE_AUTO;
442 break;
443 default:
444 ret = -EINVAL;
445 break;
446 }
447
448 return ret;
449}
450
451struct cs42l51_ratios {
452 unsigned int ratio;
453 unsigned char speed_mode;
454 unsigned char mclk;
455};
456
457static struct cs42l51_ratios slave_ratios[] = {
458 { 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 },
459 { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
460 { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
461 { 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 },
462 { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
463 { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
464 { 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 },
465 { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
466 { 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 },
467 { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
468 { 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 },
469};
470
471static struct cs42l51_ratios slave_auto_ratios[] = {
472 { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
473 { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
474 { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 },
475 { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
476 { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 },
477 { 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 },
478 { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 },
479 { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 },
480};
481
482static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
483 int clk_id, unsigned int freq, int dir)
484{
485 struct snd_soc_codec *codec = codec_dai->codec;
486 struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
487 struct cs42l51_ratios *ratios = NULL;
488 int nr_ratios = 0;
489 unsigned int rates = 0;
490 unsigned int rate_min = -1;
491 unsigned int rate_max = 0;
492 int i;
493
494 cs42l51->mclk = freq;
495
496 switch (cs42l51->func) {
497 case MODE_MASTER:
498 return -EINVAL;
499 case MODE_SLAVE:
500 ratios = slave_ratios;
501 nr_ratios = ARRAY_SIZE(slave_ratios);
502 break;
503 case MODE_SLAVE_AUTO:
504 ratios = slave_auto_ratios;
505 nr_ratios = ARRAY_SIZE(slave_auto_ratios);
506 break;
507 }
508
509 for (i = 0; i < nr_ratios; i++) {
510 unsigned int rate = freq / ratios[i].ratio;
511 rates |= snd_pcm_rate_to_rate_bit(rate);
512 if (rate < rate_min)
513 rate_min = rate;
514 if (rate > rate_max)
515 rate_max = rate;
516 }
517 rates &= ~SNDRV_PCM_RATE_KNOT;
518
519 if (!rates) {
520 dev_err(codec->dev, "could not find a valid sample rate\n");
521 return -EINVAL;
522 }
523
524 codec_dai->playback.rates = rates;
525 codec_dai->playback.rate_min = rate_min;
526 codec_dai->playback.rate_max = rate_max;
527
528 codec_dai->capture.rates = rates;
529 codec_dai->capture.rate_min = rate_min;
530 codec_dai->capture.rate_max = rate_max;
531
532 return 0;
533}
534
535static int cs42l51_hw_params(struct snd_pcm_substream *substream,
536 struct snd_pcm_hw_params *params,
537 struct snd_soc_dai *dai)
538{
539 struct snd_soc_pcm_runtime *rtd = substream->private_data;
540 struct snd_soc_device *socdev = rtd->socdev;
541 struct snd_soc_codec *codec = socdev->card->codec;
542 struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
543 int ret;
544 unsigned int i;
545 unsigned int rate;
546 unsigned int ratio;
547 struct cs42l51_ratios *ratios = NULL;
548 int nr_ratios = 0;
549 int intf_ctl, power_ctl, fmt;
550
551 switch (cs42l51->func) {
552 case MODE_MASTER:
553 return -EINVAL;
554 case MODE_SLAVE:
555 ratios = slave_ratios;
556 nr_ratios = ARRAY_SIZE(slave_ratios);
557 break;
558 case MODE_SLAVE_AUTO:
559 ratios = slave_auto_ratios;
560 nr_ratios = ARRAY_SIZE(slave_auto_ratios);
561 break;
562 }
563
564 /* Figure out which MCLK/LRCK ratio to use */
565 rate = params_rate(params); /* Sampling rate, in Hz */
566 ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */
567 for (i = 0; i < nr_ratios; i++) {
568 if (ratios[i].ratio == ratio)
569 break;
570 }
571
572 if (i == nr_ratios) {
573 /* We did not find a matching ratio */
574 dev_err(codec->dev, "could not find matching ratio\n");
575 return -EINVAL;
576 }
577
578 intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
579 power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
580
581 intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
582 | CS42L51_INTF_CTL_DAC_FORMAT(7));
583 power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
584 | CS42L51_MIC_POWER_CTL_MCLK_DIV2);
585
586 switch (cs42l51->func) {
587 case MODE_MASTER:
588 intf_ctl |= CS42L51_INTF_CTL_MASTER;
589 power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
590 break;
591 case MODE_SLAVE:
592 power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
593 break;
594 case MODE_SLAVE_AUTO:
595 power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
596 break;
597 }
598
599 switch (cs42l51->audio_mode) {
600 case SND_SOC_DAIFMT_I2S:
601 intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
602 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
603 break;
604 case SND_SOC_DAIFMT_LEFT_J:
605 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
606 break;
607 case SND_SOC_DAIFMT_RIGHT_J:
608 switch (params_format(params)) {
609 case SNDRV_PCM_FORMAT_S16_LE:
610 case SNDRV_PCM_FORMAT_S16_BE:
611 fmt = CS42L51_DAC_DIF_RJ16;
612 break;
613 case SNDRV_PCM_FORMAT_S18_3LE:
614 case SNDRV_PCM_FORMAT_S18_3BE:
615 fmt = CS42L51_DAC_DIF_RJ18;
616 break;
617 case SNDRV_PCM_FORMAT_S20_3LE:
618 case SNDRV_PCM_FORMAT_S20_3BE:
619 fmt = CS42L51_DAC_DIF_RJ20;
620 break;
621 case SNDRV_PCM_FORMAT_S24_LE:
622 case SNDRV_PCM_FORMAT_S24_BE:
623 fmt = CS42L51_DAC_DIF_RJ24;
624 break;
625 default:
626 dev_err(codec->dev, "unknown format\n");
627 return -EINVAL;
628 }
629 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
630 break;
631 default:
632 dev_err(codec->dev, "unknown format\n");
633 return -EINVAL;
634 }
635
636 if (ratios[i].mclk)
637 power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
638
639 ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
640 if (ret < 0)
641 return ret;
642
643 ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
644 if (ret < 0)
645 return ret;
646
647 return 0;
648}
649
650static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
651{
652 struct snd_soc_codec *codec = dai->codec;
653 int reg;
654 int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
655
656 reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
657
658 if (mute)
659 reg |= mask;
660 else
661 reg &= ~mask;
662
663 return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
664}
665
666static struct snd_soc_dai_ops cs42l51_dai_ops = {
667 .hw_params = cs42l51_hw_params,
668 .set_sysclk = cs42l51_set_dai_sysclk,
669 .set_fmt = cs42l51_set_dai_fmt,
670 .digital_mute = cs42l51_dai_mute,
671};
672
673struct snd_soc_dai cs42l51_dai = {
674 .name = "CS42L51 HiFi",
675 .playback = {
676 .stream_name = "Playback",
677 .channels_min = 1,
678 .channels_max = 2,
679 .rates = SNDRV_PCM_RATE_8000_96000,
680 .formats = CS42L51_FORMATS,
681 },
682 .capture = {
683 .stream_name = "Capture",
684 .channels_min = 1,
685 .channels_max = 2,
686 .rates = SNDRV_PCM_RATE_8000_96000,
687 .formats = CS42L51_FORMATS,
688 },
689 .ops = &cs42l51_dai_ops,
690};
691EXPORT_SYMBOL_GPL(cs42l51_dai);
692
693
694static int cs42l51_probe(struct platform_device *pdev)
695{
696 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
697 struct snd_soc_codec *codec;
698 int ret = 0;
699
700 if (!cs42l51_codec) {
701 dev_err(&pdev->dev, "CS42L51 codec not yet registered\n");
702 return -EINVAL;
703 }
704
705 socdev->card->codec = cs42l51_codec;
706 codec = socdev->card->codec;
707
708 /* Register PCMs */
709 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
710 if (ret < 0) {
711 dev_err(&pdev->dev, "failed to create PCMs\n");
712 return ret;
713 }
714
715 snd_soc_add_controls(codec, cs42l51_snd_controls,
716 ARRAY_SIZE(cs42l51_snd_controls));
717 snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets,
718 ARRAY_SIZE(cs42l51_dapm_widgets));
719 snd_soc_dapm_add_routes(codec, cs42l51_routes,
720 ARRAY_SIZE(cs42l51_routes));
721
722 return 0;
723}
724
725
726static int cs42l51_remove(struct platform_device *pdev)
727{
728 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
729
730 snd_soc_free_pcms(socdev);
731 snd_soc_dapm_free(socdev);
732
733 return 0;
734}
735
736struct snd_soc_codec_device soc_codec_device_cs42l51 = {
737 .probe = cs42l51_probe,
738 .remove = cs42l51_remove
739};
740EXPORT_SYMBOL_GPL(soc_codec_device_cs42l51);
741
742static int __init cs42l51_init(void)
743{
744 int ret;
745
746 ret = i2c_add_driver(&cs42l51_i2c_driver);
747 if (ret != 0) {
748 printk(KERN_ERR "%s: can't add i2c driver\n", __func__);
749 return ret;
750 }
751 return 0;
752}
753module_init(cs42l51_init);
754
755static void __exit cs42l51_exit(void)
756{
757 i2c_del_driver(&cs42l51_i2c_driver);
758}
759module_exit(cs42l51_exit);
760
761MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
762MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
763MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
new file mode 100644
index 000000000000..8f0bd9786ad2
--- /dev/null
+++ b/sound/soc/codecs/cs42l51.h
@@ -0,0 +1,163 @@
1/*
2 * cs42l51.h
3 *
4 * ASoC Driver for Cirrus Logic CS42L51 codecs
5 *
6 * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
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 as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18#ifndef _CS42L51_H
19#define _CS42L51_H
20
21#define CS42L51_CHIP_ID 0x1B
22#define CS42L51_CHIP_REV_A 0x00
23#define CS42L51_CHIP_REV_B 0x01
24
25#define CS42L51_CHIP_REV_ID 0x01
26#define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b))
27
28#define CS42L51_POWER_CTL1 0x02
29#define CS42L51_POWER_CTL1_PDN_DACB (1<<6)
30#define CS42L51_POWER_CTL1_PDN_DACA (1<<5)
31#define CS42L51_POWER_CTL1_PDN_PGAB (1<<4)
32#define CS42L51_POWER_CTL1_PDN_PGAA (1<<3)
33#define CS42L51_POWER_CTL1_PDN_ADCB (1<<2)
34#define CS42L51_POWER_CTL1_PDN_ADCA (1<<1)
35#define CS42L51_POWER_CTL1_PDN (1<<0)
36
37#define CS42L51_MIC_POWER_CTL 0x03
38#define CS42L51_MIC_POWER_CTL_AUTO (1<<7)
39#define CS42L51_MIC_POWER_CTL_SPEED(x) (((x)&3)<<5)
40#define CS42L51_QSM_MODE 3
41#define CS42L51_HSM_MODE 2
42#define CS42L51_SSM_MODE 1
43#define CS42L51_DSM_MODE 0
44#define CS42L51_MIC_POWER_CTL_3ST_SP (1<<4)
45#define CS42L51_MIC_POWER_CTL_PDN_MICB (1<<3)
46#define CS42L51_MIC_POWER_CTL_PDN_MICA (1<<2)
47#define CS42L51_MIC_POWER_CTL_PDN_BIAS (1<<1)
48#define CS42L51_MIC_POWER_CTL_MCLK_DIV2 (1<<0)
49
50#define CS42L51_INTF_CTL 0x04
51#define CS42L51_INTF_CTL_LOOPBACK (1<<7)
52#define CS42L51_INTF_CTL_MASTER (1<<6)
53#define CS42L51_INTF_CTL_DAC_FORMAT(x) (((x)&7)<<3)
54#define CS42L51_DAC_DIF_LJ24 0x00
55#define CS42L51_DAC_DIF_I2S 0x01
56#define CS42L51_DAC_DIF_RJ24 0x02
57#define CS42L51_DAC_DIF_RJ20 0x03
58#define CS42L51_DAC_DIF_RJ18 0x04
59#define CS42L51_DAC_DIF_RJ16 0x05
60#define CS42L51_INTF_CTL_ADC_I2S (1<<2)
61#define CS42L51_INTF_CTL_DIGMIX (1<<1)
62#define CS42L51_INTF_CTL_MICMIX (1<<0)
63
64#define CS42L51_MIC_CTL 0x05
65#define CS42L51_MIC_CTL_ADC_SNGVOL (1<<7)
66#define CS42L51_MIC_CTL_ADCD_DBOOST (1<<6)
67#define CS42L51_MIC_CTL_ADCA_DBOOST (1<<5)
68#define CS42L51_MIC_CTL_MICBIAS_SEL (1<<4)
69#define CS42L51_MIC_CTL_MICBIAS_LVL(x) (((x)&3)<<2)
70#define CS42L51_MIC_CTL_MICB_BOOST (1<<1)
71#define CS42L51_MIC_CTL_MICA_BOOST (1<<0)
72
73#define CS42L51_ADC_CTL 0x06
74#define CS42L51_ADC_CTL_ADCB_HPFEN (1<<7)
75#define CS42L51_ADC_CTL_ADCB_HPFRZ (1<<6)
76#define CS42L51_ADC_CTL_ADCA_HPFEN (1<<5)
77#define CS42L51_ADC_CTL_ADCA_HPFRZ (1<<4)
78#define CS42L51_ADC_CTL_SOFTB (1<<3)
79#define CS42L51_ADC_CTL_ZCROSSB (1<<2)
80#define CS42L51_ADC_CTL_SOFTA (1<<1)
81#define CS42L51_ADC_CTL_ZCROSSA (1<<0)
82
83#define CS42L51_ADC_INPUT 0x07
84#define CS42L51_ADC_INPUT_AINB_MUX(x) (((x)&3)<<6)
85#define CS42L51_ADC_INPUT_AINA_MUX(x) (((x)&3)<<4)
86#define CS42L51_ADC_INPUT_INV_ADCB (1<<3)
87#define CS42L51_ADC_INPUT_INV_ADCA (1<<2)
88#define CS42L51_ADC_INPUT_ADCB_MUTE (1<<1)
89#define CS42L51_ADC_INPUT_ADCA_MUTE (1<<0)
90
91#define CS42L51_DAC_OUT_CTL 0x08
92#define CS42L51_DAC_OUT_CTL_HP_GAIN(x) (((x)&7)<<5)
93#define CS42L51_DAC_OUT_CTL_DAC_SNGVOL (1<<4)
94#define CS42L51_DAC_OUT_CTL_INV_PCMB (1<<3)
95#define CS42L51_DAC_OUT_CTL_INV_PCMA (1<<2)
96#define CS42L51_DAC_OUT_CTL_DACB_MUTE (1<<1)
97#define CS42L51_DAC_OUT_CTL_DACA_MUTE (1<<0)
98
99#define CS42L51_DAC_CTL 0x09
100#define CS42L51_DAC_CTL_DATA_SEL(x) (((x)&3)<<6)
101#define CS42L51_DAC_CTL_FREEZE (1<<5)
102#define CS42L51_DAC_CTL_DEEMPH (1<<3)
103#define CS42L51_DAC_CTL_AMUTE (1<<2)
104#define CS42L51_DAC_CTL_DACSZ(x) (((x)&3)<<0)
105
106#define CS42L51_ALC_PGA_CTL 0x0A
107#define CS42L51_ALC_PGB_CTL 0x0B
108#define CS42L51_ALC_PGX_ALCX_SRDIS (1<<7)
109#define CS42L51_ALC_PGX_ALCX_ZCDIS (1<<6)
110#define CS42L51_ALC_PGX_PGX_VOL(x) (((x)&0x1f)<<0)
111
112#define CS42L51_ADCA_ATT 0x0C
113#define CS42L51_ADCB_ATT 0x0D
114
115#define CS42L51_ADCA_VOL 0x0E
116#define CS42L51_ADCB_VOL 0x0F
117#define CS42L51_PCMA_VOL 0x10
118#define CS42L51_PCMB_VOL 0x11
119#define CS42L51_MIX_MUTE_ADCMIX (1<<7)
120#define CS42L51_MIX_VOLUME(x) (((x)&0x7f)<<0)
121
122#define CS42L51_BEEP_FREQ 0x12
123#define CS42L51_BEEP_VOL 0x13
124#define CS42L51_BEEP_CONF 0x14
125
126#define CS42L51_TONE_CTL 0x15
127#define CS42L51_TONE_CTL_TREB(x) (((x)&0xf)<<4)
128#define CS42L51_TONE_CTL_BASS(x) (((x)&0xf)<<0)
129
130#define CS42L51_AOUTA_VOL 0x16
131#define CS42L51_AOUTB_VOL 0x17
132#define CS42L51_PCM_MIXER 0x18
133#define CS42L51_LIMIT_THRES_DIS 0x19
134#define CS42L51_LIMIT_REL 0x1A
135#define CS42L51_LIMIT_ATT 0x1B
136#define CS42L51_ALC_EN 0x1C
137#define CS42L51_ALC_REL 0x1D
138#define CS42L51_ALC_THRES 0x1E
139#define CS42L51_NOISE_CONF 0x1F
140
141#define CS42L51_STATUS 0x20
142#define CS42L51_STATUS_SP_CLKERR (1<<6)
143#define CS42L51_STATUS_SPEA_OVFL (1<<5)
144#define CS42L51_STATUS_SPEB_OVFL (1<<4)
145#define CS42L51_STATUS_PCMA_OVFL (1<<3)
146#define CS42L51_STATUS_PCMB_OVFL (1<<2)
147#define CS42L51_STATUS_ADCA_OVFL (1<<1)
148#define CS42L51_STATUS_ADCB_OVFL (1<<0)
149
150#define CS42L51_CHARGE_FREQ 0x21
151
152#define CS42L51_FIRSTREG 0x01
153/*
154 * Hack: with register 0x21, it makes 33 registers. Looks like someone in the
155 * i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using
156 * 32 regs
157 */
158#define CS42L51_LASTREG 0x20
159#define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1)
160
161extern struct snd_soc_dai cs42l51_dai;
162extern struct snd_soc_codec_device soc_codec_device_cs42l51;
163#endif
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
index a63191141052..9119836051a4 100644
--- a/sound/soc/codecs/spdif_transciever.c
+++ b/sound/soc/codecs/spdif_transciever.c
@@ -16,8 +16,10 @@
16 16
17#include <linux/module.h> 17#include <linux/module.h>
18#include <linux/moduleparam.h> 18#include <linux/moduleparam.h>
19#include <linux/slab.h>
19#include <sound/soc.h> 20#include <sound/soc.h>
20#include <sound/pcm.h> 21#include <sound/pcm.h>
22#include <sound/initval.h>
21 23
22#include "spdif_transciever.h" 24#include "spdif_transciever.h"
23 25
@@ -26,6 +28,48 @@ MODULE_LICENSE("GPL");
26#define STUB_RATES SNDRV_PCM_RATE_8000_96000 28#define STUB_RATES SNDRV_PCM_RATE_8000_96000
27#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE 29#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE
28 30
31static struct snd_soc_codec *spdif_dit_codec;
32
33static int spdif_dit_codec_probe(struct platform_device *pdev)
34{
35 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
36 struct snd_soc_codec *codec;
37 int ret;
38
39 if (spdif_dit_codec == NULL) {
40 dev_err(&pdev->dev, "Codec device not registered\n");
41 return -ENODEV;
42 }
43
44 socdev->card->codec = spdif_dit_codec;
45 codec = spdif_dit_codec;
46
47 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
48 if (ret < 0) {
49 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
50 goto err_create_pcms;
51 }
52
53 return 0;
54
55err_create_pcms:
56 return ret;
57}
58
59static int spdif_dit_codec_remove(struct platform_device *pdev)
60{
61 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
62
63 snd_soc_free_pcms(socdev);
64
65 return 0;
66}
67
68struct snd_soc_codec_device soc_codec_dev_spdif_dit = {
69 .probe = spdif_dit_codec_probe,
70 .remove = spdif_dit_codec_remove,
71}; EXPORT_SYMBOL_GPL(soc_codec_dev_spdif_dit);
72
29struct snd_soc_dai dit_stub_dai = { 73struct snd_soc_dai dit_stub_dai = {
30 .name = "DIT", 74 .name = "DIT",
31 .playback = { 75 .playback = {
@@ -40,13 +84,61 @@ EXPORT_SYMBOL_GPL(dit_stub_dai);
40 84
41static int spdif_dit_probe(struct platform_device *pdev) 85static int spdif_dit_probe(struct platform_device *pdev)
42{ 86{
87 struct snd_soc_codec *codec;
88 int ret;
89
90 if (spdif_dit_codec) {
91 dev_err(&pdev->dev, "Another Codec is registered\n");
92 ret = -EINVAL;
93 goto err_reg_codec;
94 }
95
96 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
97 if (codec == NULL)
98 return -ENOMEM;
99
100 codec->dev = &pdev->dev;
101
102 mutex_init(&codec->mutex);
103
104 INIT_LIST_HEAD(&codec->dapm_widgets);
105 INIT_LIST_HEAD(&codec->dapm_paths);
106
107 codec->name = "spdif-dit";
108 codec->owner = THIS_MODULE;
109 codec->dai = &dit_stub_dai;
110 codec->num_dai = 1;
111
112 spdif_dit_codec = codec;
113
114 ret = snd_soc_register_codec(codec);
115 if (ret < 0) {
116 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
117 goto err_reg_codec;
118 }
119
43 dit_stub_dai.dev = &pdev->dev; 120 dit_stub_dai.dev = &pdev->dev;
44 return snd_soc_register_dai(&dit_stub_dai); 121 ret = snd_soc_register_dai(&dit_stub_dai);
122 if (ret < 0) {
123 dev_err(codec->dev, "Failed to register dai: %d\n", ret);
124 goto err_reg_dai;
125 }
126
127 return 0;
128
129err_reg_dai:
130 snd_soc_unregister_codec(codec);
131err_reg_codec:
132 kfree(spdif_dit_codec);
133 return ret;
45} 134}
46 135
47static int spdif_dit_remove(struct platform_device *pdev) 136static int spdif_dit_remove(struct platform_device *pdev)
48{ 137{
49 snd_soc_unregister_dai(&dit_stub_dai); 138 snd_soc_unregister_dai(&dit_stub_dai);
139 snd_soc_unregister_codec(spdif_dit_codec);
140 kfree(spdif_dit_codec);
141 spdif_dit_codec = NULL;
50 return 0; 142 return 0;
51} 143}
52 144
diff --git a/sound/soc/codecs/spdif_transciever.h b/sound/soc/codecs/spdif_transciever.h
index 296f2eb6c4ef..1e102124f546 100644
--- a/sound/soc/codecs/spdif_transciever.h
+++ b/sound/soc/codecs/spdif_transciever.h
@@ -12,6 +12,7 @@
12#ifndef CODEC_STUBS_H 12#ifndef CODEC_STUBS_H
13#define CODEC_STUBS_H 13#define CODEC_STUBS_H
14 14
15extern struct snd_soc_codec_device soc_codec_dev_spdif_dit;
15extern struct snd_soc_dai dit_stub_dai; 16extern struct snd_soc_dai dit_stub_dai;
16 17
17#endif /* CODEC_STUBS_H */ 18#endif /* CODEC_STUBS_H */
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index c018772cc430..dd8d909788c1 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -30,8 +30,6 @@
30 30
31#include "wm8990.h" 31#include "wm8990.h"
32 32
33#define WM8990_VERSION "0.2"
34
35/* codec private data */ 33/* codec private data */
36struct wm8990_priv { 34struct wm8990_priv {
37 unsigned int sysclk; 35 unsigned int sysclk;
@@ -1511,8 +1509,6 @@ static int wm8990_probe(struct platform_device *pdev)
1511 struct wm8990_priv *wm8990; 1509 struct wm8990_priv *wm8990;
1512 int ret; 1510 int ret;
1513 1511
1514 pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION);
1515
1516 setup = socdev->codec_data; 1512 setup = socdev->codec_data;
1517 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 1513 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
1518 if (codec == NULL) 1514 if (codec == NULL)
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
new file mode 100644
index 000000000000..16ec2a2dba4d
--- /dev/null
+++ b/sound/soc/kirkwood/Kconfig
@@ -0,0 +1,20 @@
1config SND_KIRKWOOD_SOC
2 tristate "SoC Audio for the Marvell Kirkwood chip"
3 depends on ARCH_KIRKWOOD
4 help
5 Say Y or M if you want to add support for codecs attached to
6 the Kirkwood I2S interface. You will also need to select the
7 audio interfaces to support below.
8
9config SND_KIRKWOOD_SOC_I2S
10 tristate
11
12config SND_KIRKWOOD_SOC_OPENRD
13 tristate "SoC Audio support for Kirkwood Openrd Client"
14 depends on SND_KIRKWOOD_SOC && MACH_OPENRD_CLIENT
15 select SND_KIRKWOOD_SOC_I2S
16 select SND_SOC_CS42L51
17 help
18 Say Y if you want to add support for SoC audio on
19 Openrd Client.
20
diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile
new file mode 100644
index 000000000000..33a16dcab5b5
--- /dev/null
+++ b/sound/soc/kirkwood/Makefile
@@ -0,0 +1,9 @@
1snd-soc-kirkwood-objs := kirkwood-dma.o
2snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o
3
4obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
5obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o
6
7snd-soc-openrd-objs := kirkwood-openrd.o
8
9obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
new file mode 100644
index 000000000000..a30205be3e2b
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -0,0 +1,383 @@
1/*
2 * kirkwood-dma.c
3 *
4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/device.h>
15#include <linux/io.h>
16#include <linux/slab.h>
17#include <linux/interrupt.h>
18#include <linux/dma-mapping.h>
19#include <linux/mbus.h>
20#include <sound/soc.h>
21#include "kirkwood-dma.h"
22#include "kirkwood.h"
23
24#define KIRKWOOD_RATES \
25 (SNDRV_PCM_RATE_44100 | \
26 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
27#define KIRKWOOD_FORMATS \
28 (SNDRV_PCM_FMTBIT_S16_LE | \
29 SNDRV_PCM_FMTBIT_S24_LE | \
30 SNDRV_PCM_FMTBIT_S32_LE)
31
32struct kirkwood_dma_priv {
33 struct snd_pcm_substream *play_stream;
34 struct snd_pcm_substream *rec_stream;
35 struct kirkwood_dma_data *data;
36};
37
38static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
39 .info = (SNDRV_PCM_INFO_INTERLEAVED |
40 SNDRV_PCM_INFO_MMAP |
41 SNDRV_PCM_INFO_MMAP_VALID |
42 SNDRV_PCM_INFO_BLOCK_TRANSFER |
43 SNDRV_PCM_INFO_PAUSE),
44 .formats = KIRKWOOD_FORMATS,
45 .rates = KIRKWOOD_RATES,
46 .rate_min = 44100,
47 .rate_max = 96000,
48 .channels_min = 1,
49 .channels_max = 2,
50 .buffer_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS,
51 .period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES,
52 .period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES,
53 .periods_min = KIRKWOOD_SND_MIN_PERIODS,
54 .periods_max = KIRKWOOD_SND_MAX_PERIODS,
55 .fifo_size = 0,
56};
57
58static u64 kirkwood_dma_dmamask = 0xFFFFFFFFUL;
59
60static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
61{
62 struct kirkwood_dma_priv *prdata = dev_id;
63 struct kirkwood_dma_data *priv = prdata->data;
64 unsigned long mask, status, cause;
65
66 mask = readl(priv->io + KIRKWOOD_INT_MASK);
67 status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask;
68
69 cause = readl(priv->io + KIRKWOOD_ERR_CAUSE);
70 if (unlikely(cause)) {
71 printk(KERN_WARNING "%s: got err interrupt 0x%lx\n",
72 __func__, cause);
73 writel(cause, priv->io + KIRKWOOD_ERR_CAUSE);
74 return IRQ_HANDLED;
75 }
76
77 /* we've enabled only bytes interrupts ... */
78 if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
79 KIRKWOOD_INT_CAUSE_REC_BYTES)) {
80 printk(KERN_WARNING "%s: unexpected interrupt %lx\n",
81 __func__, status);
82 return IRQ_NONE;
83 }
84
85 /* ack int */
86 writel(status, priv->io + KIRKWOOD_INT_CAUSE);
87
88 if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
89 snd_pcm_period_elapsed(prdata->play_stream);
90
91 if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
92 snd_pcm_period_elapsed(prdata->rec_stream);
93
94 return IRQ_HANDLED;
95}
96
97static void kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
98 unsigned long dma,
99 struct mbus_dram_target_info *dram)
100{
101 int i;
102
103 /* First disable and clear windows */
104 writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
105 writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
106
107 /* try to find matching cs for current dma address */
108 for (i = 0; i < dram->num_cs; i++) {
109 struct mbus_dram_window *cs = dram->cs + i;
110 if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
111 writel(cs->base & 0xffff0000,
112 base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
113 writel(((cs->size - 1) & 0xffff0000) |
114 (cs->mbus_attr << 8) |
115 (dram->mbus_dram_target_id << 4) | 1,
116 base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
117 }
118 }
119}
120
121static int kirkwood_dma_open(struct snd_pcm_substream *substream)
122{
123 int err;
124 struct snd_pcm_runtime *runtime = substream->runtime;
125 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
126 struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
127 struct kirkwood_dma_data *priv;
128 struct kirkwood_dma_priv *prdata = cpu_dai->private_data;
129 unsigned long addr;
130
131 priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
132 snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
133
134 /* Ensure that all constraints linked to dma burst are fullfilled */
135 err = snd_pcm_hw_constraint_minmax(runtime,
136 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
137 priv->burst * 2,
138 KIRKWOOD_AUDIO_BUF_MAX-1);
139 if (err < 0)
140 return err;
141
142 err = snd_pcm_hw_constraint_step(runtime, 0,
143 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
144 priv->burst);
145 if (err < 0)
146 return err;
147
148 err = snd_pcm_hw_constraint_step(substream->runtime, 0,
149 SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
150 priv->burst);
151 if (err < 0)
152 return err;
153
154 if (soc_runtime->dai->cpu_dai->private_data == NULL) {
155 prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL);
156 if (prdata == NULL)
157 return -ENOMEM;
158
159 prdata->data = priv;
160
161 err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
162 "kirkwood-i2s", prdata);
163 if (err) {
164 kfree(prdata);
165 return -EBUSY;
166 }
167
168 soc_runtime->dai->cpu_dai->private_data = prdata;
169
170 /*
171 * Enable Error interrupts. We're only ack'ing them but
172 * it's usefull for diagnostics
173 */
174 writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK);
175 }
176
177 addr = virt_to_phys(substream->dma_buffer.area);
178 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
179 prdata->play_stream = substream;
180 kirkwood_dma_conf_mbus_windows(priv->io,
181 KIRKWOOD_PLAYBACK_WIN, addr, priv->dram);
182 } else {
183 prdata->rec_stream = substream;
184 kirkwood_dma_conf_mbus_windows(priv->io,
185 KIRKWOOD_RECORD_WIN, addr, priv->dram);
186 }
187
188 return 0;
189}
190
191static int kirkwood_dma_close(struct snd_pcm_substream *substream)
192{
193 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
194 struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
195 struct kirkwood_dma_priv *prdata = cpu_dai->private_data;
196 struct kirkwood_dma_data *priv;
197
198 priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
199
200 if (!prdata || !priv)
201 return 0;
202
203 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
204 prdata->play_stream = NULL;
205 else
206 prdata->rec_stream = NULL;
207
208 if (!prdata->play_stream && !prdata->rec_stream) {
209 writel(0, priv->io + KIRKWOOD_ERR_MASK);
210 free_irq(priv->irq, prdata);
211 kfree(prdata);
212 soc_runtime->dai->cpu_dai->private_data = NULL;
213 }
214
215 return 0;
216}
217
218static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
219 struct snd_pcm_hw_params *params)
220{
221 struct snd_pcm_runtime *runtime = substream->runtime;
222
223 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
224 runtime->dma_bytes = params_buffer_bytes(params);
225
226 return 0;
227}
228
229static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
230{
231 snd_pcm_set_runtime_buffer(substream, NULL);
232 return 0;
233}
234
235static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
236{
237 struct snd_pcm_runtime *runtime = substream->runtime;
238 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
239 struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
240 struct kirkwood_dma_data *priv;
241 unsigned long size, count;
242
243 priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
244
245 /* compute buffer size in term of "words" as requested in specs */
246 size = frames_to_bytes(runtime, runtime->buffer_size);
247 size = (size>>2)-1;
248 count = snd_pcm_lib_period_bytes(substream);
249
250 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
251 writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
252 writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
253 writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
254 } else {
255 writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
256 writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
257 writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
258 }
259
260
261 return 0;
262}
263
264static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
265 *substream)
266{
267 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
268 struct snd_soc_dai *cpu_dai = soc_runtime->dai->cpu_dai;
269 struct kirkwood_dma_data *priv;
270 snd_pcm_uframes_t count;
271
272 priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
273
274 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
275 count = bytes_to_frames(substream->runtime,
276 readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
277 else
278 count = bytes_to_frames(substream->runtime,
279 readl(priv->io + KIRKWOOD_REC_BYTE_COUNT));
280
281 return count;
282}
283
284struct snd_pcm_ops kirkwood_dma_ops = {
285 .open = kirkwood_dma_open,
286 .close = kirkwood_dma_close,
287 .ioctl = snd_pcm_lib_ioctl,
288 .hw_params = kirkwood_dma_hw_params,
289 .hw_free = kirkwood_dma_hw_free,
290 .prepare = kirkwood_dma_prepare,
291 .pointer = kirkwood_dma_pointer,
292};
293
294static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
295 int stream)
296{
297 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
298 struct snd_dma_buffer *buf = &substream->dma_buffer;
299 size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
300
301 buf->dev.type = SNDRV_DMA_TYPE_DEV;
302 buf->dev.dev = pcm->card->dev;
303 buf->area = dma_alloc_coherent(pcm->card->dev, size,
304 &buf->addr, GFP_KERNEL);
305 if (!buf->area)
306 return -ENOMEM;
307 buf->bytes = size;
308 buf->private_data = NULL;
309
310 return 0;
311}
312
313static int kirkwood_dma_new(struct snd_card *card,
314 struct snd_soc_dai *dai, struct snd_pcm *pcm)
315{
316 int ret;
317
318 if (!card->dev->dma_mask)
319 card->dev->dma_mask = &kirkwood_dma_dmamask;
320 if (!card->dev->coherent_dma_mask)
321 card->dev->coherent_dma_mask = 0xffffffff;
322
323 if (dai->playback.channels_min) {
324 ret = kirkwood_dma_preallocate_dma_buffer(pcm,
325 SNDRV_PCM_STREAM_PLAYBACK);
326 if (ret)
327 return ret;
328 }
329
330 if (dai->capture.channels_min) {
331 ret = kirkwood_dma_preallocate_dma_buffer(pcm,
332 SNDRV_PCM_STREAM_CAPTURE);
333 if (ret)
334 return ret;
335 }
336
337 return 0;
338}
339
340static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
341{
342 struct snd_pcm_substream *substream;
343 struct snd_dma_buffer *buf;
344 int stream;
345
346 for (stream = 0; stream < 2; stream++) {
347 substream = pcm->streams[stream].substream;
348 if (!substream)
349 continue;
350 buf = &substream->dma_buffer;
351 if (!buf->area)
352 continue;
353
354 dma_free_coherent(pcm->card->dev, buf->bytes,
355 buf->area, buf->addr);
356 buf->area = NULL;
357 }
358}
359
360struct snd_soc_platform kirkwood_soc_platform = {
361 .name = "kirkwood-dma",
362 .pcm_ops = &kirkwood_dma_ops,
363 .pcm_new = kirkwood_dma_new,
364 .pcm_free = kirkwood_dma_free_dma_buffers,
365};
366EXPORT_SYMBOL_GPL(kirkwood_soc_platform);
367
368static int __init kirkwood_soc_platform_init(void)
369{
370 return snd_soc_register_platform(&kirkwood_soc_platform);
371}
372module_init(kirkwood_soc_platform_init);
373
374static void __exit kirkwood_soc_platform_exit(void)
375{
376 snd_soc_unregister_platform(&kirkwood_soc_platform);
377}
378module_exit(kirkwood_soc_platform_exit);
379
380MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
381MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
382MODULE_LICENSE("GPL");
383
diff --git a/sound/soc/kirkwood/kirkwood-dma.h b/sound/soc/kirkwood/kirkwood-dma.h
new file mode 100644
index 000000000000..ba4454cd34f1
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-dma.h
@@ -0,0 +1,17 @@
1/*
2 * kirkwood-dma.h
3 *
4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#ifndef _KIRKWOOD_DMA_H
13#define _KIRKWOOD_DMA_H
14
15extern struct snd_soc_platform kirkwood_soc_platform;
16
17#endif
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
new file mode 100644
index 000000000000..0adc59778d5a
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -0,0 +1,485 @@
1/*
2 * kirkwood-i2s.c
3 *
4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/io.h>
16#include <linux/slab.h>
17#include <linux/mbus.h>
18#include <linux/delay.h>
19#include <sound/pcm.h>
20#include <sound/pcm_params.h>
21#include <sound/soc.h>
22#include <plat/audio.h>
23#include "kirkwood-i2s.h"
24#include "kirkwood.h"
25
26#define DRV_NAME "kirkwood-i2s"
27
28#define KIRKWOOD_I2S_RATES \
29 (SNDRV_PCM_RATE_44100 | \
30 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
31#define KIRKWOOD_I2S_FORMATS \
32 (SNDRV_PCM_FMTBIT_S16_LE | \
33 SNDRV_PCM_FMTBIT_S24_LE | \
34 SNDRV_PCM_FMTBIT_S32_LE)
35
36
37struct snd_soc_dai kirkwood_i2s_dai;
38static struct kirkwood_dma_data *priv;
39
40static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
41 unsigned int fmt)
42{
43 unsigned long mask;
44 unsigned long value;
45
46 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
47 case SND_SOC_DAIFMT_RIGHT_J:
48 mask = KIRKWOOD_I2S_CTL_RJ;
49 break;
50 case SND_SOC_DAIFMT_LEFT_J:
51 mask = KIRKWOOD_I2S_CTL_LJ;
52 break;
53 case SND_SOC_DAIFMT_I2S:
54 mask = KIRKWOOD_I2S_CTL_I2S;
55 break;
56 default:
57 return -EINVAL;
58 }
59
60 /*
61 * Set same format for playback and record
62 * This avoids some troubles.
63 */
64 value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
65 value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
66 value |= mask;
67 writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
68
69 value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
70 value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
71 value |= mask;
72 writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
73
74 return 0;
75}
76
77static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
78{
79 unsigned long value;
80
81 value = KIRKWOOD_DCO_CTL_OFFSET_0;
82 switch (rate) {
83 default:
84 case 44100:
85 value |= KIRKWOOD_DCO_CTL_FREQ_11;
86 break;
87 case 48000:
88 value |= KIRKWOOD_DCO_CTL_FREQ_12;
89 break;
90 case 96000:
91 value |= KIRKWOOD_DCO_CTL_FREQ_24;
92 break;
93 }
94 writel(value, io + KIRKWOOD_DCO_CTL);
95
96 /* wait for dco locked */
97 do {
98 cpu_relax();
99 value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
100 value &= KIRKWOOD_DCO_SPCR_STATUS;
101 } while (value == 0);
102}
103
104static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
105 struct snd_pcm_hw_params *params,
106 struct snd_soc_dai *dai)
107{
108 unsigned int i2s_reg, reg;
109 unsigned long i2s_value, value;
110
111 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
112 i2s_reg = KIRKWOOD_I2S_PLAYCTL;
113 reg = KIRKWOOD_PLAYCTL;
114 } else {
115 i2s_reg = KIRKWOOD_I2S_RECCTL;
116 reg = KIRKWOOD_RECCTL;
117 }
118
119 /* set dco conf */
120 kirkwood_set_dco(priv->io, params_rate(params));
121
122 i2s_value = readl(priv->io+i2s_reg);
123 i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
124
125 value = readl(priv->io+reg);
126 value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK;
127
128 /*
129 * Size settings in play/rec i2s control regs and play/rec control
130 * regs must be the same.
131 */
132 switch (params_format(params)) {
133 case SNDRV_PCM_FORMAT_S16_LE:
134 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
135 value |= KIRKWOOD_PLAYCTL_SIZE_16_C;
136 break;
137 /*
138 * doesn't work... S20_3LE != kirkwood 20bit format ?
139 *
140 case SNDRV_PCM_FORMAT_S20_3LE:
141 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
142 value |= KIRKWOOD_PLAYCTL_SIZE_20;
143 break;
144 */
145 case SNDRV_PCM_FORMAT_S24_LE:
146 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
147 value |= KIRKWOOD_PLAYCTL_SIZE_24;
148 break;
149 case SNDRV_PCM_FORMAT_S32_LE:
150 i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
151 value |= KIRKWOOD_PLAYCTL_SIZE_32;
152 break;
153 default:
154 return -EINVAL;
155 }
156 writel(i2s_value, priv->io+i2s_reg);
157 writel(value, priv->io+reg);
158
159 return 0;
160}
161
162static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
163 int cmd, struct snd_soc_dai *dai)
164{
165 unsigned long value;
166
167 /*
168 * specs says KIRKWOOD_PLAYCTL must be read 2 times before
169 * changing it. So read 1 time here and 1 later.
170 */
171 value = readl(priv->io + KIRKWOOD_PLAYCTL);
172
173 switch (cmd) {
174 case SNDRV_PCM_TRIGGER_START:
175 /* stop audio, enable interrupts */
176 value = readl(priv->io + KIRKWOOD_PLAYCTL);
177 value |= KIRKWOOD_PLAYCTL_PAUSE;
178 writel(value, priv->io + KIRKWOOD_PLAYCTL);
179
180 value = readl(priv->io + KIRKWOOD_INT_MASK);
181 value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
182 writel(value, priv->io + KIRKWOOD_INT_MASK);
183
184 /* configure audio & enable i2s playback */
185 value = readl(priv->io + KIRKWOOD_PLAYCTL);
186 value &= ~KIRKWOOD_PLAYCTL_BURST_MASK;
187 value &= ~(KIRKWOOD_PLAYCTL_PAUSE|KIRKWOOD_PLAYCTL_SPDIF_EN);
188
189 if (priv->burst == 32)
190 value |= KIRKWOOD_PLAYCTL_BURST_32;
191 else
192 value |= KIRKWOOD_PLAYCTL_BURST_128;
193 value |= KIRKWOOD_PLAYCTL_I2S_EN;
194 writel(value, priv->io + KIRKWOOD_PLAYCTL);
195 break;
196
197 case SNDRV_PCM_TRIGGER_STOP:
198 /* stop audio, disable interrupts */
199 value = readl(priv->io + KIRKWOOD_PLAYCTL);
200 value |= KIRKWOOD_PLAYCTL_PAUSE;
201 writel(value, priv->io + KIRKWOOD_PLAYCTL);
202
203 value = readl(priv->io + KIRKWOOD_INT_MASK);
204 value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
205 writel(value, priv->io + KIRKWOOD_INT_MASK);
206
207 /* disable all playbacks */
208 value = readl(priv->io + KIRKWOOD_PLAYCTL);
209 value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
210 writel(value, priv->io + KIRKWOOD_PLAYCTL);
211 break;
212
213 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
214 case SNDRV_PCM_TRIGGER_SUSPEND:
215 value = readl(priv->io + KIRKWOOD_PLAYCTL);
216 value |= KIRKWOOD_PLAYCTL_PAUSE;
217 writel(value, priv->io + KIRKWOOD_PLAYCTL);
218 break;
219
220 case SNDRV_PCM_TRIGGER_RESUME:
221 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
222 value = readl(priv->io + KIRKWOOD_PLAYCTL);
223 value &= ~KIRKWOOD_PLAYCTL_PAUSE;
224 writel(value, priv->io + KIRKWOOD_PLAYCTL);
225 break;
226
227 default:
228 return -EINVAL;
229 }
230
231 return 0;
232}
233
234static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
235 int cmd, struct snd_soc_dai *dai)
236{
237 unsigned long value;
238
239 value = readl(priv->io + KIRKWOOD_RECCTL);
240
241 switch (cmd) {
242 case SNDRV_PCM_TRIGGER_START:
243 /* stop audio, enable interrupts */
244 value = readl(priv->io + KIRKWOOD_RECCTL);
245 value |= KIRKWOOD_RECCTL_PAUSE;
246 writel(value, priv->io + KIRKWOOD_RECCTL);
247
248 value = readl(priv->io + KIRKWOOD_INT_MASK);
249 value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
250 writel(value, priv->io + KIRKWOOD_INT_MASK);
251
252 /* configure audio & enable i2s record */
253 value = readl(priv->io + KIRKWOOD_RECCTL);
254 value &= ~KIRKWOOD_RECCTL_BURST_MASK;
255 value &= ~KIRKWOOD_RECCTL_MONO;
256 value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_SPDIF_EN);
257
258 if (priv->burst == 32)
259 value |= KIRKWOOD_RECCTL_BURST_32;
260 else
261 value |= KIRKWOOD_RECCTL_BURST_128;
262 value |= KIRKWOOD_RECCTL_I2S_EN;
263
264 writel(value, priv->io + KIRKWOOD_RECCTL);
265 break;
266
267 case SNDRV_PCM_TRIGGER_STOP:
268 /* stop audio, disable interrupts */
269 value = readl(priv->io + KIRKWOOD_RECCTL);
270 value |= KIRKWOOD_RECCTL_PAUSE;
271 writel(value, priv->io + KIRKWOOD_RECCTL);
272
273 value = readl(priv->io + KIRKWOOD_INT_MASK);
274 value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
275 writel(value, priv->io + KIRKWOOD_INT_MASK);
276
277 /* disable all records */
278 value = readl(priv->io + KIRKWOOD_RECCTL);
279 value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
280 writel(value, priv->io + KIRKWOOD_RECCTL);
281 break;
282
283 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
284 case SNDRV_PCM_TRIGGER_SUSPEND:
285 value = readl(priv->io + KIRKWOOD_RECCTL);
286 value |= KIRKWOOD_RECCTL_PAUSE;
287 writel(value, priv->io + KIRKWOOD_RECCTL);
288 break;
289
290 case SNDRV_PCM_TRIGGER_RESUME:
291 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
292 value = readl(priv->io + KIRKWOOD_RECCTL);
293 value &= ~KIRKWOOD_RECCTL_PAUSE;
294 writel(value, priv->io + KIRKWOOD_RECCTL);
295 break;
296
297 default:
298 return -EINVAL;
299 break;
300 }
301
302 return 0;
303}
304
305static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
306 struct snd_soc_dai *dai)
307{
308 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
309 return kirkwood_i2s_play_trigger(substream, cmd, dai);
310 else
311 return kirkwood_i2s_rec_trigger(substream, cmd, dai);
312
313 return 0;
314}
315
316static int kirkwood_i2s_probe(struct platform_device *pdev,
317 struct snd_soc_dai *dai)
318{
319 unsigned long value;
320 unsigned int reg_data;
321
322 /* put system in a "safe" state : */
323 /* disable audio interrupts */
324 writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
325 writel(0, priv->io + KIRKWOOD_INT_MASK);
326
327 reg_data = readl(priv->io + 0x1200);
328 reg_data &= (~(0x333FF8));
329 reg_data |= 0x111D18;
330 writel(reg_data, priv->io + 0x1200);
331
332 msleep(500);
333
334 reg_data = readl(priv->io + 0x1200);
335 reg_data &= (~(0x333FF8));
336 reg_data |= 0x111D18;
337 writel(reg_data, priv->io + 0x1200);
338
339 /* disable playback/record */
340 value = readl(priv->io + KIRKWOOD_PLAYCTL);
341 value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
342 writel(value, priv->io + KIRKWOOD_PLAYCTL);
343
344 value = readl(priv->io + KIRKWOOD_RECCTL);
345 value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
346 writel(value, priv->io + KIRKWOOD_RECCTL);
347
348 return 0;
349
350}
351
352static void kirkwood_i2s_remove(struct platform_device *pdev,
353 struct snd_soc_dai *dai)
354{
355}
356
357static struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
358 .trigger = kirkwood_i2s_trigger,
359 .hw_params = kirkwood_i2s_hw_params,
360 .set_fmt = kirkwood_i2s_set_fmt,
361};
362
363
364struct snd_soc_dai kirkwood_i2s_dai = {
365 .name = DRV_NAME,
366 .id = 0,
367 .probe = kirkwood_i2s_probe,
368 .remove = kirkwood_i2s_remove,
369 .playback = {
370 .channels_min = 1,
371 .channels_max = 2,
372 .rates = KIRKWOOD_I2S_RATES,
373 .formats = KIRKWOOD_I2S_FORMATS,},
374 .capture = {
375 .channels_min = 1,
376 .channels_max = 2,
377 .rates = KIRKWOOD_I2S_RATES,
378 .formats = KIRKWOOD_I2S_FORMATS,},
379 .ops = &kirkwood_i2s_dai_ops,
380};
381EXPORT_SYMBOL_GPL(kirkwood_i2s_dai);
382
383static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev)
384{
385 struct resource *mem;
386 struct kirkwood_asoc_platform_data *data =
387 pdev->dev.platform_data;
388 int err;
389
390 priv = kzalloc(sizeof(struct kirkwood_dma_data), GFP_KERNEL);
391 if (!priv) {
392 dev_err(&pdev->dev, "allocation failed\n");
393 err = -ENOMEM;
394 goto error;
395 }
396
397 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
398 if (!mem) {
399 dev_err(&pdev->dev, "platform_get_resource failed\n");
400 err = -ENXIO;
401 goto err_alloc;
402 }
403
404 priv->mem = request_mem_region(mem->start, SZ_16K, DRV_NAME);
405 if (!priv->mem) {
406 dev_err(&pdev->dev, "request_mem_region failed\n");
407 err = -EBUSY;
408 goto error;
409 }
410
411 priv->io = ioremap(priv->mem->start, SZ_16K);
412 if (!priv->io) {
413 dev_err(&pdev->dev, "ioremap failed\n");
414 err = -ENOMEM;
415 goto err_iomem;
416 }
417
418 priv->irq = platform_get_irq(pdev, 0);
419 if (priv->irq <= 0) {
420 dev_err(&pdev->dev, "platform_get_irq failed\n");
421 err = -ENXIO;
422 goto err_ioremap;
423 }
424
425 if (!data || !data->dram) {
426 dev_err(&pdev->dev, "no platform data ?!\n");
427 err = -EINVAL;
428 goto err_ioremap;
429 }
430
431 priv->dram = data->dram;
432 priv->burst = data->burst;
433
434 kirkwood_i2s_dai.capture.dma_data = priv;
435 kirkwood_i2s_dai.playback.dma_data = priv;
436
437 return snd_soc_register_dai(&kirkwood_i2s_dai);
438
439err_ioremap:
440 iounmap(priv->io);
441err_iomem:
442 release_mem_region(priv->mem->start, SZ_16K);
443err_alloc:
444 kfree(priv);
445error:
446 return err;
447}
448
449static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev)
450{
451 if (priv) {
452 iounmap(priv->io);
453 release_mem_region(priv->mem->start, SZ_16K);
454 kfree(priv);
455 }
456 snd_soc_unregister_dai(&kirkwood_i2s_dai);
457 return 0;
458}
459
460static struct platform_driver kirkwood_i2s_driver = {
461 .probe = kirkwood_i2s_dev_probe,
462 .remove = kirkwood_i2s_dev_remove,
463 .driver = {
464 .name = DRV_NAME,
465 .owner = THIS_MODULE,
466 },
467};
468
469static int __init kirkwood_i2s_init(void)
470{
471 return platform_driver_register(&kirkwood_i2s_driver);
472}
473module_init(kirkwood_i2s_init);
474
475static void __exit kirkwood_i2s_exit(void)
476{
477 platform_driver_unregister(&kirkwood_i2s_driver);
478}
479module_exit(kirkwood_i2s_exit);
480
481/* Module information */
482MODULE_AUTHOR("Arnaud Patard, <apatard@mandriva.com>");
483MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
484MODULE_LICENSE("GPL");
485MODULE_ALIAS("platform:kirkwood-i2s");
diff --git a/sound/soc/kirkwood/kirkwood-i2s.h b/sound/soc/kirkwood/kirkwood-i2s.h
new file mode 100644
index 000000000000..c5595c616d7a
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-i2s.h
@@ -0,0 +1,17 @@
1/*
2 * kirkwood-i2s.h
3 *
4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#ifndef _KIRKWOOD_I2S_H
13#define _KIRKWOOD_I2S_H
14
15extern struct snd_soc_dai kirkwood_i2s_dai;
16
17#endif
diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c
new file mode 100644
index 000000000000..0353d06bc41a
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood-openrd.c
@@ -0,0 +1,126 @@
1/*
2 * kirkwood-openrd.c
3 *
4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/interrupt.h>
15#include <linux/platform_device.h>
16#include <linux/slab.h>
17#include <sound/soc.h>
18#include <mach/kirkwood.h>
19#include <plat/audio.h>
20#include <asm/mach-types.h>
21#include "kirkwood-i2s.h"
22#include "kirkwood-dma.h"
23#include "../codecs/cs42l51.h"
24
25static int openrd_client_hw_params(struct snd_pcm_substream *substream,
26 struct snd_pcm_hw_params *params)
27{
28 struct snd_soc_pcm_runtime *rtd = substream->private_data;
29 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
30 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
31 int ret;
32 unsigned int freq, fmt;
33
34 fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
35 ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
36 if (ret < 0)
37 return ret;
38
39 ret = snd_soc_dai_set_fmt(codec_dai, fmt);
40 if (ret < 0)
41 return ret;
42
43 switch (params_rate(params)) {
44 default:
45 case 44100:
46 freq = 11289600;
47 break;
48 case 48000:
49 freq = 12288000;
50 break;
51 case 96000:
52 freq = 24576000;
53 break;
54 }
55
56 return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
57
58}
59
60static struct snd_soc_ops openrd_client_ops = {
61 .hw_params = openrd_client_hw_params,
62};
63
64
65static struct snd_soc_dai_link openrd_client_dai[] = {
66{
67 .name = "CS42L51",
68 .stream_name = "CS42L51 HiFi",
69 .cpu_dai = &kirkwood_i2s_dai,
70 .codec_dai = &cs42l51_dai,
71 .ops = &openrd_client_ops,
72},
73};
74
75
76static struct snd_soc_card openrd_client = {
77 .name = "OpenRD Client",
78 .platform = &kirkwood_soc_platform,
79 .dai_link = openrd_client_dai,
80 .num_links = ARRAY_SIZE(openrd_client_dai),
81};
82
83static struct snd_soc_device openrd_client_snd_devdata = {
84 .card = &openrd_client,
85 .codec_dev = &soc_codec_device_cs42l51,
86};
87
88static struct platform_device *openrd_client_snd_device;
89
90static int __init openrd_client_init(void)
91{
92 int ret;
93
94 if (!machine_is_openrd_client())
95 return 0;
96
97 openrd_client_snd_device = platform_device_alloc("soc-audio", -1);
98 if (!openrd_client_snd_device)
99 return -ENOMEM;
100
101 platform_set_drvdata(openrd_client_snd_device,
102 &openrd_client_snd_devdata);
103 openrd_client_snd_devdata.dev = &openrd_client_snd_device->dev;
104
105 ret = platform_device_add(openrd_client_snd_device);
106 if (ret) {
107 printk(KERN_ERR "%s: platform_device_add failed\n", __func__);
108 platform_device_put(openrd_client_snd_device);
109 }
110
111 return ret;
112}
113
114static void __exit openrd_client_exit(void)
115{
116 platform_device_unregister(openrd_client_snd_device);
117}
118
119module_init(openrd_client_init);
120module_exit(openrd_client_exit);
121
122/* Module information */
123MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
124MODULE_DESCRIPTION("ALSA SoC OpenRD Client");
125MODULE_LICENSE("GPL");
126MODULE_ALIAS("platform:soc-audio");
diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h
new file mode 100644
index 000000000000..b6e4f68d71dd
--- /dev/null
+++ b/sound/soc/kirkwood/kirkwood.h
@@ -0,0 +1,126 @@
1/*
2 * kirkwood.h
3 *
4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#ifndef _KIRKWOOD_AUDIO_H
13#define _KIRKWOOD_AUDIO_H
14
15#define KIRKWOOD_RECORD_WIN 0
16#define KIRKWOOD_PLAYBACK_WIN 1
17#define KIRKWOOD_MAX_AUDIO_WIN 2
18
19#define KIRKWOOD_AUDIO_WIN_BASE_REG(win) (0xA00 + ((win)<<3))
20#define KIRKWOOD_AUDIO_WIN_CTRL_REG(win) (0xA04 + ((win)<<3))
21
22
23#define KIRKWOOD_RECCTL 0x1000
24#define KIRKWOOD_RECCTL_SPDIF_EN (1<<11)
25#define KIRKWOOD_RECCTL_I2S_EN (1<<10)
26#define KIRKWOOD_RECCTL_PAUSE (1<<9)
27#define KIRKWOOD_RECCTL_MUTE (1<<8)
28#define KIRKWOOD_RECCTL_BURST_MASK (3<<5)
29#define KIRKWOOD_RECCTL_BURST_128 (2<<5)
30#define KIRKWOOD_RECCTL_BURST_32 (1<<5)
31#define KIRKWOOD_RECCTL_MONO (1<<4)
32#define KIRKWOOD_RECCTL_MONO_CHAN_RIGHT (1<<3)
33#define KIRKWOOD_RECCTL_MONO_CHAN_LEFT (0<<3)
34#define KIRKWOOD_RECCTL_SIZE_MASK (7<<0)
35#define KIRKWOOD_RECCTL_SIZE_16 (7<<0)
36#define KIRKWOOD_RECCTL_SIZE_16_C (3<<0)
37#define KIRKWOOD_RECCTL_SIZE_20 (2<<0)
38#define KIRKWOOD_RECCTL_SIZE_24 (1<<0)
39#define KIRKWOOD_RECCTL_SIZE_32 (0<<0)
40
41#define KIRKWOOD_REC_BUF_ADDR 0x1004
42#define KIRKWOOD_REC_BUF_SIZE 0x1008
43#define KIRKWOOD_REC_BYTE_COUNT 0x100C
44
45#define KIRKWOOD_PLAYCTL 0x1100
46#define KIRKWOOD_PLAYCTL_PLAY_BUSY (1<<16)
47#define KIRKWOOD_PLAYCTL_BURST_MASK (3<<11)
48#define KIRKWOOD_PLAYCTL_BURST_128 (2<<11)
49#define KIRKWOOD_PLAYCTL_BURST_32 (1<<11)
50#define KIRKWOOD_PLAYCTL_PAUSE (1<<9)
51#define KIRKWOOD_PLAYCTL_SPDIF_MUTE (1<<8)
52#define KIRKWOOD_PLAYCTL_I2S_MUTE (1<<7)
53#define KIRKWOOD_PLAYCTL_SPDIF_EN (1<<4)
54#define KIRKWOOD_PLAYCTL_I2S_EN (1<<3)
55#define KIRKWOOD_PLAYCTL_SIZE_MASK (7<<0)
56#define KIRKWOOD_PLAYCTL_SIZE_16 (7<<0)
57#define KIRKWOOD_PLAYCTL_SIZE_16_C (3<<0)
58#define KIRKWOOD_PLAYCTL_SIZE_20 (2<<0)
59#define KIRKWOOD_PLAYCTL_SIZE_24 (1<<0)
60#define KIRKWOOD_PLAYCTL_SIZE_32 (0<<0)
61
62#define KIRKWOOD_PLAY_BUF_ADDR 0x1104
63#define KIRKWOOD_PLAY_BUF_SIZE 0x1108
64#define KIRKWOOD_PLAY_BYTE_COUNT 0x110C
65
66#define KIRKWOOD_DCO_CTL 0x1204
67#define KIRKWOOD_DCO_CTL_OFFSET_MASK (0xFFF<<2)
68#define KIRKWOOD_DCO_CTL_OFFSET_0 (0x800<<2)
69#define KIRKWOOD_DCO_CTL_FREQ_MASK (3<<0)
70#define KIRKWOOD_DCO_CTL_FREQ_11 (0<<0)
71#define KIRKWOOD_DCO_CTL_FREQ_12 (1<<0)
72#define KIRKWOOD_DCO_CTL_FREQ_24 (2<<0)
73
74#define KIRKWOOD_DCO_SPCR_STATUS 0x120c
75#define KIRKWOOD_DCO_SPCR_STATUS_DCO_LOCK (1<<16)
76
77#define KIRKWOOD_ERR_CAUSE 0x1300
78#define KIRKWOOD_ERR_MASK 0x1304
79
80#define KIRKWOOD_INT_CAUSE 0x1308
81#define KIRKWOOD_INT_MASK 0x130C
82#define KIRKWOOD_INT_CAUSE_PLAY_BYTES (1<<14)
83#define KIRKWOOD_INT_CAUSE_REC_BYTES (1<<13)
84#define KIRKWOOD_INT_CAUSE_DMA_PLAY_END (1<<7)
85#define KIRKWOOD_INT_CAUSE_DMA_PLAY_3Q (1<<6)
86#define KIRKWOOD_INT_CAUSE_DMA_PLAY_HALF (1<<5)
87#define KIRKWOOD_INT_CAUSE_DMA_PLAY_1Q (1<<4)
88#define KIRKWOOD_INT_CAUSE_DMA_REC_END (1<<3)
89#define KIRKWOOD_INT_CAUSE_DMA_REC_3Q (1<<2)
90#define KIRKWOOD_INT_CAUSE_DMA_REC_HALF (1<<1)
91#define KIRKWOOD_INT_CAUSE_DMA_REC_1Q (1<<0)
92
93#define KIRKWOOD_REC_BYTE_INT_COUNT 0x1310
94#define KIRKWOOD_PLAY_BYTE_INT_COUNT 0x1314
95#define KIRKWOOD_BYTE_INT_COUNT_MASK 0xffffff
96
97#define KIRKWOOD_I2S_PLAYCTL 0x2508
98#define KIRKWOOD_I2S_RECCTL 0x2408
99#define KIRKWOOD_I2S_CTL_JUST_MASK (0xf<<26)
100#define KIRKWOOD_I2S_CTL_LJ (0<<26)
101#define KIRKWOOD_I2S_CTL_I2S (5<<26)
102#define KIRKWOOD_I2S_CTL_RJ (8<<26)
103#define KIRKWOOD_I2S_CTL_SIZE_MASK (3<<30)
104#define KIRKWOOD_I2S_CTL_SIZE_16 (3<<30)
105#define KIRKWOOD_I2S_CTL_SIZE_20 (2<<30)
106#define KIRKWOOD_I2S_CTL_SIZE_24 (1<<30)
107#define KIRKWOOD_I2S_CTL_SIZE_32 (0<<30)
108
109#define KIRKWOOD_AUDIO_BUF_MAX (16*1024*1024)
110
111/* Theses values come from the marvell alsa driver */
112/* need to find where they come from */
113#define KIRKWOOD_SND_MIN_PERIODS 8
114#define KIRKWOOD_SND_MAX_PERIODS 16
115#define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x4000
116#define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x4000
117
118struct kirkwood_dma_data {
119 struct resource *mem;
120 void __iomem *io;
121 int irq;
122 int burst;
123 struct mbus_dram_target_info *dram;
124};
125
126#endif
diff --git a/sound/soc/nuc900/Kconfig b/sound/soc/nuc900/Kconfig
new file mode 100644
index 000000000000..a0ed1c618f60
--- /dev/null
+++ b/sound/soc/nuc900/Kconfig
@@ -0,0 +1,27 @@
1##
2## NUC900 series AC97 API
3##
4config SND_SOC_NUC900
5 tristate "SoC Audio for NUC900 series"
6 depends on ARCH_W90X900
7 help
8 This option enables support for AC97 mode on the NUC900 SoC.
9
10config SND_SOC_NUC900_AC97
11 tristate
12 select AC97_BUS
13 select SND_AC97_CODEC
14 select SND_SOC_AC97_BUS
15
16
17##
18## Boards
19##
20config SND_SOC_NUC900EVB
21 tristate "NUC900 AC97 support for demo board"
22 depends on SND_SOC_NUC900
23 select SND_SOC_NUC900_AC97
24 select SND_SOC_AC97_CODEC
25 help
26 Select this option to enable audio (AC97) on the
27 NUC900 demoboard.
diff --git a/sound/soc/nuc900/Makefile b/sound/soc/nuc900/Makefile
new file mode 100644
index 000000000000..7e46c7150316
--- /dev/null
+++ b/sound/soc/nuc900/Makefile
@@ -0,0 +1,11 @@
1# NUC900 series audio
2snd-soc-nuc900-pcm-objs := nuc900-pcm.o
3snd-soc-nuc900-ac97-objs := nuc900-ac97.o
4
5obj-$(CONFIG_SND_SOC_NUC900) += snd-soc-nuc900-pcm.o
6obj-$(CONFIG_SND_SOC_NUC900_AC97) += snd-soc-nuc900-ac97.o
7
8# Boards
9snd-soc-nuc900-audio-objs := nuc900-audio.o
10
11obj-$(CONFIG_SND_SOC_NUC900EVB) += snd-soc-nuc900-audio.o
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
new file mode 100644
index 000000000000..f7b44e081420
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -0,0 +1,442 @@
1/*
2 * Copyright (c) 2009-2010 Nuvoton technology corporation.
3 *
4 * Wan ZongShun <mcuos.com@gmail.com>
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 as published by
8 * the Free Software Foundation;version 2 of the License.
9 *
10 */
11
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/device.h>
16#include <linux/delay.h>
17#include <linux/mutex.h>
18#include <linux/suspend.h>
19#include <sound/core.h>
20#include <sound/pcm.h>
21#include <sound/initval.h>
22#include <sound/soc.h>
23#include <linux/device.h>
24#include <linux/clk.h>
25
26#include <mach/mfp.h>
27
28#include "nuc900-auido.h"
29
30static DEFINE_MUTEX(ac97_mutex);
31struct nuc900_audio *nuc900_ac97_data;
32
33static int nuc900_checkready(void)
34{
35 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
36
37 if (!(AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS0) & CODEC_READY))
38 return -EPERM;
39
40 return 0;
41}
42
43/* AC97 controller reads codec register */
44static unsigned short nuc900_ac97_read(struct snd_ac97 *ac97,
45 unsigned short reg)
46{
47 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
48 unsigned long timeout = 0x10000, val;
49
50 mutex_lock(&ac97_mutex);
51
52 val = nuc900_checkready();
53 if (!!val) {
54 dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
55 goto out;
56 }
57
58 /* set the R_WB bit and write register index */
59 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, R_WB | reg);
60
61 /* set the valid frame bit and valid slots */
62 val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
63 val |= (VALID_FRAME | SLOT1_VALID);
64 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
65
66 udelay(100);
67
68 /* polling the AC_R_FINISH */
69 val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
70 val &= AC_R_FINISH;
71 while (!val && timeout--)
72 mdelay(1);
73
74 if (!timeout) {
75 dev_err(nuc900_audio->dev, "AC97 read register time out !\n");
76 val = -EPERM;
77 goto out;
78 }
79
80 val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0) ;
81 val &= ~SLOT1_VALID;
82 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, val);
83
84 if (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS1) >> 2 != reg) {
85 dev_err(nuc900_audio->dev,
86 "R_INDEX of REG_ACTL_ACIS1 not match!\n");
87 }
88
89 udelay(100);
90 val = (AUDIO_READ(nuc900_audio->mmio + ACTL_ACIS2) & 0xFFFF);
91
92out:
93 mutex_unlock(&ac97_mutex);
94 return val;
95}
96
97/* AC97 controller writes to codec register */
98static void nuc900_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
99 unsigned short val)
100{
101 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
102 unsigned long tmp, timeout = 0x10000;
103
104 mutex_lock(&ac97_mutex);
105
106 tmp = nuc900_checkready();
107 if (!!tmp)
108 dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
109
110 /* clear the R_WB bit and write register index */
111 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS1, reg);
112
113 /* write register value */
114 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS2, val);
115
116 /* set the valid frame bit and valid slots */
117 tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
118 tmp |= SLOT1_VALID | SLOT2_VALID | VALID_FRAME;
119 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
120
121 udelay(100);
122
123 /* polling the AC_W_FINISH */
124 tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
125 tmp &= AC_W_FINISH;
126 while (tmp && timeout--)
127 mdelay(1);
128
129 if (!timeout)
130 dev_err(nuc900_audio->dev, "AC97 write register time out !\n");
131
132 tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
133 tmp &= ~(SLOT1_VALID | SLOT2_VALID);
134 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
135
136 mutex_unlock(&ac97_mutex);
137
138}
139
140static void nuc900_ac97_warm_reset(struct snd_ac97 *ac97)
141{
142 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
143 unsigned long val;
144
145 mutex_lock(&ac97_mutex);
146
147 /* warm reset AC 97 */
148 val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
149 val |= AC_W_RES;
150 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
151
152 udelay(1000);
153
154 val = nuc900_checkready();
155 if (!!val)
156 dev_err(nuc900_audio->dev, "AC97 codec is not ready\n");
157
158 mutex_unlock(&ac97_mutex);
159}
160
161static void nuc900_ac97_cold_reset(struct snd_ac97 *ac97)
162{
163 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
164 unsigned long val;
165
166 mutex_lock(&ac97_mutex);
167
168 /* reset Audio Controller */
169 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
170 val |= ACTL_RESET_BIT;
171 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
172
173 udelay(1000);
174
175 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
176 val &= (~ACTL_RESET_BIT);
177 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
178
179 udelay(1000);
180
181 /* reset AC-link interface */
182
183 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
184 val |= AC_RESET;
185 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
186
187 udelay(1000);
188
189 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
190 val &= ~AC_RESET;
191 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
192
193 udelay(1000);
194
195 /* cold reset AC 97 */
196 val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
197 val |= AC_C_RES;
198 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
199
200 udelay(1000);
201
202 val = AUDIO_READ(nuc900_audio->mmio + ACTL_ACCON);
203 val &= (~AC_C_RES);
204 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACCON, val);
205
206 udelay(1000);
207
208 mutex_unlock(&ac97_mutex);
209
210}
211
212/* AC97 controller operations */
213struct snd_ac97_bus_ops soc_ac97_ops = {
214 .read = nuc900_ac97_read,
215 .write = nuc900_ac97_write,
216 .reset = nuc900_ac97_cold_reset,
217 .warm_reset = nuc900_ac97_warm_reset,
218}
219EXPORT_SYMBOL_GPL(soc_ac97_ops);
220
221static int nuc900_ac97_trigger(struct snd_pcm_substream *substream,
222 int cmd, struct snd_soc_dai *dai)
223{
224 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
225 int ret, stype = SUBSTREAM_TYPE(substream);
226 unsigned long val, tmp;
227
228 ret = 0;
229
230 switch (cmd) {
231 case SNDRV_PCM_TRIGGER_START:
232 case SNDRV_PCM_TRIGGER_RESUME:
233 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
234 if (PCM_TX == stype) {
235 tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
236 tmp |= (SLOT3_VALID | SLOT4_VALID | VALID_FRAME);
237 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
238
239 tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
240 tmp |= (P_DMA_END_IRQ | P_DMA_MIDDLE_IRQ);
241 AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, tmp);
242 val |= AC_PLAY;
243 } else {
244 tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
245 tmp |= (R_DMA_END_IRQ | R_DMA_MIDDLE_IRQ);
246
247 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, tmp);
248 val |= AC_RECORD;
249 }
250
251 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
252
253 break;
254 case SNDRV_PCM_TRIGGER_STOP:
255 case SNDRV_PCM_TRIGGER_SUSPEND:
256 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
257 if (PCM_TX == stype) {
258 tmp = AUDIO_READ(nuc900_audio->mmio + ACTL_ACOS0);
259 tmp &= ~(SLOT3_VALID | SLOT4_VALID);
260 AUDIO_WRITE(nuc900_audio->mmio + ACTL_ACOS0, tmp);
261
262 AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, RESET_PRSR);
263 val &= ~AC_PLAY;
264 } else {
265 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, RESET_PRSR);
266 val &= ~AC_RECORD;
267 }
268
269 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
270
271 break;
272 default:
273 ret = -EINVAL;
274 }
275
276 return ret;
277}
278
279static int nuc900_ac97_probe(struct platform_device *pdev,
280 struct snd_soc_dai *dai)
281{
282 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
283 unsigned long val;
284
285 mutex_lock(&ac97_mutex);
286
287 /* enable unit clock */
288 clk_enable(nuc900_audio->clk);
289
290 /* enable audio controller and AC-link interface */
291 val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
292 val |= (IIS_AC_PIN_SEL | ACLINK_EN);
293 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
294
295 mutex_unlock(&ac97_mutex);
296
297 return 0;
298}
299
300static void nuc900_ac97_remove(struct platform_device *pdev,
301 struct snd_soc_dai *dai)
302{
303 struct nuc900_audio *nuc900_audio = nuc900_ac97_data;
304
305 clk_disable(nuc900_audio->clk);
306}
307
308static struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
309 .trigger = nuc900_ac97_trigger,
310};
311
312struct snd_soc_dai nuc900_ac97_dai = {
313 .name = "nuc900-ac97",
314 .probe = nuc900_ac97_probe,
315 .remove = nuc900_ac97_remove,
316 .ac97_control = 1,
317 .playback = {
318 .rates = SNDRV_PCM_RATE_8000_48000,
319 .formats = SNDRV_PCM_FMTBIT_S16_LE,
320 .channels_min = 1,
321 .channels_max = 2,
322 },
323 .capture = {
324 .rates = SNDRV_PCM_RATE_8000_48000,
325 .formats = SNDRV_PCM_FMTBIT_S16_LE,
326 .channels_min = 1,
327 .channels_max = 2,
328 },
329 .ops = &nuc900_ac97_dai_ops,
330}
331EXPORT_SYMBOL_GPL(nuc900_ac97_dai);
332
333static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev)
334{
335 struct nuc900_audio *nuc900_audio;
336 int ret;
337
338 if (nuc900_ac97_data)
339 return -EBUSY;
340
341 nuc900_audio = kzalloc(sizeof(struct nuc900_audio), GFP_KERNEL);
342 if (!nuc900_audio)
343 return -ENOMEM;
344
345 spin_lock_init(&nuc900_audio->lock);
346
347 nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
348 if (!nuc900_audio->res) {
349 ret = -ENODEV;
350 goto out0;
351 }
352
353 if (!request_mem_region(nuc900_audio->res->start,
354 resource_size(nuc900_audio->res), pdev->name)) {
355 ret = -EBUSY;
356 goto out0;
357 }
358
359 nuc900_audio->mmio = ioremap(nuc900_audio->res->start,
360 resource_size(nuc900_audio->res));
361 if (!nuc900_audio->mmio) {
362 ret = -ENOMEM;
363 goto out1;
364 }
365
366 nuc900_audio->clk = clk_get(&pdev->dev, NULL);
367 if (IS_ERR(nuc900_audio->clk)) {
368 ret = PTR_ERR(nuc900_audio->clk);
369 goto out2;
370 }
371
372 nuc900_audio->irq_num = platform_get_irq(pdev, 0);
373 if (!nuc900_audio->irq_num) {
374 ret = -EBUSY;
375 goto out2;
376 }
377
378 nuc900_ac97_data = nuc900_audio;
379
380 nuc900_audio->dev = nuc900_ac97_dai.dev = &pdev->dev;
381
382 ret = snd_soc_register_dai(&nuc900_ac97_dai);
383 if (ret)
384 goto out3;
385
386 mfp_set_groupg(nuc900_audio->dev); /* enbale ac97 multifunction pin*/
387
388 return 0;
389
390out3:
391 clk_put(nuc900_audio->clk);
392out2:
393 iounmap(nuc900_audio->mmio);
394out1:
395 release_mem_region(nuc900_audio->res->start,
396 resource_size(nuc900_audio->res));
397out0:
398 kfree(nuc900_audio);
399 return ret;
400}
401
402static int __devexit nuc900_ac97_drvremove(struct platform_device *pdev)
403{
404
405 snd_soc_unregister_dai(&nuc900_ac97_dai);
406
407 clk_put(nuc900_ac97_data->clk);
408 iounmap(nuc900_ac97_data->mmio);
409 release_mem_region(nuc900_ac97_data->res->start,
410 resource_size(nuc900_ac97_data->res));
411
412 nuc900_ac97_data = NULL;
413
414 return 0;
415}
416
417static struct platform_driver nuc900_ac97_driver = {
418 .driver = {
419 .name = "nuc900-audio",
420 .owner = THIS_MODULE,
421 },
422 .probe = nuc900_ac97_drvprobe,
423 .remove = __devexit_p(nuc900_ac97_drvremove),
424};
425
426static int __init nuc900_ac97_init(void)
427{
428 return platform_driver_register(&nuc900_ac97_driver);
429}
430
431static void __exit nuc900_ac97_exit(void)
432{
433 platform_driver_unregister(&nuc900_ac97_driver);
434}
435
436module_init(nuc900_ac97_init);
437module_exit(nuc900_ac97_exit);
438
439MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
440MODULE_DESCRIPTION("NUC900 AC97 SoC driver!");
441MODULE_LICENSE("GPL");
442MODULE_ALIAS("platform:nuc900-ac97");
diff --git a/sound/soc/nuc900/nuc900-audio.c b/sound/soc/nuc900/nuc900-audio.c
new file mode 100644
index 000000000000..b33d5b844d71
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-audio.c
@@ -0,0 +1,81 @@
1/*
2 * Copyright (c) 2010 Nuvoton technology corporation.
3 *
4 * Wan ZongShun <mcuos.com@gmail.com>
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 as published by
8 * the Free Software Foundation;version 2 of the License.
9 *
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
18#include <sound/core.h>
19#include <sound/pcm.h>
20#include <sound/soc.h>
21#include <sound/soc-dapm.h>
22
23#include "../codecs/ac97.h"
24#include "nuc900-auido.h"
25
26static struct snd_soc_dai_link nuc900evb_ac97_dai = {
27 .name = "AC97",
28 .stream_name = "AC97 HiFi",
29 .cpu_dai = &nuc900_ac97_dai,
30 .codec_dai = &ac97_dai,
31};
32
33static struct snd_soc_card nuc900evb_audio_machine = {
34 .name = "NUC900EVB_AC97",
35 .dai_link = &nuc900evb_ac97_dai,
36 .num_links = 1,
37 .platform = &nuc900_soc_platform,
38};
39
40static struct snd_soc_device nuc900evb_ac97_devdata = {
41 .card = &nuc900evb_audio_machine,
42 .codec_dev = &soc_codec_dev_ac97,
43};
44
45static struct platform_device *nuc900evb_asoc_dev;
46
47static int __init nuc900evb_audio_init(void)
48{
49 int ret;
50
51 ret = -ENOMEM;
52 nuc900evb_asoc_dev = platform_device_alloc("soc-audio", -1);
53 if (!nuc900evb_asoc_dev)
54 goto out;
55
56 /* nuc900 board audio device */
57 platform_set_drvdata(nuc900evb_asoc_dev, &nuc900evb_ac97_devdata);
58
59 nuc900evb_ac97_devdata.dev = &nuc900evb_asoc_dev->dev;
60 ret = platform_device_add(nuc900evb_asoc_dev);
61
62 if (ret) {
63 platform_device_put(nuc900evb_asoc_dev);
64 nuc900evb_asoc_dev = NULL;
65 }
66
67out:
68 return ret;
69}
70
71static void __exit nuc900evb_audio_exit(void)
72{
73 platform_device_unregister(nuc900evb_asoc_dev);
74}
75
76module_init(nuc900evb_audio_init);
77module_exit(nuc900evb_audio_exit);
78
79MODULE_LICENSE("GPL");
80MODULE_DESCRIPTION("NUC900 Series ASoC audio support");
81MODULE_AUTHOR("Wan ZongShun");
diff --git a/sound/soc/nuc900/nuc900-auido.h b/sound/soc/nuc900/nuc900-auido.h
new file mode 100644
index 000000000000..95ac4ef2f353
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-auido.h
@@ -0,0 +1,121 @@
1/*
2 * Copyright (c) 2010 Nuvoton technology corporation.
3 *
4 * Wan ZongShun <mcuos.com@gmail.com>
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 as published by
8 * the Free Software Foundation;version 2 of the License.
9 *
10 */
11
12#ifndef _NUC900_AUDIO_H
13#define _NUC900_AUDIO_H
14
15#include <linux/io.h>
16
17/* Audio Control Registers */
18#define ACTL_CON 0x00
19#define ACTL_RESET 0x04
20#define ACTL_RDSTB 0x08
21#define ACTL_RDST_LENGTH 0x0C
22#define ACTL_RDSTC 0x10
23#define ACTL_RSR 0x14
24#define ACTL_PDSTB 0x18
25#define ACTL_PDST_LENGTH 0x1C
26#define ACTL_PDSTC 0x20
27#define ACTL_PSR 0x24
28#define ACTL_IISCON 0x28
29#define ACTL_ACCON 0x2C
30#define ACTL_ACOS0 0x30
31#define ACTL_ACOS1 0x34
32#define ACTL_ACOS2 0x38
33#define ACTL_ACIS0 0x3C
34#define ACTL_ACIS1 0x40
35#define ACTL_ACIS2 0x44
36#define ACTL_COUNTER 0x48
37
38/* bit definition of REG_ACTL_CON register */
39#define R_DMA_IRQ 0x1000
40#define T_DMA_IRQ 0x0800
41#define IIS_AC_PIN_SEL 0x0100
42#define FIFO_TH 0x0080
43#define ADC_EN 0x0010
44#define M80_EN 0x0008
45#define ACLINK_EN 0x0004
46#define IIS_EN 0x0002
47
48/* bit definition of REG_ACTL_RESET register */
49#define W5691_PLAY 0x20000
50#define ACTL_RESET_BIT 0x10000
51#define RECORD_RIGHT_CHNNEL 0x08000
52#define RECORD_LEFT_CHNNEL 0x04000
53#define PLAY_RIGHT_CHNNEL 0x02000
54#define PLAY_LEFT_CHNNEL 0x01000
55#define DAC_PLAY 0x00800
56#define ADC_RECORD 0x00400
57#define M80_PLAY 0x00200
58#define AC_RECORD 0x00100
59#define AC_PLAY 0x00080
60#define IIS_RECORD 0x00040
61#define IIS_PLAY 0x00020
62#define DAC_RESET 0x00010
63#define ADC_RESET 0x00008
64#define M80_RESET 0x00004
65#define AC_RESET 0x00002
66#define IIS_RESET 0x00001
67
68/* bit definition of REG_ACTL_ACCON register */
69#define AC_BCLK_PU_EN 0x20
70#define AC_R_FINISH 0x10
71#define AC_W_FINISH 0x08
72#define AC_W_RES 0x04
73#define AC_C_RES 0x02
74
75/* bit definition of ACTL_RSR register */
76#define R_FIFO_EMPTY 0x04
77#define R_DMA_END_IRQ 0x02
78#define R_DMA_MIDDLE_IRQ 0x01
79
80/* bit definition of ACTL_PSR register */
81#define P_FIFO_EMPTY 0x04
82#define P_DMA_END_IRQ 0x02
83#define P_DMA_MIDDLE_IRQ 0x01
84
85/* bit definition of ACTL_ACOS0 register */
86#define SLOT1_VALID 0x01
87#define SLOT2_VALID 0x02
88#define SLOT3_VALID 0x04
89#define SLOT4_VALID 0x08
90#define VALID_FRAME 0x10
91
92/* bit definition of ACTL_ACOS1 register */
93#define R_WB 0x80
94
95#define CODEC_READY 0x10
96#define RESET_PRSR 0x00
97#define AUDIO_WRITE(addr, val) __raw_writel(val, addr)
98#define AUDIO_READ(addr) __raw_readl(addr)
99#define PCM_TX 0
100#define PCM_RX 1
101#define SUBSTREAM_TYPE(substream) \
102 ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
103
104struct nuc900_audio {
105 void __iomem *mmio;
106 spinlock_t lock;
107 dma_addr_t dma_addr[2];
108 unsigned long buffersize[2];
109 unsigned long irq_num;
110 struct snd_pcm_substream *substream;
111 struct resource *res;
112 struct clk *clk;
113 struct device *dev;
114
115};
116
117extern struct nuc900_audio *nuc900_ac97_data;
118extern struct snd_soc_dai nuc900_ac97_dai;
119extern struct snd_soc_platform nuc900_soc_platform;
120
121#endif /*end _NUC900_AUDIO_H */
diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c
new file mode 100644
index 000000000000..32a503c1c4be
--- /dev/null
+++ b/sound/soc/nuc900/nuc900-pcm.c
@@ -0,0 +1,352 @@
1/*
2 * Copyright (c) 2010 Nuvoton technology corporation.
3 *
4 * Wan ZongShun <mcuos.com@gmail.com>
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 as published by
8 * the Free Software Foundation;version 2 of the License.
9 *
10 */
11
12#include <linux/module.h>
13#include <linux/init.h>
14#include <linux/io.h>
15#include <linux/platform_device.h>
16#include <linux/slab.h>
17#include <linux/dma-mapping.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 <mach/hardware.h>
25
26#include "nuc900-auido.h"
27
28static const struct snd_pcm_hardware nuc900_pcm_hardware = {
29 .info = SNDRV_PCM_INFO_INTERLEAVED |
30 SNDRV_PCM_INFO_BLOCK_TRANSFER |
31 SNDRV_PCM_INFO_MMAP |
32 SNDRV_PCM_INFO_MMAP_VALID |
33 SNDRV_PCM_INFO_PAUSE |
34 SNDRV_PCM_INFO_RESUME,
35 .formats = SNDRV_PCM_FMTBIT_S16_LE,
36 .channels_min = 1,
37 .channels_max = 2,
38 .buffer_bytes_max = 4*1024,
39 .period_bytes_min = 1*1024,
40 .period_bytes_max = 4*1024,
41 .periods_min = 1,
42 .periods_max = 1024,
43};
44
45static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
46 struct snd_pcm_hw_params *params)
47{
48 struct snd_pcm_runtime *runtime = substream->runtime;
49 struct nuc900_audio *nuc900_audio = runtime->private_data;
50 unsigned long flags, stype = SUBSTREAM_TYPE(substream);
51 int ret = 0;
52
53 spin_lock_irqsave(&nuc900_audio->lock, flags);
54
55 ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
56 if (ret < 0)
57 return ret;
58
59 nuc900_audio->substream = substream;
60 nuc900_audio->dma_addr[stype] = runtime->dma_addr;
61 nuc900_audio->buffersize[stype] = params_buffer_bytes(params);
62
63 spin_unlock_irqrestore(&nuc900_audio->lock, flags);
64
65 return ret;
66}
67
68static void nuc900_update_dma_register(struct snd_pcm_substream *substream,
69 dma_addr_t dma_addr, size_t count)
70{
71 struct snd_pcm_runtime *runtime = substream->runtime;
72 struct nuc900_audio *nuc900_audio = runtime->private_data;
73 void __iomem *mmio_addr, *mmio_len;
74
75 if (SUBSTREAM_TYPE(substream) == PCM_TX) {
76 mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
77 mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
78 } else {
79 mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
80 mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
81 }
82
83 AUDIO_WRITE(mmio_addr, dma_addr);
84 AUDIO_WRITE(mmio_len, count);
85}
86
87static void nuc900_dma_start(struct snd_pcm_substream *substream)
88{
89 struct snd_pcm_runtime *runtime = substream->runtime;
90 struct nuc900_audio *nuc900_audio = runtime->private_data;
91 unsigned long val;
92
93 val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
94 val |= (T_DMA_IRQ | R_DMA_IRQ);
95 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
96}
97
98static void nuc900_dma_stop(struct snd_pcm_substream *substream)
99{
100 struct snd_pcm_runtime *runtime = substream->runtime;
101 struct nuc900_audio *nuc900_audio = runtime->private_data;
102 unsigned long val;
103
104 val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
105 val &= ~(T_DMA_IRQ | R_DMA_IRQ);
106 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
107}
108
109static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
110{
111 struct snd_pcm_substream *substream = dev_id;
112 struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
113 unsigned long val;
114
115 spin_lock(&nuc900_audio->lock);
116
117 val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
118
119 if (val & R_DMA_IRQ) {
120 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
121
122 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
123
124 if (val & R_DMA_MIDDLE_IRQ) {
125 val |= R_DMA_MIDDLE_IRQ;
126 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
127 }
128
129 if (val & R_DMA_END_IRQ) {
130 val |= R_DMA_END_IRQ;
131 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
132 }
133 } else if (val & T_DMA_IRQ) {
134 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
135
136 val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
137
138 if (val & P_DMA_MIDDLE_IRQ) {
139 val |= P_DMA_MIDDLE_IRQ;
140 AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
141 }
142
143 if (val & P_DMA_END_IRQ) {
144 val |= P_DMA_END_IRQ;
145 AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
146 }
147 } else {
148 dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
149 spin_unlock(&nuc900_audio->lock);
150 return IRQ_HANDLED;
151 }
152
153 spin_unlock(&nuc900_audio->lock);
154
155 snd_pcm_period_elapsed(substream);
156
157 return IRQ_HANDLED;
158}
159
160static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
161{
162 snd_pcm_lib_free_pages(substream);
163 return 0;
164}
165
166static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
167{
168 struct snd_pcm_runtime *runtime = substream->runtime;
169 struct nuc900_audio *nuc900_audio = runtime->private_data;
170 unsigned long flags, val, stype = SUBSTREAM_TYPE(substream);;
171
172 spin_lock_irqsave(&nuc900_audio->lock, flags);
173
174 nuc900_update_dma_register(substream,
175 nuc900_audio->dma_addr[stype], nuc900_audio->buffersize[stype]);
176
177 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
178
179 switch (runtime->channels) {
180 case 1:
181 if (PCM_TX == stype) {
182 val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
183 val |= PLAY_RIGHT_CHNNEL;
184 } else {
185 val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
186 val |= RECORD_RIGHT_CHNNEL;
187 }
188 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
189 break;
190 case 2:
191 if (PCM_TX == stype)
192 val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
193 else
194 val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
195 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
196 break;
197 default:
198 return -EINVAL;
199 }
200 spin_unlock_irqrestore(&nuc900_audio->lock, flags);
201 return 0;
202}
203
204static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
205{
206 int ret = 0;
207
208 switch (cmd) {
209 case SNDRV_PCM_TRIGGER_START:
210 case SNDRV_PCM_TRIGGER_RESUME:
211 nuc900_dma_start(substream);
212 break;
213
214 case SNDRV_PCM_TRIGGER_STOP:
215 case SNDRV_PCM_TRIGGER_SUSPEND:
216 nuc900_dma_stop(substream);
217 break;
218
219 default:
220 ret = -EINVAL;
221 break;
222 }
223
224 return ret;
225}
226
227int nuc900_dma_getposition(struct snd_pcm_substream *substream,
228 dma_addr_t *src, dma_addr_t *dst)
229{
230 struct snd_pcm_runtime *runtime = substream->runtime;
231 struct nuc900_audio *nuc900_audio = runtime->private_data;
232
233 if (src != NULL)
234 *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
235
236 if (dst != NULL)
237 *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
238
239 return 0;
240}
241
242static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
243{
244 struct snd_pcm_runtime *runtime = substream->runtime;
245 dma_addr_t src, dst;
246 unsigned long res;
247
248 nuc900_dma_getposition(substream, &src, &dst);
249
250 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
251 res = dst - runtime->dma_addr;
252 else
253 res = src - runtime->dma_addr;
254
255 return bytes_to_frames(substream->runtime, res);
256}
257
258static int nuc900_dma_open(struct snd_pcm_substream *substream)
259{
260 struct snd_pcm_runtime *runtime = substream->runtime;
261 struct nuc900_audio *nuc900_audio;
262
263 snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
264
265 nuc900_audio = nuc900_ac97_data;
266
267 if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
268 IRQF_DISABLED, "nuc900-dma", substream))
269 return -EBUSY;
270
271 runtime->private_data = nuc900_audio;
272
273 return 0;
274}
275
276static int nuc900_dma_close(struct snd_pcm_substream *substream)
277{
278 struct snd_pcm_runtime *runtime = substream->runtime;
279 struct nuc900_audio *nuc900_audio = runtime->private_data;
280
281 free_irq(nuc900_audio->irq_num, substream);
282
283 return 0;
284}
285
286static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
287 struct vm_area_struct *vma)
288{
289 struct snd_pcm_runtime *runtime = substream->runtime;
290
291 return dma_mmap_writecombine(substream->pcm->card->dev, vma,
292 runtime->dma_area,
293 runtime->dma_addr,
294 runtime->dma_bytes);
295}
296
297static struct snd_pcm_ops nuc900_dma_ops = {
298 .open = nuc900_dma_open,
299 .close = nuc900_dma_close,
300 .ioctl = snd_pcm_lib_ioctl,
301 .hw_params = nuc900_dma_hw_params,
302 .hw_free = nuc900_dma_hw_free,
303 .prepare = nuc900_dma_prepare,
304 .trigger = nuc900_dma_trigger,
305 .pointer = nuc900_dma_pointer,
306 .mmap = nuc900_dma_mmap,
307};
308
309static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
310{
311 snd_pcm_lib_preallocate_free_for_all(pcm);
312}
313
314static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
315static int nuc900_dma_new(struct snd_card *card,
316 struct snd_soc_dai *dai, struct snd_pcm *pcm)
317{
318 if (!card->dev->dma_mask)
319 card->dev->dma_mask = &nuc900_pcm_dmamask;
320 if (!card->dev->coherent_dma_mask)
321 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
322
323 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
324 card->dev, 4 * 1024, (4 * 1024) - 1);
325
326 return 0;
327}
328
329struct snd_soc_platform nuc900_soc_platform = {
330 .name = "nuc900-dma",
331 .pcm_ops = &nuc900_dma_ops,
332 .pcm_new = nuc900_dma_new,
333 .pcm_free = nuc900_dma_free_dma_buffers,
334}
335EXPORT_SYMBOL_GPL(nuc900_soc_platform);
336
337static int __init nuc900_soc_platform_init(void)
338{
339 return snd_soc_register_platform(&nuc900_soc_platform);
340}
341
342static void __exit nuc900_soc_platform_exit(void)
343{
344 snd_soc_unregister_platform(&nuc900_soc_platform);
345}
346
347module_init(nuc900_soc_platform_init);
348module_exit(nuc900_soc_platform_exit);
349
350MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
351MODULE_DESCRIPTION("nuc900 Audio DMA module");
352MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 2a7cc222d098..292d817c9a94 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -1,6 +1,6 @@
1config SND_S3C24XX_SOC 1config SND_S3C24XX_SOC
2 tristate "SoC Audio for the Samsung S3CXXXX chips" 2 tristate "SoC Audio for the Samsung S3CXXXX chips"
3 depends on ARCH_S3C2410 || ARCH_S3C64XX 3 depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210
4 select S3C64XX_DMA if ARCH_S3C64XX 4 select S3C64XX_DMA if ARCH_S3C64XX
5 help 5 help
6 Say Y or M if you want to add support for codecs attached to 6 Say Y or M if you want to add support for codecs attached to
@@ -120,7 +120,7 @@ config SND_S3C24XX_SOC_SIMTEC_HERMES
120 120
121config SND_SOC_SMDK_WM9713 121config SND_SOC_SMDK_WM9713
122 tristate "SoC AC97 Audio support for SMDK with WM9713" 122 tristate "SoC AC97 Audio support for SMDK with WM9713"
123 depends on SND_S3C24XX_SOC && MACH_SMDK6410 123 depends on SND_S3C24XX_SOC && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110)
124 select SND_SOC_WM9713 124 select SND_SOC_WM9713
125 select SND_S3C_SOC_AC97 125 select SND_S3C_SOC_AC97
126 help 126 help
diff --git a/sound/soc/s3c24xx/smdk_wm9713.c b/sound/soc/s3c24xx/smdk_wm9713.c
index 24fd39f38ccb..5527b9e88c98 100644
--- a/sound/soc/s3c24xx/smdk_wm9713.c
+++ b/sound/soc/s3c24xx/smdk_wm9713.c
@@ -25,6 +25,9 @@ static struct snd_soc_card smdk;
25 * Default CFG switch settings to use this driver: 25 * Default CFG switch settings to use this driver:
26 * 26 *
27 * SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off 27 * SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off
28 * SMDKC100: Set CFG6 1-3 On, CFG7 1 On
29 * SMDKC110: Set CFGB10 1-2 Off, CFGB12 1-3 On
30 * SMDKV210: Set CFGB10 1-2 Off, CFGB12 1-3 On
28 */ 31 */
29 32
30/* 33/*
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 998569d60330..254dd1c6914d 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2353,6 +2353,101 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec,
2353EXPORT_SYMBOL_GPL(snd_soc_limit_volume); 2353EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
2354 2354
2355/** 2355/**
2356 * snd_soc_info_volsw_2r_sx - double with tlv and variable data size
2357 * mixer info callback
2358 * @kcontrol: mixer control
2359 * @uinfo: control element information
2360 *
2361 * Returns 0 for success.
2362 */
2363int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
2364 struct snd_ctl_elem_info *uinfo)
2365{
2366 struct soc_mixer_control *mc =
2367 (struct soc_mixer_control *)kcontrol->private_value;
2368 int max = mc->max;
2369 int min = mc->min;
2370
2371 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2372 uinfo->count = 2;
2373 uinfo->value.integer.min = 0;
2374 uinfo->value.integer.max = max-min;
2375
2376 return 0;
2377}
2378EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx);
2379
2380/**
2381 * snd_soc_get_volsw_2r_sx - double with tlv and variable data size
2382 * mixer get callback
2383 * @kcontrol: mixer control
2384 * @uinfo: control element information
2385 *
2386 * Returns 0 for success.
2387 */
2388int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
2389 struct snd_ctl_elem_value *ucontrol)
2390{
2391 struct soc_mixer_control *mc =
2392 (struct soc_mixer_control *)kcontrol->private_value;
2393 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2394 unsigned int mask = (1<<mc->shift)-1;
2395 int min = mc->min;
2396 int val = snd_soc_read(codec, mc->reg) & mask;
2397 int valr = snd_soc_read(codec, mc->rreg) & mask;
2398
2399 ucontrol->value.integer.value[0] = ((val & 0xff)-min);
2400 ucontrol->value.integer.value[1] = ((valr & 0xff)-min);
2401 return 0;
2402}
2403EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx);
2404
2405/**
2406 * snd_soc_put_volsw_2r_sx - double with tlv and variable data size
2407 * mixer put callback
2408 * @kcontrol: mixer control
2409 * @uinfo: control element information
2410 *
2411 * Returns 0 for success.
2412 */
2413int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
2414 struct snd_ctl_elem_value *ucontrol)
2415{
2416 struct soc_mixer_control *mc =
2417 (struct soc_mixer_control *)kcontrol->private_value;
2418 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2419 unsigned int mask = (1<<mc->shift)-1;
2420 int min = mc->min;
2421 int ret;
2422 unsigned int val, valr, oval, ovalr;
2423
2424 val = ((ucontrol->value.integer.value[0]+min) & 0xff);
2425 val &= mask;
2426 valr = ((ucontrol->value.integer.value[1]+min) & 0xff);
2427 valr &= mask;
2428
2429 oval = snd_soc_read(codec, mc->reg) & mask;
2430 ovalr = snd_soc_read(codec, mc->rreg) & mask;
2431
2432 ret = 0;
2433 if (oval != val) {
2434 ret = snd_soc_write(codec, mc->reg, val);
2435 if (ret < 0)
2436 return 0;
2437 ret = 1;
2438 }
2439 if (ovalr != valr) {
2440 ret = snd_soc_write(codec, mc->rreg, valr);
2441 if (ret < 0)
2442 return 0;
2443 ret = 1;
2444 }
2445
2446 return 0;
2447}
2448EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
2449
2450/**
2356 * snd_soc_dai_set_sysclk - configure DAI system or master clock. 2451 * snd_soc_dai_set_sysclk - configure DAI system or master clock.
2357 * @dai: DAI 2452 * @dai: DAI
2358 * @clk_id: DAI specific clock ID 2453 * @clk_id: DAI specific clock ID