diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2008-08-06 08:18:26 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-08-13 05:46:19 -0400 |
commit | e88ba01544f8b8cce64d08b2982715516793225c (patch) | |
tree | 879af62837fb26948aaed9149a77101b1a522cad | |
parent | c9a7dc2c5279830c0ad77715c0ace3e1edb07f4c (diff) |
ALSA: ASoC: Add WM8580 CODEC driver
The WM8580 is an audio CODEC designed for DVD and surround sound
applications, offering three stereo DACs and a stereo ADC.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r-- | sound/soc/codecs/Kconfig | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm8580.c | 1055 | ||||
-rw-r--r-- | sound/soc/codecs/wm8580.h | 42 |
4 files changed, 1103 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d7bacf6c529c..eb79c5cab47a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -5,6 +5,7 @@ config SND_SOC_ALL_CODECS | |||
5 | select SND_SOC_AK4535 | 5 | select SND_SOC_AK4535 |
6 | select SND_SOC_UDA1380 | 6 | select SND_SOC_UDA1380 |
7 | select SND_SOC_WM8510 | 7 | select SND_SOC_WM8510 |
8 | select SND_SOC_WM8580 | ||
8 | select SND_SOC_WM8731 | 9 | select SND_SOC_WM8731 |
9 | select SND_SOC_WM8750 | 10 | select SND_SOC_WM8750 |
10 | select SND_SOC_WM8753 | 11 | select SND_SOC_WM8753 |
@@ -38,6 +39,9 @@ config SND_SOC_UDA1380 | |||
38 | config SND_SOC_WM8510 | 39 | config SND_SOC_WM8510 |
39 | tristate | 40 | tristate |
40 | 41 | ||
42 | config SND_SOC_WM8580 | ||
43 | tristate | ||
44 | |||
41 | config SND_SOC_WM8731 | 45 | config SND_SOC_WM8731 |
42 | tristate | 46 | tristate |
43 | 47 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 98808d945ded..7c694ca6b850 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -3,6 +3,7 @@ snd-soc-ad1980-objs := ad1980.o | |||
3 | snd-soc-ak4535-objs := ak4535.o | 3 | snd-soc-ak4535-objs := ak4535.o |
4 | snd-soc-uda1380-objs := uda1380.o | 4 | snd-soc-uda1380-objs := uda1380.o |
5 | snd-soc-wm8510-objs := wm8510.o | 5 | snd-soc-wm8510-objs := wm8510.o |
6 | snd-soc-wm8580-objs := wm8580.o | ||
6 | snd-soc-wm8731-objs := wm8731.o | 7 | snd-soc-wm8731-objs := wm8731.o |
7 | snd-soc-wm8750-objs := wm8750.o | 8 | snd-soc-wm8750-objs := wm8750.o |
8 | snd-soc-wm8753-objs := wm8753.o | 9 | snd-soc-wm8753-objs := wm8753.o |
@@ -19,6 +20,7 @@ obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o | |||
19 | obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o | 20 | obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o |
20 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o | 21 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o |
21 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o | 22 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o |
23 | obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o | ||
22 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o | 24 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o |
23 | obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o | 25 | obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o |
24 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o | 26 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o |
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c new file mode 100644 index 000000000000..df1ffbe305bf --- /dev/null +++ b/sound/soc/codecs/wm8580.c | |||
@@ -0,0 +1,1055 @@ | |||
1 | /* | ||
2 | * wm8580.c -- WM8580 ALSA Soc Audio driver | ||
3 | * | ||
4 | * Copyright 2008 Wolfson Microelectronics PLC. | ||
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 | * Notes: | ||
12 | * The WM8580 is a multichannel codec with S/PDIF support, featuring six | ||
13 | * DAC channels and two ADC channels. | ||
14 | * | ||
15 | * Currently only the primary audio interface is supported - S/PDIF and | ||
16 | * the secondary audio interfaces are not. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/version.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/pm.h> | ||
26 | #include <linux/i2c.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <sound/core.h> | ||
29 | #include <sound/pcm.h> | ||
30 | #include <sound/pcm_params.h> | ||
31 | #include <sound/soc.h> | ||
32 | #include <sound/soc-dapm.h> | ||
33 | #include <sound/tlv.h> | ||
34 | #include <sound/initval.h> | ||
35 | #include <asm/div64.h> | ||
36 | |||
37 | #include "wm8580.h" | ||
38 | |||
39 | #define AUDIO_NAME "wm8580" | ||
40 | #define WM8580_VERSION "0.1" | ||
41 | |||
42 | struct pll_state { | ||
43 | unsigned int in; | ||
44 | unsigned int out; | ||
45 | }; | ||
46 | |||
47 | /* codec private data */ | ||
48 | struct wm8580_priv { | ||
49 | struct pll_state a; | ||
50 | struct pll_state b; | ||
51 | }; | ||
52 | |||
53 | /* WM8580 register space */ | ||
54 | #define WM8580_PLLA1 0x00 | ||
55 | #define WM8580_PLLA2 0x01 | ||
56 | #define WM8580_PLLA3 0x02 | ||
57 | #define WM8580_PLLA4 0x03 | ||
58 | #define WM8580_PLLB1 0x04 | ||
59 | #define WM8580_PLLB2 0x05 | ||
60 | #define WM8580_PLLB3 0x06 | ||
61 | #define WM8580_PLLB4 0x07 | ||
62 | #define WM8580_CLKSEL 0x08 | ||
63 | #define WM8580_PAIF1 0x09 | ||
64 | #define WM8580_PAIF2 0x0A | ||
65 | #define WM8580_SAIF1 0x0B | ||
66 | #define WM8580_PAIF3 0x0C | ||
67 | #define WM8580_PAIF4 0x0D | ||
68 | #define WM8580_SAIF2 0x0E | ||
69 | #define WM8580_DAC_CONTROL1 0x0F | ||
70 | #define WM8580_DAC_CONTROL2 0x10 | ||
71 | #define WM8580_DAC_CONTROL3 0x11 | ||
72 | #define WM8580_DAC_CONTROL4 0x12 | ||
73 | #define WM8580_DAC_CONTROL5 0x13 | ||
74 | #define WM8580_DIGITAL_ATTENUATION_DACL1 0x14 | ||
75 | #define WM8580_DIGITAL_ATTENUATION_DACR1 0x15 | ||
76 | #define WM8580_DIGITAL_ATTENUATION_DACL2 0x16 | ||
77 | #define WM8580_DIGITAL_ATTENUATION_DACR2 0x17 | ||
78 | #define WM8580_DIGITAL_ATTENUATION_DACL3 0x18 | ||
79 | #define WM8580_DIGITAL_ATTENUATION_DACR3 0x19 | ||
80 | #define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C | ||
81 | #define WM8580_ADC_CONTROL1 0x1D | ||
82 | #define WM8580_SPDTXCHAN0 0x1E | ||
83 | #define WM8580_SPDTXCHAN1 0x1F | ||
84 | #define WM8580_SPDTXCHAN2 0x20 | ||
85 | #define WM8580_SPDTXCHAN3 0x21 | ||
86 | #define WM8580_SPDTXCHAN4 0x22 | ||
87 | #define WM8580_SPDTXCHAN5 0x23 | ||
88 | #define WM8580_SPDMODE 0x24 | ||
89 | #define WM8580_INTMASK 0x25 | ||
90 | #define WM8580_GPO1 0x26 | ||
91 | #define WM8580_GPO2 0x27 | ||
92 | #define WM8580_GPO3 0x28 | ||
93 | #define WM8580_GPO4 0x29 | ||
94 | #define WM8580_GPO5 0x2A | ||
95 | #define WM8580_INTSTAT 0x2B | ||
96 | #define WM8580_SPDRXCHAN1 0x2C | ||
97 | #define WM8580_SPDRXCHAN2 0x2D | ||
98 | #define WM8580_SPDRXCHAN3 0x2E | ||
99 | #define WM8580_SPDRXCHAN4 0x2F | ||
100 | #define WM8580_SPDRXCHAN5 0x30 | ||
101 | #define WM8580_SPDSTAT 0x31 | ||
102 | #define WM8580_PWRDN1 0x32 | ||
103 | #define WM8580_PWRDN2 0x33 | ||
104 | #define WM8580_READBACK 0x34 | ||
105 | #define WM8580_RESET 0x35 | ||
106 | |||
107 | /* PLLB4 (register 7h) */ | ||
108 | #define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60 | ||
109 | #define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20 | ||
110 | #define WM8580_PLLB4_MCLKOUTSRC_PLLB 0x40 | ||
111 | #define WM8580_PLLB4_MCLKOUTSRC_OSC 0x60 | ||
112 | |||
113 | #define WM8580_PLLB4_CLKOUTSRC_MASK 0x180 | ||
114 | #define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080 | ||
115 | #define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100 | ||
116 | #define WM8580_PLLB4_CLKOUTSRC_OSCCLK 0x180 | ||
117 | |||
118 | /* CLKSEL (register 8h) */ | ||
119 | #define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03 | ||
120 | #define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01 | ||
121 | #define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02 | ||
122 | |||
123 | /* AIF control 1 (registers 9h-bh) */ | ||
124 | #define WM8580_AIF_RATE_MASK 0x7 | ||
125 | #define WM8580_AIF_RATE_128 0x0 | ||
126 | #define WM8580_AIF_RATE_192 0x1 | ||
127 | #define WM8580_AIF_RATE_256 0x2 | ||
128 | #define WM8580_AIF_RATE_384 0x3 | ||
129 | #define WM8580_AIF_RATE_512 0x4 | ||
130 | #define WM8580_AIF_RATE_768 0x5 | ||
131 | #define WM8580_AIF_RATE_1152 0x6 | ||
132 | |||
133 | #define WM8580_AIF_BCLKSEL_MASK 0x18 | ||
134 | #define WM8580_AIF_BCLKSEL_64 0x00 | ||
135 | #define WM8580_AIF_BCLKSEL_128 0x08 | ||
136 | #define WM8580_AIF_BCLKSEL_256 0x10 | ||
137 | #define WM8580_AIF_BCLKSEL_SYSCLK 0x18 | ||
138 | |||
139 | #define WM8580_AIF_MS 0x20 | ||
140 | |||
141 | #define WM8580_AIF_CLKSRC_MASK 0xc0 | ||
142 | #define WM8580_AIF_CLKSRC_PLLA 0x40 | ||
143 | #define WM8580_AIF_CLKSRC_PLLB 0x40 | ||
144 | #define WM8580_AIF_CLKSRC_MCLK 0xc0 | ||
145 | |||
146 | /* AIF control 2 (registers ch-eh) */ | ||
147 | #define WM8580_AIF_FMT_MASK 0x03 | ||
148 | #define WM8580_AIF_FMT_RIGHTJ 0x00 | ||
149 | #define WM8580_AIF_FMT_LEFTJ 0x01 | ||
150 | #define WM8580_AIF_FMT_I2S 0x02 | ||
151 | #define WM8580_AIF_FMT_DSP 0x03 | ||
152 | |||
153 | #define WM8580_AIF_LENGTH_MASK 0x0c | ||
154 | #define WM8580_AIF_LENGTH_16 0x00 | ||
155 | #define WM8580_AIF_LENGTH_20 0x04 | ||
156 | #define WM8580_AIF_LENGTH_24 0x08 | ||
157 | #define WM8580_AIF_LENGTH_32 0x0c | ||
158 | |||
159 | #define WM8580_AIF_LRP 0x10 | ||
160 | #define WM8580_AIF_BCP 0x20 | ||
161 | |||
162 | /* Powerdown Register 1 (register 32h) */ | ||
163 | #define WM8580_PWRDN1_PWDN 0x001 | ||
164 | #define WM8580_PWRDN1_ALLDACPD 0x040 | ||
165 | |||
166 | /* Powerdown Register 2 (register 33h) */ | ||
167 | #define WM8580_PWRDN2_OSSCPD 0x001 | ||
168 | #define WM8580_PWRDN2_PLLAPD 0x002 | ||
169 | #define WM8580_PWRDN2_PLLBPD 0x004 | ||
170 | #define WM8580_PWRDN2_SPDIFPD 0x008 | ||
171 | #define WM8580_PWRDN2_SPDIFTXD 0x010 | ||
172 | #define WM8580_PWRDN2_SPDIFRXD 0x020 | ||
173 | |||
174 | #define WM8580_DAC_CONTROL5_MUTEALL 0x10 | ||
175 | |||
176 | /* | ||
177 | * wm8580 register cache | ||
178 | * We can't read the WM8580 register space when we | ||
179 | * are using 2 wire for device control, so we cache them instead. | ||
180 | */ | ||
181 | static const u16 wm8580_reg[] = { | ||
182 | 0x0121, 0x017e, 0x007d, 0x0014, /*R3*/ | ||
183 | 0x0121, 0x017e, 0x007d, 0x0194, /*R7*/ | ||
184 | 0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/ | ||
185 | 0x0182, 0x0082, 0x000a, 0x0024, /*R15*/ | ||
186 | 0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/ | ||
187 | 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/ | ||
188 | 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/ | ||
189 | 0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/ | ||
190 | 0x0000, 0x0000, 0x0031, 0x000b, /*R35*/ | ||
191 | 0x0039, 0x0000, 0x0010, 0x0032, /*R39*/ | ||
192 | 0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/ | ||
193 | 0x0000, 0x0000, 0x0000, 0x0000, /*R47*/ | ||
194 | 0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/ | ||
195 | 0x0000, 0x0000 /*R53*/ | ||
196 | }; | ||
197 | |||
198 | /* | ||
199 | * read wm8580 register cache | ||
200 | */ | ||
201 | static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec, | ||
202 | unsigned int reg) | ||
203 | { | ||
204 | u16 *cache = codec->reg_cache; | ||
205 | BUG_ON(reg > ARRAY_SIZE(wm8580_reg)); | ||
206 | return cache[reg]; | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * write wm8580 register cache | ||
211 | */ | ||
212 | static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec, | ||
213 | unsigned int reg, unsigned int value) | ||
214 | { | ||
215 | u16 *cache = codec->reg_cache; | ||
216 | |||
217 | cache[reg] = value; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * write to the WM8580 register space | ||
222 | */ | ||
223 | static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg, | ||
224 | unsigned int value) | ||
225 | { | ||
226 | u8 data[2]; | ||
227 | |||
228 | BUG_ON(reg > ARRAY_SIZE(wm8580_reg)); | ||
229 | |||
230 | /* Registers are 9 bits wide */ | ||
231 | value &= 0x1ff; | ||
232 | |||
233 | switch (reg) { | ||
234 | case WM8580_RESET: | ||
235 | /* Uncached */ | ||
236 | break; | ||
237 | default: | ||
238 | if (value == wm8580_read_reg_cache(codec, reg)) | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | /* data is | ||
243 | * D15..D9 WM8580 register offset | ||
244 | * D8...D0 register data | ||
245 | */ | ||
246 | data[0] = (reg << 1) | ((value >> 8) & 0x0001); | ||
247 | data[1] = value & 0x00ff; | ||
248 | |||
249 | wm8580_write_reg_cache(codec, reg, value); | ||
250 | if (codec->hw_write(codec->control_data, data, 2) == 2) | ||
251 | return 0; | ||
252 | else | ||
253 | return -EIO; | ||
254 | } | ||
255 | |||
256 | static inline unsigned int wm8580_read(struct snd_soc_codec *codec, | ||
257 | unsigned int reg) | ||
258 | { | ||
259 | switch (reg) { | ||
260 | default: | ||
261 | return wm8580_read_reg_cache(codec, reg); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); | ||
266 | |||
267 | static int wm8580_out_vu(struct snd_kcontrol *kcontrol, | ||
268 | struct snd_ctl_elem_value *ucontrol) | ||
269 | { | ||
270 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
271 | int reg = kcontrol->private_value & 0xff; | ||
272 | int reg2 = (kcontrol->private_value >> 24) & 0xff; | ||
273 | int ret; | ||
274 | u16 val; | ||
275 | |||
276 | /* Clear the register cache so we write without VU set */ | ||
277 | wm8580_write_reg_cache(codec, reg, 0); | ||
278 | wm8580_write_reg_cache(codec, reg2, 0); | ||
279 | |||
280 | ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); | ||
281 | if (ret < 0) | ||
282 | return ret; | ||
283 | |||
284 | /* Now write again with the volume update bit set */ | ||
285 | val = wm8580_read_reg_cache(codec, reg); | ||
286 | wm8580_write(codec, reg, val | 0x0100); | ||
287 | |||
288 | val = wm8580_read_reg_cache(codec, reg2); | ||
289 | wm8580_write(codec, reg2, val | 0x0100); | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | #define SOC_WM8580_OUT_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \ | ||
295 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ | ||
296 | .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | ||
297 | SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||
298 | .tlv.p = (tlv_array), \ | ||
299 | .info = snd_soc_info_volsw_2r, \ | ||
300 | .get = snd_soc_get_volsw_2r, .put = wm8580_out_vu, \ | ||
301 | .private_value = (reg_left) | ((shift) << 8) | \ | ||
302 | ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) } | ||
303 | |||
304 | static const struct snd_kcontrol_new wm8580_snd_controls[] = { | ||
305 | SOC_WM8580_OUT_DOUBLE_R_TLV("DAC1 Playback Volume", | ||
306 | WM8580_DIGITAL_ATTENUATION_DACL1, | ||
307 | WM8580_DIGITAL_ATTENUATION_DACR1, | ||
308 | 0, 0xff, 0, dac_tlv), | ||
309 | SOC_WM8580_OUT_DOUBLE_R_TLV("DAC2 Playback Volume", | ||
310 | WM8580_DIGITAL_ATTENUATION_DACL2, | ||
311 | WM8580_DIGITAL_ATTENUATION_DACR2, | ||
312 | 0, 0xff, 0, dac_tlv), | ||
313 | SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume", | ||
314 | WM8580_DIGITAL_ATTENUATION_DACL3, | ||
315 | WM8580_DIGITAL_ATTENUATION_DACR3, | ||
316 | 0, 0xff, 0, dac_tlv), | ||
317 | |||
318 | SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0), | ||
319 | SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0), | ||
320 | SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0), | ||
321 | |||
322 | SOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4, 0, 1, 1, 0), | ||
323 | SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4, 2, 3, 1, 0), | ||
324 | SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4, 4, 5, 1, 0), | ||
325 | |||
326 | SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0), | ||
327 | SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 0), | ||
328 | SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 0), | ||
329 | SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 0), | ||
330 | |||
331 | SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0), | ||
332 | SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), | ||
333 | }; | ||
334 | |||
335 | /* Add non-DAPM controls */ | ||
336 | static int wm8580_add_controls(struct snd_soc_codec *codec) | ||
337 | { | ||
338 | int err, i; | ||
339 | |||
340 | for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) { | ||
341 | err = snd_ctl_add(codec->card, | ||
342 | snd_soc_cnew(&wm8580_snd_controls[i], | ||
343 | codec, NULL)); | ||
344 | if (err < 0) | ||
345 | return err; | ||
346 | } | ||
347 | return 0; | ||
348 | } | ||
349 | static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { | ||
350 | SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1), | ||
351 | SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1), | ||
352 | SND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1), | ||
353 | |||
354 | SND_SOC_DAPM_OUTPUT("VOUT1L"), | ||
355 | SND_SOC_DAPM_OUTPUT("VOUT1R"), | ||
356 | SND_SOC_DAPM_OUTPUT("VOUT2L"), | ||
357 | SND_SOC_DAPM_OUTPUT("VOUT2R"), | ||
358 | SND_SOC_DAPM_OUTPUT("VOUT3L"), | ||
359 | SND_SOC_DAPM_OUTPUT("VOUT3R"), | ||
360 | |||
361 | SND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1), | ||
362 | |||
363 | SND_SOC_DAPM_INPUT("AINL"), | ||
364 | SND_SOC_DAPM_INPUT("AINR"), | ||
365 | }; | ||
366 | |||
367 | static const struct snd_soc_dapm_route audio_map[] = { | ||
368 | { "VOUT1L", NULL, "DAC1" }, | ||
369 | { "VOUT1R", NULL, "DAC1" }, | ||
370 | |||
371 | { "VOUT2L", NULL, "DAC2" }, | ||
372 | { "VOUT2R", NULL, "DAC2" }, | ||
373 | |||
374 | { "VOUT3L", NULL, "DAC3" }, | ||
375 | { "VOUT3R", NULL, "DAC3" }, | ||
376 | |||
377 | { "ADC", NULL, "AINL" }, | ||
378 | { "ADC", NULL, "AINR" }, | ||
379 | }; | ||
380 | |||
381 | static int wm8580_add_widgets(struct snd_soc_codec *codec) | ||
382 | { | ||
383 | snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets, | ||
384 | ARRAY_SIZE(wm8580_dapm_widgets)); | ||
385 | |||
386 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
387 | |||
388 | snd_soc_dapm_new_widgets(codec); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | /* PLL divisors */ | ||
393 | struct _pll_div { | ||
394 | u32 prescale:1; | ||
395 | u32 postscale:1; | ||
396 | u32 freqmode:2; | ||
397 | u32 n:4; | ||
398 | u32 k:24; | ||
399 | }; | ||
400 | |||
401 | /* The size in bits of the pll divide */ | ||
402 | #define FIXED_PLL_SIZE (1 << 22) | ||
403 | |||
404 | /* PLL rate to output rate divisions */ | ||
405 | static struct { | ||
406 | unsigned int div; | ||
407 | unsigned int freqmode; | ||
408 | unsigned int postscale; | ||
409 | } post_table[] = { | ||
410 | { 2, 0, 0 }, | ||
411 | { 4, 0, 1 }, | ||
412 | { 4, 1, 0 }, | ||
413 | { 8, 1, 1 }, | ||
414 | { 8, 2, 0 }, | ||
415 | { 16, 2, 1 }, | ||
416 | { 12, 3, 0 }, | ||
417 | { 24, 3, 1 } | ||
418 | }; | ||
419 | |||
420 | static int pll_factors(struct _pll_div *pll_div, unsigned int target, | ||
421 | unsigned int source) | ||
422 | { | ||
423 | u64 Kpart; | ||
424 | unsigned int K, Ndiv, Nmod; | ||
425 | int i; | ||
426 | |||
427 | pr_debug("wm8580: PLL %dHz->%dHz\n", source, target); | ||
428 | |||
429 | /* Scale the output frequency up; the PLL should run in the | ||
430 | * region of 90-100MHz. | ||
431 | */ | ||
432 | for (i = 0; i < ARRAY_SIZE(post_table); i++) { | ||
433 | if (target * post_table[i].div >= 90000000 && | ||
434 | target * post_table[i].div <= 100000000) { | ||
435 | pll_div->freqmode = post_table[i].freqmode; | ||
436 | pll_div->postscale = post_table[i].postscale; | ||
437 | target *= post_table[i].div; | ||
438 | break; | ||
439 | } | ||
440 | } | ||
441 | |||
442 | if (i == ARRAY_SIZE(post_table)) { | ||
443 | printk(KERN_ERR "wm8580: Unable to scale output frequency " | ||
444 | "%u\n", target); | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | Ndiv = target / source; | ||
449 | |||
450 | if (Ndiv < 5) { | ||
451 | source /= 2; | ||
452 | pll_div->prescale = 1; | ||
453 | Ndiv = target / source; | ||
454 | } else | ||
455 | pll_div->prescale = 0; | ||
456 | |||
457 | if ((Ndiv < 5) || (Ndiv > 13)) { | ||
458 | printk(KERN_ERR | ||
459 | "WM8580 N=%d outside supported range\n", Ndiv); | ||
460 | return -EINVAL; | ||
461 | } | ||
462 | |||
463 | pll_div->n = Ndiv; | ||
464 | Nmod = target % source; | ||
465 | Kpart = FIXED_PLL_SIZE * (long long)Nmod; | ||
466 | |||
467 | do_div(Kpart, source); | ||
468 | |||
469 | K = Kpart & 0xFFFFFFFF; | ||
470 | |||
471 | pll_div->k = K; | ||
472 | |||
473 | pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n", | ||
474 | pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode, | ||
475 | pll_div->postscale); | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, | ||
481 | int pll_id, unsigned int freq_in, unsigned int freq_out) | ||
482 | { | ||
483 | int offset; | ||
484 | struct snd_soc_codec *codec = codec_dai->codec; | ||
485 | struct wm8580_priv *wm8580 = codec->private_data; | ||
486 | struct pll_state *state; | ||
487 | struct _pll_div pll_div; | ||
488 | unsigned int reg; | ||
489 | unsigned int pwr_mask; | ||
490 | int ret; | ||
491 | |||
492 | /* GCC isn't able to work out the ifs below for initialising/using | ||
493 | * pll_div so suppress warnings. | ||
494 | */ | ||
495 | memset(&pll_div, 0, sizeof(pll_div)); | ||
496 | |||
497 | switch (pll_id) { | ||
498 | case WM8580_PLLA: | ||
499 | state = &wm8580->a; | ||
500 | offset = 0; | ||
501 | pwr_mask = WM8580_PWRDN2_PLLAPD; | ||
502 | break; | ||
503 | case WM8580_PLLB: | ||
504 | state = &wm8580->b; | ||
505 | offset = 4; | ||
506 | pwr_mask = WM8580_PWRDN2_PLLBPD; | ||
507 | break; | ||
508 | default: | ||
509 | return -ENODEV; | ||
510 | } | ||
511 | |||
512 | if (freq_in && freq_out) { | ||
513 | ret = pll_factors(&pll_div, freq_out, freq_in); | ||
514 | if (ret != 0) | ||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | state->in = freq_in; | ||
519 | state->out = freq_out; | ||
520 | |||
521 | /* Always disable the PLL - it is not safe to leave it running | ||
522 | * while reprogramming it. | ||
523 | */ | ||
524 | reg = wm8580_read(codec, WM8580_PWRDN2); | ||
525 | wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask); | ||
526 | |||
527 | if (!freq_in || !freq_out) | ||
528 | return 0; | ||
529 | |||
530 | wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff); | ||
531 | wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff); | ||
532 | wm8580_write(codec, WM8580_PLLA3 + offset, | ||
533 | (pll_div.k >> 18 & 0xf) | (pll_div.n << 4)); | ||
534 | |||
535 | reg = wm8580_read(codec, WM8580_PLLA4 + offset); | ||
536 | reg &= ~0x3f; | ||
537 | reg |= pll_div.prescale | pll_div.postscale << 1 | | ||
538 | pll_div.freqmode << 4; | ||
539 | |||
540 | wm8580_write(codec, WM8580_PLLA4 + offset, reg); | ||
541 | |||
542 | /* All done, turn it on */ | ||
543 | reg = wm8580_read(codec, WM8580_PWRDN2); | ||
544 | wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask); | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | /* | ||
550 | * Set PCM DAI bit size and sample rate. | ||
551 | */ | ||
552 | static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, | ||
553 | struct snd_pcm_hw_params *params) | ||
554 | { | ||
555 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
556 | struct snd_soc_dai_link *dai = rtd->dai; | ||
557 | struct snd_soc_device *socdev = rtd->socdev; | ||
558 | struct snd_soc_codec *codec = socdev->codec; | ||
559 | u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id); | ||
560 | |||
561 | paifb &= ~WM8580_AIF_LENGTH_MASK; | ||
562 | /* bit size */ | ||
563 | switch (params_format(params)) { | ||
564 | case SNDRV_PCM_FORMAT_S16_LE: | ||
565 | break; | ||
566 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
567 | paifb |= WM8580_AIF_LENGTH_20; | ||
568 | break; | ||
569 | case SNDRV_PCM_FORMAT_S24_LE: | ||
570 | paifb |= WM8580_AIF_LENGTH_24; | ||
571 | break; | ||
572 | case SNDRV_PCM_FORMAT_S32_LE: | ||
573 | paifb |= WM8580_AIF_LENGTH_24; | ||
574 | break; | ||
575 | default: | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | |||
579 | wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb); | ||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, | ||
584 | unsigned int fmt) | ||
585 | { | ||
586 | struct snd_soc_codec *codec = codec_dai->codec; | ||
587 | unsigned int aifa; | ||
588 | unsigned int aifb; | ||
589 | int can_invert_lrclk; | ||
590 | |||
591 | aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id); | ||
592 | aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id); | ||
593 | |||
594 | aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); | ||
595 | |||
596 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
597 | case SND_SOC_DAIFMT_CBS_CFS: | ||
598 | aifa &= ~WM8580_AIF_MS; | ||
599 | break; | ||
600 | case SND_SOC_DAIFMT_CBM_CFM: | ||
601 | aifa |= WM8580_AIF_MS; | ||
602 | break; | ||
603 | default: | ||
604 | return -EINVAL; | ||
605 | } | ||
606 | |||
607 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
608 | case SND_SOC_DAIFMT_I2S: | ||
609 | can_invert_lrclk = 1; | ||
610 | aifb |= WM8580_AIF_FMT_I2S; | ||
611 | break; | ||
612 | case SND_SOC_DAIFMT_RIGHT_J: | ||
613 | can_invert_lrclk = 1; | ||
614 | aifb |= WM8580_AIF_FMT_RIGHTJ; | ||
615 | break; | ||
616 | case SND_SOC_DAIFMT_LEFT_J: | ||
617 | can_invert_lrclk = 1; | ||
618 | aifb |= WM8580_AIF_FMT_LEFTJ; | ||
619 | break; | ||
620 | case SND_SOC_DAIFMT_DSP_A: | ||
621 | can_invert_lrclk = 0; | ||
622 | aifb |= WM8580_AIF_FMT_DSP; | ||
623 | break; | ||
624 | case SND_SOC_DAIFMT_DSP_B: | ||
625 | can_invert_lrclk = 0; | ||
626 | aifb |= WM8580_AIF_FMT_DSP; | ||
627 | aifb |= WM8580_AIF_LRP; | ||
628 | break; | ||
629 | default: | ||
630 | return -EINVAL; | ||
631 | } | ||
632 | |||
633 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
634 | case SND_SOC_DAIFMT_NB_NF: | ||
635 | break; | ||
636 | |||
637 | case SND_SOC_DAIFMT_IB_IF: | ||
638 | if (!can_invert_lrclk) | ||
639 | return -EINVAL; | ||
640 | aifb |= WM8580_AIF_BCP; | ||
641 | aifb |= WM8580_AIF_LRP; | ||
642 | break; | ||
643 | |||
644 | case SND_SOC_DAIFMT_IB_NF: | ||
645 | aifb |= WM8580_AIF_BCP; | ||
646 | break; | ||
647 | |||
648 | case SND_SOC_DAIFMT_NB_IF: | ||
649 | if (!can_invert_lrclk) | ||
650 | return -EINVAL; | ||
651 | aifb |= WM8580_AIF_LRP; | ||
652 | break; | ||
653 | |||
654 | default: | ||
655 | return -EINVAL; | ||
656 | } | ||
657 | |||
658 | wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa); | ||
659 | wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb); | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, | ||
665 | int div_id, int div) | ||
666 | { | ||
667 | struct snd_soc_codec *codec = codec_dai->codec; | ||
668 | unsigned int reg; | ||
669 | |||
670 | switch (div_id) { | ||
671 | case WM8580_MCLK: | ||
672 | reg = wm8580_read(codec, WM8580_PLLB4); | ||
673 | reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK; | ||
674 | |||
675 | switch (div) { | ||
676 | case WM8580_CLKSRC_MCLK: | ||
677 | /* Input */ | ||
678 | break; | ||
679 | |||
680 | case WM8580_CLKSRC_PLLA: | ||
681 | reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA; | ||
682 | break; | ||
683 | case WM8580_CLKSRC_PLLB: | ||
684 | reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB; | ||
685 | break; | ||
686 | |||
687 | case WM8580_CLKSRC_OSC: | ||
688 | reg |= WM8580_PLLB4_MCLKOUTSRC_OSC; | ||
689 | break; | ||
690 | |||
691 | default: | ||
692 | return -EINVAL; | ||
693 | } | ||
694 | wm8580_write(codec, WM8580_PLLB4, reg); | ||
695 | break; | ||
696 | |||
697 | case WM8580_DAC_CLKSEL: | ||
698 | reg = wm8580_read(codec, WM8580_CLKSEL); | ||
699 | reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK; | ||
700 | |||
701 | switch (div) { | ||
702 | case WM8580_CLKSRC_MCLK: | ||
703 | break; | ||
704 | |||
705 | case WM8580_CLKSRC_PLLA: | ||
706 | reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA; | ||
707 | break; | ||
708 | |||
709 | case WM8580_CLKSRC_PLLB: | ||
710 | reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB; | ||
711 | break; | ||
712 | |||
713 | default: | ||
714 | return -EINVAL; | ||
715 | } | ||
716 | wm8580_write(codec, WM8580_CLKSEL, reg); | ||
717 | break; | ||
718 | |||
719 | case WM8580_CLKOUTSRC: | ||
720 | reg = wm8580_read(codec, WM8580_PLLB4); | ||
721 | reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; | ||
722 | |||
723 | switch (div) { | ||
724 | case WM8580_CLKSRC_NONE: | ||
725 | break; | ||
726 | |||
727 | case WM8580_CLKSRC_PLLA: | ||
728 | reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK; | ||
729 | break; | ||
730 | |||
731 | case WM8580_CLKSRC_PLLB: | ||
732 | reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK; | ||
733 | break; | ||
734 | |||
735 | case WM8580_CLKSRC_OSC: | ||
736 | reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK; | ||
737 | break; | ||
738 | |||
739 | default: | ||
740 | return -EINVAL; | ||
741 | } | ||
742 | wm8580_write(codec, WM8580_PLLB4, reg); | ||
743 | break; | ||
744 | |||
745 | default: | ||
746 | return -EINVAL; | ||
747 | } | ||
748 | |||
749 | return 0; | ||
750 | } | ||
751 | |||
752 | static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) | ||
753 | { | ||
754 | struct snd_soc_codec *codec = codec_dai->codec; | ||
755 | unsigned int reg; | ||
756 | |||
757 | reg = wm8580_read(codec, WM8580_DAC_CONTROL5); | ||
758 | |||
759 | if (mute) | ||
760 | reg |= WM8580_DAC_CONTROL5_MUTEALL; | ||
761 | else | ||
762 | reg &= ~WM8580_DAC_CONTROL5_MUTEALL; | ||
763 | |||
764 | wm8580_write(codec, WM8580_DAC_CONTROL5, reg); | ||
765 | |||
766 | return 0; | ||
767 | } | ||
768 | |||
769 | static int wm8580_set_bias_level(struct snd_soc_codec *codec, | ||
770 | enum snd_soc_bias_level level) | ||
771 | { | ||
772 | u16 reg; | ||
773 | switch (level) { | ||
774 | case SND_SOC_BIAS_ON: | ||
775 | case SND_SOC_BIAS_PREPARE: | ||
776 | case SND_SOC_BIAS_STANDBY: | ||
777 | break; | ||
778 | case SND_SOC_BIAS_OFF: | ||
779 | reg = wm8580_read(codec, WM8580_PWRDN1); | ||
780 | wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN); | ||
781 | break; | ||
782 | } | ||
783 | codec->bias_level = level; | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | #define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
788 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
789 | |||
790 | struct snd_soc_dai wm8580_dai[] = { | ||
791 | { | ||
792 | .name = "WM8580 PAIFRX", | ||
793 | .id = 0, | ||
794 | .playback = { | ||
795 | .stream_name = "Playback", | ||
796 | .channels_min = 1, | ||
797 | .channels_max = 6, | ||
798 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
799 | .formats = WM8580_FORMATS, | ||
800 | }, | ||
801 | .ops = { | ||
802 | .hw_params = wm8580_paif_hw_params, | ||
803 | }, | ||
804 | .dai_ops = { | ||
805 | .set_fmt = wm8580_set_paif_dai_fmt, | ||
806 | .set_clkdiv = wm8580_set_dai_clkdiv, | ||
807 | .set_pll = wm8580_set_dai_pll, | ||
808 | .digital_mute = wm8580_digital_mute, | ||
809 | }, | ||
810 | }, | ||
811 | { | ||
812 | .name = "WM8580 PAIFTX", | ||
813 | .id = 1, | ||
814 | .capture = { | ||
815 | .stream_name = "Capture", | ||
816 | .channels_min = 2, | ||
817 | .channels_max = 2, | ||
818 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
819 | .formats = WM8580_FORMATS, | ||
820 | }, | ||
821 | .ops = { | ||
822 | .hw_params = wm8580_paif_hw_params, | ||
823 | }, | ||
824 | .dai_ops = { | ||
825 | .set_fmt = wm8580_set_paif_dai_fmt, | ||
826 | .set_clkdiv = wm8580_set_dai_clkdiv, | ||
827 | .set_pll = wm8580_set_dai_pll, | ||
828 | }, | ||
829 | }, | ||
830 | }; | ||
831 | EXPORT_SYMBOL_GPL(wm8580_dai); | ||
832 | |||
833 | /* | ||
834 | * initialise the WM8580 driver | ||
835 | * register the mixer and dsp interfaces with the kernel | ||
836 | */ | ||
837 | static int wm8580_init(struct snd_soc_device *socdev) | ||
838 | { | ||
839 | struct snd_soc_codec *codec = socdev->codec; | ||
840 | int ret = 0; | ||
841 | |||
842 | codec->name = "WM8580"; | ||
843 | codec->owner = THIS_MODULE; | ||
844 | codec->read = wm8580_read_reg_cache; | ||
845 | codec->write = wm8580_write; | ||
846 | codec->set_bias_level = wm8580_set_bias_level; | ||
847 | codec->dai = wm8580_dai; | ||
848 | codec->num_dai = ARRAY_SIZE(wm8580_dai); | ||
849 | codec->reg_cache_size = ARRAY_SIZE(wm8580_reg); | ||
850 | codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg), | ||
851 | GFP_KERNEL); | ||
852 | |||
853 | if (codec->reg_cache == NULL) | ||
854 | return -ENOMEM; | ||
855 | |||
856 | /* Get the codec into a known state */ | ||
857 | wm8580_write(codec, WM8580_RESET, 0); | ||
858 | |||
859 | /* Power up and get individual control of the DACs */ | ||
860 | wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) & | ||
861 | ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD)); | ||
862 | |||
863 | /* Make VMID high impedence */ | ||
864 | wm8580_write(codec, WM8580_ADC_CONTROL1, | ||
865 | wm8580_read(codec, WM8580_ADC_CONTROL1) & ~0x100); | ||
866 | |||
867 | /* register pcms */ | ||
868 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, | ||
869 | SNDRV_DEFAULT_STR1); | ||
870 | if (ret < 0) { | ||
871 | printk(KERN_ERR "wm8580: failed to create pcms\n"); | ||
872 | goto pcm_err; | ||
873 | } | ||
874 | |||
875 | wm8580_add_controls(codec); | ||
876 | wm8580_add_widgets(codec); | ||
877 | |||
878 | ret = snd_soc_register_card(socdev); | ||
879 | if (ret < 0) { | ||
880 | printk(KERN_ERR "wm8580: failed to register card\n"); | ||
881 | goto card_err; | ||
882 | } | ||
883 | return ret; | ||
884 | |||
885 | card_err: | ||
886 | snd_soc_free_pcms(socdev); | ||
887 | snd_soc_dapm_free(socdev); | ||
888 | pcm_err: | ||
889 | kfree(codec->reg_cache); | ||
890 | return ret; | ||
891 | } | ||
892 | |||
893 | /* If the i2c layer weren't so broken, we could pass this kind of data | ||
894 | around */ | ||
895 | static struct snd_soc_device *wm8580_socdev; | ||
896 | |||
897 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
898 | |||
899 | /* | ||
900 | * WM8580 2 wire address is determined by GPIO5 | ||
901 | * state during powerup. | ||
902 | * low = 0x1a | ||
903 | * high = 0x1b | ||
904 | */ | ||
905 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
906 | |||
907 | /* Magic definition of all other variables and things */ | ||
908 | I2C_CLIENT_INSMOD; | ||
909 | |||
910 | static struct i2c_driver wm8580_i2c_driver; | ||
911 | static struct i2c_client client_template; | ||
912 | |||
913 | static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
914 | { | ||
915 | struct snd_soc_device *socdev = wm8580_socdev; | ||
916 | struct wm8580_setup_data *setup = socdev->codec_data; | ||
917 | struct snd_soc_codec *codec = socdev->codec; | ||
918 | struct i2c_client *i2c; | ||
919 | int ret; | ||
920 | |||
921 | if (addr != setup->i2c_address) | ||
922 | return -ENODEV; | ||
923 | |||
924 | client_template.adapter = adap; | ||
925 | client_template.addr = addr; | ||
926 | |||
927 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
928 | if (i2c == NULL) { | ||
929 | kfree(codec); | ||
930 | return -ENOMEM; | ||
931 | } | ||
932 | i2c_set_clientdata(i2c, codec); | ||
933 | codec->control_data = i2c; | ||
934 | |||
935 | ret = i2c_attach_client(i2c); | ||
936 | if (ret < 0) { | ||
937 | dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr); | ||
938 | goto err; | ||
939 | } | ||
940 | |||
941 | ret = wm8580_init(socdev); | ||
942 | if (ret < 0) { | ||
943 | dev_err(&i2c->dev, "failed to initialise WM8580\n"); | ||
944 | goto err; | ||
945 | } | ||
946 | |||
947 | return ret; | ||
948 | |||
949 | err: | ||
950 | kfree(codec); | ||
951 | kfree(i2c); | ||
952 | return ret; | ||
953 | } | ||
954 | |||
955 | static int wm8580_i2c_detach(struct i2c_client *client) | ||
956 | { | ||
957 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
958 | i2c_detach_client(client); | ||
959 | kfree(codec->reg_cache); | ||
960 | kfree(client); | ||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | static int wm8580_i2c_attach(struct i2c_adapter *adap) | ||
965 | { | ||
966 | return i2c_probe(adap, &addr_data, wm8580_codec_probe); | ||
967 | } | ||
968 | |||
969 | /* corgi i2c codec control layer */ | ||
970 | static struct i2c_driver wm8580_i2c_driver = { | ||
971 | .driver = { | ||
972 | .name = "WM8580 I2C Codec", | ||
973 | .owner = THIS_MODULE, | ||
974 | }, | ||
975 | .attach_adapter = wm8580_i2c_attach, | ||
976 | .detach_client = wm8580_i2c_detach, | ||
977 | .command = NULL, | ||
978 | }; | ||
979 | |||
980 | static struct i2c_client client_template = { | ||
981 | .name = "WM8580", | ||
982 | .driver = &wm8580_i2c_driver, | ||
983 | }; | ||
984 | #endif | ||
985 | |||
986 | static int wm8580_probe(struct platform_device *pdev) | ||
987 | { | ||
988 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
989 | struct wm8580_setup_data *setup; | ||
990 | struct snd_soc_codec *codec; | ||
991 | struct wm8580_priv *wm8580; | ||
992 | int ret = 0; | ||
993 | |||
994 | pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION); | ||
995 | |||
996 | setup = socdev->codec_data; | ||
997 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
998 | if (codec == NULL) | ||
999 | return -ENOMEM; | ||
1000 | |||
1001 | wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL); | ||
1002 | if (wm8580 == NULL) { | ||
1003 | kfree(codec); | ||
1004 | return -ENOMEM; | ||
1005 | } | ||
1006 | |||
1007 | codec->private_data = wm8580; | ||
1008 | socdev->codec = codec; | ||
1009 | mutex_init(&codec->mutex); | ||
1010 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
1011 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
1012 | wm8580_socdev = socdev; | ||
1013 | |||
1014 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1015 | if (setup->i2c_address) { | ||
1016 | normal_i2c[0] = setup->i2c_address; | ||
1017 | codec->hw_write = (hw_write_t)i2c_master_send; | ||
1018 | ret = i2c_add_driver(&wm8580_i2c_driver); | ||
1019 | if (ret != 0) | ||
1020 | printk(KERN_ERR "can't add i2c driver"); | ||
1021 | } | ||
1022 | #else | ||
1023 | /* Add other interfaces here */ | ||
1024 | #endif | ||
1025 | return ret; | ||
1026 | } | ||
1027 | |||
1028 | /* power down chip */ | ||
1029 | static int wm8580_remove(struct platform_device *pdev) | ||
1030 | { | ||
1031 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1032 | struct snd_soc_codec *codec = socdev->codec; | ||
1033 | |||
1034 | if (codec->control_data) | ||
1035 | wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1036 | snd_soc_free_pcms(socdev); | ||
1037 | snd_soc_dapm_free(socdev); | ||
1038 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1039 | i2c_del_driver(&wm8580_i2c_driver); | ||
1040 | #endif | ||
1041 | kfree(codec->private_data); | ||
1042 | kfree(codec); | ||
1043 | |||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
1047 | struct snd_soc_codec_device soc_codec_dev_wm8580 = { | ||
1048 | .probe = wm8580_probe, | ||
1049 | .remove = wm8580_remove, | ||
1050 | }; | ||
1051 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580); | ||
1052 | |||
1053 | MODULE_DESCRIPTION("ASoC WM8580 driver"); | ||
1054 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
1055 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h new file mode 100644 index 000000000000..589ddaba21d7 --- /dev/null +++ b/sound/soc/codecs/wm8580.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * wm8580.h -- audio driver for WM8580 | ||
3 | * | ||
4 | * Copyright 2008 Samsung Electronics. | ||
5 | * Author: Ryu Euiyoul | ||
6 | * ryu.real@gmail.com | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef _WM8580_H | ||
16 | #define _WM8580_H | ||
17 | |||
18 | #define WM8580_PLLA 1 | ||
19 | #define WM8580_PLLB 2 | ||
20 | |||
21 | #define WM8580_MCLK 1 | ||
22 | #define WM8580_DAC_CLKSEL 2 | ||
23 | #define WM8580_CLKOUTSRC 3 | ||
24 | |||
25 | #define WM8580_CLKSRC_MCLK 1 | ||
26 | #define WM8580_CLKSRC_PLLA 2 | ||
27 | #define WM8580_CLKSRC_PLLB 3 | ||
28 | #define WM8580_CLKSRC_OSC 4 | ||
29 | #define WM8580_CLKSRC_NONE 5 | ||
30 | |||
31 | struct wm8580_setup_data { | ||
32 | unsigned short i2c_address; | ||
33 | }; | ||
34 | |||
35 | #define WM8580_DAI_PAIFRX 0 | ||
36 | #define WM8580_DAI_PAIFTX 1 | ||
37 | |||
38 | extern struct snd_soc_dai wm8580_dai[]; | ||
39 | extern struct snd_soc_codec_device soc_codec_dev_wm8580; | ||
40 | |||
41 | #endif | ||
42 | |||