aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Pellegrin <chripell@gmail.com>2008-11-15 02:58:16 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2008-11-17 06:45:39 -0500
commit1cad1de1b216b355a60d907c103b2daf1a285345 (patch)
tree737b36d3d003a0e18010f5be3d2c90ed510ba591
parent6e5d9db271ab57789b09bcc61083ab71b7eabea9 (diff)
ASoC: UDA134x codec driver
Signed-off-by: Christian Pellegrin <chripell@fsfe.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--include/sound/l3.h18
-rw-r--r--include/sound/uda134x.h26
-rw-r--r--sound/soc/codecs/Kconfig8
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/l3.c91
-rw-r--r--sound/soc/codecs/uda134x.c656
-rw-r--r--sound/soc/codecs/uda134x_codec.h36
7 files changed, 839 insertions, 0 deletions
diff --git a/include/sound/l3.h b/include/sound/l3.h
new file mode 100644
index 000000000000..423a08f0f1b0
--- /dev/null
+++ b/include/sound/l3.h
@@ -0,0 +1,18 @@
1#ifndef _L3_H_
2#define _L3_H_ 1
3
4struct l3_pins {
5 void (*setdat)(int);
6 void (*setclk)(int);
7 void (*setmode)(int);
8 int data_hold;
9 int data_setup;
10 int clock_high;
11 int mode_hold;
12 int mode;
13 int mode_setup;
14};
15
16int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
17
18#endif
diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h
new file mode 100644
index 000000000000..475ef8bb7dcd
--- /dev/null
+++ b/include/sound/uda134x.h
@@ -0,0 +1,26 @@
1/*
2 * uda134x.h -- UDA134x ALSA SoC Codec driver
3 *
4 * Copyright 2007 Dension Audio Systems Ltd.
5 * Author: Zoltan Devai
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#ifndef _UDA134X_H
13#define _UDA134X_H
14
15#include <sound/l3.h>
16
17struct uda134x_platform_data {
18 struct l3_pins l3;
19 void (*power) (int);
20 int model;
21#define UDA134X_UDA1340 1
22#define UDA134X_UDA1341 2
23#define UDA134X_UDA1344 3
24};
25
26#endif /* _UDA134X_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8a84460a6f74..04f49f5c3c3d 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -10,6 +10,7 @@ config SND_SOC_ALL_CODECS
10 select SND_SOC_TLV320AIC26 if SPI_MASTER 10 select SND_SOC_TLV320AIC26 if SPI_MASTER
11 select SND_SOC_TLV320AIC3X if I2C 11 select SND_SOC_TLV320AIC3X if I2C
12 select SND_SOC_TWL4030 if TWL4030_CORE 12 select SND_SOC_TWL4030 if TWL4030_CORE
13 select SND_SOC_UDA134X
13 select SND_SOC_UDA1380 if I2C 14 select SND_SOC_UDA1380 if I2C
14 select SND_SOC_WM8510 if (I2C || SPI_MASTER) 15 select SND_SOC_WM8510 if (I2C || SPI_MASTER)
15 select SND_SOC_WM8580 if I2C 16 select SND_SOC_WM8580 if I2C
@@ -66,6 +67,9 @@ config SND_SOC_CS4270_VD33_ERRATA
66 bool 67 bool
67 depends on SND_SOC_CS4270 68 depends on SND_SOC_CS4270
68 69
70config SND_SOC_L3
71 tristate
72
69config SND_SOC_SSM2602 73config SND_SOC_SSM2602
70 tristate 74 tristate
71 75
@@ -85,6 +89,10 @@ config SND_SOC_TWL4030
85 tristate 89 tristate
86 depends on TWL4030_CORE 90 depends on TWL4030_CORE
87 91
92config SND_SOC_UDA134X
93 tristate
94 select SND_SOC_L3
95
88config SND_SOC_UDA1380 96config SND_SOC_UDA1380
89 tristate 97 tristate
90 98
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7ae17a6ea271..de6572356d1b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -3,11 +3,13 @@ snd-soc-ad1980-objs := ad1980.o
3snd-soc-ad73311-objs := ad73311.o 3snd-soc-ad73311-objs := ad73311.o
4snd-soc-ak4535-objs := ak4535.o 4snd-soc-ak4535-objs := ak4535.o
5snd-soc-cs4270-objs := cs4270.o 5snd-soc-cs4270-objs := cs4270.o
6snd-soc-l3-objs := l3.o
6snd-soc-ssm2602-objs := ssm2602.o 7snd-soc-ssm2602-objs := ssm2602.o
7snd-soc-tlv320aic23-objs := tlv320aic23.o 8snd-soc-tlv320aic23-objs := tlv320aic23.o
8snd-soc-tlv320aic26-objs := tlv320aic26.o 9snd-soc-tlv320aic26-objs := tlv320aic26.o
9snd-soc-tlv320aic3x-objs := tlv320aic3x.o 10snd-soc-tlv320aic3x-objs := tlv320aic3x.o
10snd-soc-twl4030-objs := twl4030.o 11snd-soc-twl4030-objs := twl4030.o
12snd-soc-uda134x-objs := uda134x.o
11snd-soc-uda1380-objs := uda1380.o 13snd-soc-uda1380-objs := uda1380.o
12snd-soc-wm8510-objs := wm8510.o 14snd-soc-wm8510-objs := wm8510.o
13snd-soc-wm8580-objs := wm8580.o 15snd-soc-wm8580-objs := wm8580.o
@@ -27,11 +29,13 @@ obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
27obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o 29obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
28obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o 30obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
29obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o 31obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
32obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
30obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o 33obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
31obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o 34obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
32obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o 35obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
33obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o 36obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
34obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o 37obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
38obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
35obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o 39obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
36obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o 40obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
37obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o 41obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
new file mode 100644
index 000000000000..5353af58862c
--- /dev/null
+++ b/sound/soc/codecs/l3.c
@@ -0,0 +1,91 @@
1/*
2 * L3 code
3 *
4 * Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 *
11 * based on:
12 *
13 * L3 bus algorithm module.
14 *
15 * Copyright (C) 2001 Russell King, All Rights Reserved.
16 *
17 *
18 */
19
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/delay.h>
23
24#include <sound/l3.h>
25
26/*
27 * Send one byte of data to the chip. Data is latched into the chip on
28 * the rising edge of the clock.
29 */
30static void sendbyte(struct l3_pins *adap, unsigned int byte)
31{
32 int i;
33
34 for (i = 0; i < 8; i++) {
35 adap->setclk(0);
36 udelay(adap->data_hold);
37 adap->setdat(byte & 1);
38 udelay(adap->data_setup);
39 adap->setclk(1);
40 udelay(adap->clock_high);
41 byte >>= 1;
42 }
43}
44
45/*
46 * Send a set of bytes to the chip. We need to pulse the MODE line
47 * between each byte, but never at the start nor at the end of the
48 * transfer.
49 */
50static void sendbytes(struct l3_pins *adap, const u8 *buf,
51 int len)
52{
53 int i;
54
55 for (i = 0; i < len; i++) {
56 if (i) {
57 udelay(adap->mode_hold);
58 adap->setmode(0);
59 udelay(adap->mode);
60 }
61 adap->setmode(1);
62 udelay(adap->mode_setup);
63 sendbyte(adap, buf[i]);
64 }
65}
66
67int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
68{
69 adap->setclk(1);
70 adap->setdat(1);
71 adap->setmode(1);
72 udelay(adap->mode);
73
74 adap->setmode(0);
75 udelay(adap->mode_setup);
76 sendbyte(adap, addr);
77 udelay(adap->mode_hold);
78
79 sendbytes(adap, data, len);
80
81 adap->setclk(1);
82 adap->setdat(1);
83 adap->setmode(0);
84
85 return len;
86}
87EXPORT_SYMBOL_GPL(l3_write);
88
89MODULE_DESCRIPTION("L3 bit-banging driver");
90MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
91MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
new file mode 100644
index 000000000000..04b30da10228
--- /dev/null
+++ b/sound/soc/codecs/uda134x.c
@@ -0,0 +1,656 @@
1/*
2 * uda134x.c -- UDA134X ALSA SoC Codec driver
3 *
4 * Modifications by Christian Pellegrin <chripell@evolware.org>
5 *
6 * Copyright 2007 Dension Audio Systems Ltd.
7 * Author: Zoltan Devai
8 *
9 * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/module.h>
17#include <linux/delay.h>
18#include <sound/pcm.h>
19#include <sound/pcm_params.h>
20#include <sound/soc.h>
21#include <sound/soc-dapm.h>
22#include <sound/initval.h>
23
24#include <sound/uda134x.h>
25#include <sound/l3.h>
26
27#include "uda134x_codec.h"
28
29
30#define POWER_OFF_ON_STANDBY 1
31/*
32 ALSA SOC usually puts the device in standby mode when it's not used
33 for sometime. If you define POWER_OFF_ON_STANDBY the driver will
34 turn off the ADC/DAC when this callback is invoked and turn it back
35 on when needed. Unfortunately this will result in a very light bump
36 (it can be audible only with good earphones). If this bothers you
37 just comment this line, you will have slightly higher power
38 consumption . Please note that sending the L3 command for ADC is
39 enough to make the bump, so it doesn't make difference if you
40 completely take off power from the codec.
41 */
42
43#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
44#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
45 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
46
47struct uda134x_priv {
48 int sysclk;
49 int dai_fmt;
50
51 struct snd_pcm_substream *master_substream;
52 struct snd_pcm_substream *slave_substream;
53};
54
55/* In-data addresses are hard-coded into the reg-cache values */
56static const char uda134x_reg[UDA134X_REGS_NUM] = {
57 /* Extended address registers */
58 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
59 /* Status, data regs */
60 0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
61};
62
63/*
64 * The codec has no support for reading its registers except for peak level...
65 */
66static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
67 unsigned int reg)
68{
69 u8 *cache = codec->reg_cache;
70
71 if (reg >= UDA134X_REGS_NUM)
72 return -1;
73 return cache[reg];
74}
75
76/*
77 * Write the register cache
78 */
79static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
80 u8 reg, unsigned int value)
81{
82 u8 *cache = codec->reg_cache;
83
84 if (reg >= UDA134X_REGS_NUM)
85 return;
86 cache[reg] = value;
87}
88
89/*
90 * Write to the uda134x registers
91 *
92 */
93static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
94 unsigned int value)
95{
96 int ret;
97 u8 addr;
98 u8 data = value;
99 struct uda134x_platform_data *pd = codec->control_data;
100
101 pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
102
103 if (reg >= UDA134X_REGS_NUM) {
104 printk(KERN_ERR "%s unkown register: reg: %d",
105 __func__, reg);
106 return -EINVAL;
107 }
108
109 uda134x_write_reg_cache(codec, reg, value);
110
111 switch (reg) {
112 case UDA134X_STATUS0:
113 case UDA134X_STATUS1:
114 addr = UDA134X_STATUS_ADDR;
115 break;
116 case UDA134X_DATA000:
117 case UDA134X_DATA001:
118 case UDA134X_DATA010:
119 addr = UDA134X_DATA0_ADDR;
120 break;
121 case UDA134X_DATA1:
122 addr = UDA134X_DATA1_ADDR;
123 break;
124 default:
125 /* It's an extended address register */
126 addr = (reg | UDA134X_EXTADDR_PREFIX);
127
128 ret = l3_write(&pd->l3,
129 UDA134X_DATA0_ADDR, &addr, 1);
130 if (ret != 1)
131 return -EIO;
132
133 addr = UDA134X_DATA0_ADDR;
134 data = (value | UDA134X_EXTDATA_PREFIX);
135 break;
136 }
137
138 ret = l3_write(&pd->l3,
139 addr, &data, 1);
140 if (ret != 1)
141 return -EIO;
142
143 return 0;
144}
145
146static inline void uda134x_reset(struct snd_soc_codec *codec)
147{
148 u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
149 uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6));
150 msleep(1);
151 uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6));
152}
153
154static int uda134x_mute(struct snd_soc_dai *dai, int mute)
155{
156 struct snd_soc_codec *codec = dai->codec;
157 u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010);
158
159 pr_debug("%s mute: %d\n", __func__, mute);
160
161 if (mute)
162 mute_reg |= (1<<2);
163 else
164 mute_reg &= ~(1<<2);
165
166 uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
167
168 return 0;
169}
170
171static int uda134x_startup(struct snd_pcm_substream *substream)
172{
173 struct snd_soc_pcm_runtime *rtd = substream->private_data;
174 struct snd_soc_device *socdev = rtd->socdev;
175 struct snd_soc_codec *codec = socdev->codec;
176 struct uda134x_priv *uda134x = codec->private_data;
177 struct snd_pcm_runtime *master_runtime;
178
179 if (uda134x->master_substream) {
180 master_runtime = uda134x->master_substream->runtime;
181
182 pr_debug("%s constraining to %d bits at %d\n", __func__,
183 master_runtime->sample_bits,
184 master_runtime->rate);
185
186 snd_pcm_hw_constraint_minmax(substream->runtime,
187 SNDRV_PCM_HW_PARAM_RATE,
188 master_runtime->rate,
189 master_runtime->rate);
190
191 snd_pcm_hw_constraint_minmax(substream->runtime,
192 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
193 master_runtime->sample_bits,
194 master_runtime->sample_bits);
195
196 uda134x->slave_substream = substream;
197 } else
198 uda134x->master_substream = substream;
199
200 return 0;
201}
202
203static void uda134x_shutdown(struct snd_pcm_substream *substream)
204{
205 struct snd_soc_pcm_runtime *rtd = substream->private_data;
206 struct snd_soc_device *socdev = rtd->socdev;
207 struct snd_soc_codec *codec = socdev->codec;
208 struct uda134x_priv *uda134x = codec->private_data;
209
210 if (uda134x->master_substream == substream)
211 uda134x->master_substream = uda134x->slave_substream;
212
213 uda134x->slave_substream = NULL;
214}
215
216static int uda134x_hw_params(struct snd_pcm_substream *substream,
217 struct snd_pcm_hw_params *params)
218{
219 struct snd_soc_pcm_runtime *rtd = substream->private_data;
220 struct snd_soc_device *socdev = rtd->socdev;
221 struct snd_soc_codec *codec = socdev->codec;
222 struct uda134x_priv *uda134x = codec->private_data;
223 u8 hw_params;
224
225 if (substream == uda134x->slave_substream) {
226 pr_debug("%s ignoring hw_params for slave substream\n",
227 __func__);
228 return 0;
229 }
230
231 hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
232 hw_params &= STATUS0_SYSCLK_MASK;
233 hw_params &= STATUS0_DAIFMT_MASK;
234
235 pr_debug("%s sysclk: %d, rate:%d\n", __func__,
236 uda134x->sysclk, params_rate(params));
237
238 /* set SYSCLK / fs ratio */
239 switch (uda134x->sysclk / params_rate(params)) {
240 case 512:
241 break;
242 case 384:
243 hw_params |= (1<<4);
244 break;
245 case 256:
246 hw_params |= (1<<5);
247 break;
248 default:
249 printk(KERN_ERR "%s unsupported fs\n", __func__);
250 return -EINVAL;
251 }
252
253 pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
254 uda134x->dai_fmt, params_format(params));
255
256 /* set DAI format and word length */
257 switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
258 case SND_SOC_DAIFMT_I2S:
259 break;
260 case SND_SOC_DAIFMT_RIGHT_J:
261 switch (params_format(params)) {
262 case SNDRV_PCM_FORMAT_S16_LE:
263 hw_params |= (1<<1);
264 break;
265 case SNDRV_PCM_FORMAT_S18_3LE:
266 hw_params |= (1<<2);
267 break;
268 case SNDRV_PCM_FORMAT_S20_3LE:
269 hw_params |= ((1<<2) | (1<<1));
270 break;
271 default:
272 printk(KERN_ERR "%s unsupported format (right)\n",
273 __func__);
274 return -EINVAL;
275 }
276 break;
277 case SND_SOC_DAIFMT_LEFT_J:
278 hw_params |= (1<<3);
279 break;
280 default:
281 printk(KERN_ERR "%s unsupported format\n", __func__);
282 return -EINVAL;
283 }
284
285 uda134x_write(codec, UDA134X_STATUS0, hw_params);
286
287 return 0;
288}
289
290static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
291 int clk_id, unsigned int freq, int dir)
292{
293 struct snd_soc_codec *codec = codec_dai->codec;
294 struct uda134x_priv *uda134x = codec->private_data;
295
296 pr_debug("%s clk_id: %d, freq: %d, dir: %d\n", __func__,
297 clk_id, freq, dir);
298
299 /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
300 because the codec is slave. Of course limitations of the clock
301 master (the IIS controller) apply.
302 We'll error out on set_hw_params if it's not OK */
303 if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
304 uda134x->sysclk = freq;
305 return 0;
306 }
307
308 printk(KERN_ERR "%s unsupported sysclk\n", __func__);
309 return -EINVAL;
310}
311
312static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
313 unsigned int fmt)
314{
315 struct snd_soc_codec *codec = codec_dai->codec;
316 struct uda134x_priv *uda134x = codec->private_data;
317
318 pr_debug("%s fmt: %08X\n", __func__, fmt);
319
320 /* codec supports only full slave mode */
321 if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
322 printk(KERN_ERR "%s unsupported slave mode\n", __func__);
323 return -EINVAL;
324 }
325
326 /* no support for clock inversion */
327 if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
328 printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
329 return -EINVAL;
330 }
331
332 /* We can't setup DAI format here as it depends on the word bit num */
333 /* so let's just store the value for later */
334 uda134x->dai_fmt = fmt;
335
336 return 0;
337}
338
339static int uda134x_set_bias_level(struct snd_soc_codec *codec,
340 enum snd_soc_bias_level level)
341{
342 u8 reg;
343 struct uda134x_platform_data *pd = codec->control_data;
344 int i;
345 u8 *cache = codec->reg_cache;
346
347 pr_debug("%s bias level %d\n", __func__, level);
348
349 switch (level) {
350 case SND_SOC_BIAS_ON:
351 /* ADC, DAC on */
352 reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
353 uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
354 break;
355 case SND_SOC_BIAS_PREPARE:
356 /* power on */
357 if (pd->power) {
358 pd->power(1);
359 /* Sync reg_cache with the hardware */
360 for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
361 codec->write(codec, i, *cache++);
362 }
363 break;
364 case SND_SOC_BIAS_STANDBY:
365 /* ADC, DAC power off */
366 reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
367 uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
368 break;
369 case SND_SOC_BIAS_OFF:
370 /* power off */
371 if (pd->power)
372 pd->power(0);
373 break;
374 }
375 codec->bias_level = level;
376 return 0;
377}
378
379static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
380 "Minimum2", "Maximum"};
381static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
382static const char *uda134x_mixmode[] = {"Differential", "Analog1",
383 "Analog2", "Both"};
384
385static const struct soc_enum uda134x_mixer_enum[] = {
386SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
387SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
388SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
389};
390
391static const struct snd_kcontrol_new uda1341_snd_controls[] = {
392SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
393SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
394SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
395SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
396
397SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
398SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
399
400SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
401SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
402
403SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
404SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
405SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
406
407SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
408SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
409SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
410
411SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
412SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
413SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
414SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
415SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
416SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
417};
418
419static const struct snd_kcontrol_new uda1340_snd_controls[] = {
420SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
421
422SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
423SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
424
425SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
426SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
427
428SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
429};
430
431static int uda134x_add_controls(struct snd_soc_codec *codec)
432{
433 int err, i, n;
434 const struct snd_kcontrol_new *ctrls;
435 struct uda134x_platform_data *pd = codec->control_data;
436
437 switch (pd->model) {
438 case UDA134X_UDA1340:
439 case UDA134X_UDA1344:
440 n = ARRAY_SIZE(uda1340_snd_controls);
441 ctrls = uda1340_snd_controls;
442 break;
443 case UDA134X_UDA1341:
444 n = ARRAY_SIZE(uda1341_snd_controls);
445 ctrls = uda1341_snd_controls;
446 break;
447 default:
448 printk(KERN_ERR "%s unkown codec type: %d",
449 __func__, pd->model);
450 return -EINVAL;
451 }
452
453 for (i = 0; i < n; i++) {
454 err = snd_ctl_add(codec->card,
455 snd_soc_cnew(&ctrls[i],
456 codec, NULL));
457 if (err < 0)
458 return err;
459 }
460
461 return 0;
462}
463
464struct snd_soc_dai uda134x_dai = {
465 .name = "UDA134X",
466 /* playback capabilities */
467 .playback = {
468 .stream_name = "Playback",
469 .channels_min = 1,
470 .channels_max = 2,
471 .rates = UDA134X_RATES,
472 .formats = UDA134X_FORMATS,
473 },
474 /* capture capabilities */
475 .capture = {
476 .stream_name = "Capture",
477 .channels_min = 1,
478 .channels_max = 2,
479 .rates = UDA134X_RATES,
480 .formats = UDA134X_FORMATS,
481 },
482 /* pcm operations */
483 .ops = {
484 .startup = uda134x_startup,
485 .shutdown = uda134x_shutdown,
486 .hw_params = uda134x_hw_params,
487 },
488 /* DAI operations */
489 .dai_ops = {
490 .digital_mute = uda134x_mute,
491 .set_sysclk = uda134x_set_dai_sysclk,
492 .set_fmt = uda134x_set_dai_fmt,
493 }
494};
495EXPORT_SYMBOL(uda134x_dai);
496
497
498static int uda134x_soc_probe(struct platform_device *pdev)
499{
500 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
501 struct snd_soc_codec *codec;
502 struct uda134x_priv *uda134x;
503 void *codec_setup_data = socdev->codec_data;
504 int ret = -ENOMEM;
505 struct uda134x_platform_data *pd;
506
507 printk(KERN_INFO "UDA134X SoC Audio Codec\n");
508
509 if (!codec_setup_data) {
510 printk(KERN_ERR "UDA134X SoC codec: "
511 "missing L3 bitbang function\n");
512 return -ENODEV;
513 }
514
515 pd = codec_setup_data;
516 switch (pd->model) {
517 case UDA134X_UDA1340:
518 case UDA134X_UDA1341:
519 case UDA134X_UDA1344:
520 break;
521 default:
522 printk(KERN_ERR "UDA134X SoC codec: "
523 "unsupported model %d\n",
524 pd->model);
525 return -EINVAL;
526 }
527
528 socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
529 if (socdev->codec == NULL)
530 return ret;
531
532 codec = socdev->codec;
533
534 uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
535 if (uda134x == NULL)
536 goto priv_err;
537 codec->private_data = uda134x;
538
539 codec->reg_cache = kmemdup(uda134x_reg, sizeof(uda134x_reg),
540 GFP_KERNEL);
541 if (codec->reg_cache == NULL)
542 goto reg_err;
543
544 mutex_init(&codec->mutex);
545
546 codec->reg_cache_size = sizeof(uda134x_reg);
547 codec->reg_cache_step = 1;
548
549 codec->name = "UDA134X";
550 codec->owner = THIS_MODULE;
551 codec->dai = &uda134x_dai;
552 codec->num_dai = 1;
553 codec->read = uda134x_read_reg_cache;
554 codec->write = uda134x_write;
555#ifdef POWER_OFF_ON_STANDBY
556 codec->set_bias_level = uda134x_set_bias_level;
557#endif
558 INIT_LIST_HEAD(&codec->dapm_widgets);
559 INIT_LIST_HEAD(&codec->dapm_paths);
560
561 codec->control_data = codec_setup_data;
562
563 if (pd->power)
564 pd->power(1);
565
566 uda134x_reset(codec);
567
568 /* register pcms */
569 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
570 if (ret < 0) {
571 printk(KERN_ERR "UDA134X: failed to register pcms\n");
572 goto pcm_err;
573 }
574
575 ret = uda134x_add_controls(codec);
576 if (ret < 0) {
577 printk(KERN_ERR "UDA134X: failed to register controls\n");
578 goto pcm_err;
579 }
580
581 ret = snd_soc_register_card(socdev);
582 if (ret < 0) {
583 printk(KERN_ERR "UDA134X: failed to register card\n");
584 goto card_err;
585 }
586
587 return 0;
588
589card_err:
590 snd_soc_free_pcms(socdev);
591 snd_soc_dapm_free(socdev);
592pcm_err:
593 kfree(codec->reg_cache);
594reg_err:
595 kfree(codec->private_data);
596priv_err:
597 kfree(codec);
598 return ret;
599}
600
601/* power down chip */
602static int uda134x_soc_remove(struct platform_device *pdev)
603{
604 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
605 struct snd_soc_codec *codec = socdev->codec;
606
607 uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
608 uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
609
610 snd_soc_free_pcms(socdev);
611 snd_soc_dapm_free(socdev);
612
613 kfree(codec->private_data);
614 kfree(codec->reg_cache);
615 kfree(codec);
616
617 return 0;
618}
619
620#if defined(CONFIG_PM)
621static int uda134x_soc_suspend(struct platform_device *pdev,
622 pm_message_t state)
623{
624 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
625 struct snd_soc_codec *codec = socdev->codec;
626
627 uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
628 uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
629 return 0;
630}
631
632static int uda134x_soc_resume(struct platform_device *pdev)
633{
634 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
635 struct snd_soc_codec *codec = socdev->codec;
636
637 uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
638 uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
639 return 0;
640}
641#else
642#define uda134x_soc_suspend NULL
643#define uda134x_soc_resume NULL
644#endif /* CONFIG_PM */
645
646struct snd_soc_codec_device soc_codec_dev_uda134x = {
647 .probe = uda134x_soc_probe,
648 .remove = uda134x_soc_remove,
649 .suspend = uda134x_soc_suspend,
650 .resume = uda134x_soc_resume,
651};
652EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);
653
654MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
655MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
656MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda134x_codec.h b/sound/soc/codecs/uda134x_codec.h
new file mode 100644
index 000000000000..94f440490b31
--- /dev/null
+++ b/sound/soc/codecs/uda134x_codec.h
@@ -0,0 +1,36 @@
1#ifndef _UDA134X_CODEC_H
2#define _UDA134X_CODEC_H
3
4#define UDA134X_L3ADDR 5
5#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0)
6#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1)
7#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2)
8
9#define UDA134X_EXTADDR_PREFIX 0xC0
10#define UDA134X_EXTDATA_PREFIX 0xE0
11
12/* UDA134X registers */
13#define UDA134X_EA000 0
14#define UDA134X_EA001 1
15#define UDA134X_EA010 2
16#define UDA134X_EA011 3
17#define UDA134X_EA100 4
18#define UDA134X_EA101 5
19#define UDA134X_EA110 6
20#define UDA134X_EA111 7
21#define UDA134X_STATUS0 8
22#define UDA134X_STATUS1 9
23#define UDA134X_DATA000 10
24#define UDA134X_DATA001 11
25#define UDA134X_DATA010 12
26#define UDA134X_DATA1 13
27
28#define UDA134X_REGS_NUM 14
29
30#define STATUS0_DAIFMT_MASK (~(7<<1))
31#define STATUS0_SYSCLK_MASK (~(3<<4))
32
33extern struct snd_soc_dai uda134x_dai;
34extern struct snd_soc_codec_device soc_codec_dev_uda134x;
35
36#endif