aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-04-07 13:45:21 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-04-07 13:51:23 -0400
commit5409fb4e327a84972483047ecf4fb41f279453e2 (patch)
treebac89150871748da0277cea6d8ae8083b6e0a79f /sound/soc/codecs
parent06f409d76f1d382167eb1cadde2e23a73272865d (diff)
ASoC: Add WM8988 CODEC driver
The WM8988 is a low power, high quality stereo CODEC designed for portable digital audio applications. The device integrates complete interfaces to 2 stereo headphone or line out ports. External component requirements are drastically reduced as no separate headphone amplifiers are required. Advanced on-chip digital signal processing performs graphic equaliser, 3-D sound enhancement and automatic level control for the microphone or line input. The WM8988 can operate as a master or a slave, with various master clock frequencies including 12 or 24MHz for USB devices, or standard 256fs rates like 12.288MHz and 24.576MHz. Different audio sample rates such as 96kHz, 48kHz, 44.1kHz are generated directly from the master clock without the need for an external PLL. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/wm8988.c1097
-rw-r--r--sound/soc/codecs/wm8988.h60
4 files changed, 1163 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b6c7f7a01cb0..ab364854675b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
36 select SND_SOC_WM8900 if I2C 36 select SND_SOC_WM8900 if I2C
37 select SND_SOC_WM8903 if I2C 37 select SND_SOC_WM8903 if I2C
38 select SND_SOC_WM8971 if I2C 38 select SND_SOC_WM8971 if I2C
39 select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
39 select SND_SOC_WM8990 if I2C 40 select SND_SOC_WM8990 if I2C
40 select SND_SOC_WM9705 if SND_SOC_AC97_BUS 41 select SND_SOC_WM9705 if SND_SOC_AC97_BUS
41 select SND_SOC_WM9712 if SND_SOC_AC97_BUS 42 select SND_SOC_WM9712 if SND_SOC_AC97_BUS
@@ -141,6 +142,9 @@ config SND_SOC_WM8903
141config SND_SOC_WM8971 142config SND_SOC_WM8971
142 tristate 143 tristate
143 144
145config SND_SOC_WM8988
146 tristate
147
144config SND_SOC_WM8990 148config SND_SOC_WM8990
145 tristate 149 tristate
146 150
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 030d2454725f..a72548dc1885 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -24,6 +24,7 @@ snd-soc-wm8753-objs := wm8753.o
24snd-soc-wm8900-objs := wm8900.o 24snd-soc-wm8900-objs := wm8900.o
25snd-soc-wm8903-objs := wm8903.o 25snd-soc-wm8903-objs := wm8903.o
26snd-soc-wm8971-objs := wm8971.o 26snd-soc-wm8971-objs := wm8971.o
27snd-soc-wm8988-objs := wm8988.o
27snd-soc-wm8990-objs := wm8990.o 28snd-soc-wm8990-objs := wm8990.o
28snd-soc-wm9705-objs := wm9705.o 29snd-soc-wm9705-objs := wm9705.o
29snd-soc-wm9712-objs := wm9712.o 30snd-soc-wm9712-objs := wm9712.o
@@ -55,6 +56,7 @@ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
55obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o 56obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
56obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o 57obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
57obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o 58obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
59obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o
58obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o 60obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
59obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o 61obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
60obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o 62obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
new file mode 100644
index 000000000000..c05f71803aa8
--- /dev/null
+++ b/sound/soc/codecs/wm8988.c
@@ -0,0 +1,1097 @@
1/*
2 * wm8988.c -- WM8988 ALSA SoC audio driver
3 *
4 * Copyright 2009 Wolfson Microelectronics plc
5 * Copyright 2005 Openedhand Ltd.
6 *
7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/pm.h>
19#include <linux/i2c.h>
20#include <linux/spi/spi.h>
21#include <linux/platform_device.h>
22#include <sound/core.h>
23#include <sound/pcm.h>
24#include <sound/pcm_params.h>
25#include <sound/tlv.h>
26#include <sound/soc.h>
27#include <sound/soc-dapm.h>
28#include <sound/initval.h>
29
30#include "wm8988.h"
31
32/*
33 * wm8988 register cache
34 * We can't read the WM8988 register space when we
35 * are using 2 wire for device control, so we cache them instead.
36 */
37static const u16 wm8988_reg[] = {
38 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
39 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
40 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
41 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
42 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
43 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
44 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
45 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
46 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
47 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
48 0x0079, 0x0079, 0x0079, /* 40 */
49};
50
51/* codec private data */
52struct wm8988_priv {
53 unsigned int sysclk;
54 struct snd_soc_codec codec;
55 struct snd_pcm_hw_constraint_list *sysclk_constraints;
56 u16 reg_cache[WM8988_NUM_REG];
57};
58
59
60/*
61 * read wm8988 register cache
62 */
63static inline unsigned int wm8988_read_reg_cache(struct snd_soc_codec *codec,
64 unsigned int reg)
65{
66 u16 *cache = codec->reg_cache;
67 if (reg > WM8988_NUM_REG)
68 return -1;
69 return cache[reg];
70}
71
72/*
73 * write wm8988 register cache
74 */
75static inline void wm8988_write_reg_cache(struct snd_soc_codec *codec,
76 unsigned int reg, unsigned int value)
77{
78 u16 *cache = codec->reg_cache;
79 if (reg > WM8988_NUM_REG)
80 return;
81 cache[reg] = value;
82}
83
84static int wm8988_write(struct snd_soc_codec *codec, unsigned int reg,
85 unsigned int value)
86{
87 u8 data[2];
88
89 /* data is
90 * D15..D9 WM8753 register offset
91 * D8...D0 register data
92 */
93 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
94 data[1] = value & 0x00ff;
95
96 wm8988_write_reg_cache(codec, reg, value);
97 if (codec->hw_write(codec->control_data, data, 2) == 2)
98 return 0;
99 else
100 return -EIO;
101}
102
103#define wm8988_reset(c) wm8988_write(c, WM8988_RESET, 0)
104
105/*
106 * WM8988 Controls
107 */
108
109static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"};
110static const struct soc_enum bass_boost =
111 SOC_ENUM_SINGLE(WM8988_BASS, 7, 2, bass_boost_txt);
112
113static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
114static const struct soc_enum bass_filter =
115 SOC_ENUM_SINGLE(WM8988_BASS, 6, 2, bass_filter_txt);
116
117static const char *treble_txt[] = {"8kHz", "4kHz"};
118static const struct soc_enum treble =
119 SOC_ENUM_SINGLE(WM8988_TREBLE, 6, 2, treble_txt);
120
121static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"};
122static const struct soc_enum stereo_3d_lc =
123 SOC_ENUM_SINGLE(WM8988_3D, 5, 2, stereo_3d_lc_txt);
124
125static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"};
126static const struct soc_enum stereo_3d_uc =
127 SOC_ENUM_SINGLE(WM8988_3D, 6, 2, stereo_3d_uc_txt);
128
129static const char *stereo_3d_func_txt[] = {"Capture", "Playback"};
130static const struct soc_enum stereo_3d_func =
131 SOC_ENUM_SINGLE(WM8988_3D, 7, 2, stereo_3d_func_txt);
132
133static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"};
134static const struct soc_enum alc_func =
135 SOC_ENUM_SINGLE(WM8988_ALC1, 7, 4, alc_func_txt);
136
137static const char *ng_type_txt[] = {"Constant PGA Gain",
138 "Mute ADC Output"};
139static const struct soc_enum ng_type =
140 SOC_ENUM_SINGLE(WM8988_NGATE, 1, 2, ng_type_txt);
141
142static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"};
143static const struct soc_enum deemph =
144 SOC_ENUM_SINGLE(WM8988_ADCDAC, 1, 4, deemph_txt);
145
146static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert",
147 "L + R Invert"};
148static const struct soc_enum adcpol =
149 SOC_ENUM_SINGLE(WM8988_ADCDAC, 5, 4, adcpol_txt);
150
151static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
152static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
153static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
154static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
155static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
156
157static const struct snd_kcontrol_new wm8988_snd_controls[] = {
158
159SOC_ENUM("Bass Boost", bass_boost),
160SOC_ENUM("Bass Filter", bass_filter),
161SOC_SINGLE("Bass Volume", WM8988_BASS, 0, 15, 1),
162
163SOC_SINGLE("Treble Volume", WM8988_TREBLE, 0, 15, 0),
164SOC_ENUM("Treble Cut-off", treble),
165
166SOC_SINGLE("3D Switch", WM8988_3D, 0, 1, 0),
167SOC_SINGLE("3D Volume", WM8988_3D, 1, 15, 0),
168SOC_ENUM("3D Lower Cut-off", stereo_3d_lc),
169SOC_ENUM("3D Upper Cut-off", stereo_3d_uc),
170SOC_ENUM("3D Mode", stereo_3d_func),
171
172SOC_SINGLE("ALC Capture Target Volume", WM8988_ALC1, 0, 7, 0),
173SOC_SINGLE("ALC Capture Max Volume", WM8988_ALC1, 4, 7, 0),
174SOC_ENUM("ALC Capture Function", alc_func),
175SOC_SINGLE("ALC Capture ZC Switch", WM8988_ALC2, 7, 1, 0),
176SOC_SINGLE("ALC Capture Hold Time", WM8988_ALC2, 0, 15, 0),
177SOC_SINGLE("ALC Capture Decay Time", WM8988_ALC3, 4, 15, 0),
178SOC_SINGLE("ALC Capture Attack Time", WM8988_ALC3, 0, 15, 0),
179SOC_SINGLE("ALC Capture NG Threshold", WM8988_NGATE, 3, 31, 0),
180SOC_ENUM("ALC Capture NG Type", ng_type),
181SOC_SINGLE("ALC Capture NG Switch", WM8988_NGATE, 0, 1, 0),
182
183SOC_SINGLE("ZC Timeout Switch", WM8988_ADCTL1, 0, 1, 0),
184
185SOC_DOUBLE_R_TLV("Capture Digital Volume", WM8988_LADC, WM8988_RADC,
186 0, 255, 0, adc_tlv),
187SOC_DOUBLE_R_TLV("Capture Volume", WM8988_LINVOL, WM8988_RINVOL,
188 0, 63, 0, pga_tlv),
189SOC_DOUBLE_R("Capture ZC Switch", WM8988_LINVOL, WM8988_RINVOL, 6, 1, 0),
190SOC_DOUBLE_R("Capture Switch", WM8988_LINVOL, WM8988_RINVOL, 7, 1, 1),
191
192SOC_ENUM("Playback De-emphasis", deemph),
193
194SOC_ENUM("Capture Polarity", adcpol),
195SOC_SINGLE("Playback 6dB Attenuate", WM8988_ADCDAC, 7, 1, 0),
196SOC_SINGLE("Capture 6dB Attenuate", WM8988_ADCDAC, 8, 1, 0),
197
198SOC_DOUBLE_R_TLV("PCM Volume", WM8988_LDAC, WM8988_RDAC, 0, 255, 0, dac_tlv),
199
200SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", WM8988_LOUTM1, 4, 7, 1,
201 bypass_tlv),
202SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", WM8988_LOUTM2, 4, 7, 1,
203 bypass_tlv),
204SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", WM8988_ROUTM1, 4, 7, 1,
205 bypass_tlv),
206SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", WM8988_ROUTM2, 4, 7, 1,
207 bypass_tlv),
208
209SOC_DOUBLE_R("Output 1 Playback ZC Switch", WM8988_LOUT1V,
210 WM8988_ROUT1V, 7, 1, 0),
211SOC_DOUBLE_R_TLV("Output 1 Playback Volume", WM8988_LOUT1V, WM8988_ROUT1V,
212 0, 127, 0, out_tlv),
213
214SOC_DOUBLE_R("Output 2 Playback ZC Switch", WM8988_LOUT2V,
215 WM8988_ROUT2V, 7, 1, 0),
216SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V,
217 0, 127, 0, out_tlv),
218
219};
220
221/*
222 * DAPM Controls
223 */
224
225static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
226 struct snd_kcontrol *kcontrol, int event)
227{
228 struct snd_soc_codec *codec = w->codec;
229 u16 adctl2 = wm8988_read_reg_cache(codec, WM8988_ADCTL2);
230
231 /* Use the DAC to gate LRC if active, otherwise use ADC */
232 if (wm8988_read_reg_cache(codec, WM8988_PWR2) & 0x180)
233 adctl2 &= ~0x4;
234 else
235 adctl2 |= 0x4;
236
237 return wm8988_write(codec, WM8988_ADCTL2, adctl2);
238}
239
240static const char *wm8988_line_texts[] = {
241 "Line 1", "Line 2", "PGA", "Differential"};
242
243static const unsigned int wm8988_line_values[] = {
244 0, 1, 3, 4};
245
246static const struct soc_enum wm8988_lline_enum =
247 SOC_VALUE_ENUM_SINGLE(WM8988_LOUTM1, 0, 7,
248 ARRAY_SIZE(wm8988_line_texts),
249 wm8988_line_texts,
250 wm8988_line_values);
251static const struct snd_kcontrol_new wm8988_left_line_controls =
252 SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
253
254static const struct soc_enum wm8988_rline_enum =
255 SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7,
256 ARRAY_SIZE(wm8988_line_texts),
257 wm8988_line_texts,
258 wm8988_line_values);
259static const struct snd_kcontrol_new wm8988_right_line_controls =
260 SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
261
262/* Left Mixer */
263static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = {
264 SOC_DAPM_SINGLE("Playback Switch", WM8988_LOUTM1, 8, 1, 0),
265 SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_LOUTM1, 7, 1, 0),
266 SOC_DAPM_SINGLE("Right Playback Switch", WM8988_LOUTM2, 8, 1, 0),
267 SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_LOUTM2, 7, 1, 0),
268};
269
270/* Right Mixer */
271static const struct snd_kcontrol_new wm8988_right_mixer_controls[] = {
272 SOC_DAPM_SINGLE("Left Playback Switch", WM8988_ROUTM1, 8, 1, 0),
273 SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_ROUTM1, 7, 1, 0),
274 SOC_DAPM_SINGLE("Playback Switch", WM8988_ROUTM2, 8, 1, 0),
275 SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_ROUTM2, 7, 1, 0),
276};
277
278static const char *wm8988_pga_sel[] = {"Line 1", "Line 2", "Differential"};
279static const unsigned int wm8988_pga_val[] = { 0, 1, 3 };
280
281/* Left PGA Mux */
282static const struct soc_enum wm8988_lpga_enum =
283 SOC_VALUE_ENUM_SINGLE(WM8988_LADCIN, 6, 3,
284 ARRAY_SIZE(wm8988_pga_sel),
285 wm8988_pga_sel,
286 wm8988_pga_val);
287static const struct snd_kcontrol_new wm8988_left_pga_controls =
288 SOC_DAPM_VALUE_ENUM("Route", wm8988_lpga_enum);
289
290/* Right PGA Mux */
291static const struct soc_enum wm8988_rpga_enum =
292 SOC_VALUE_ENUM_SINGLE(WM8988_RADCIN, 6, 3,
293 ARRAY_SIZE(wm8988_pga_sel),
294 wm8988_pga_sel,
295 wm8988_pga_val);
296static const struct snd_kcontrol_new wm8988_right_pga_controls =
297 SOC_DAPM_VALUE_ENUM("Route", wm8988_rpga_enum);
298
299/* Differential Mux */
300static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"};
301static const struct soc_enum diffmux =
302 SOC_ENUM_SINGLE(WM8988_ADCIN, 8, 2, wm8988_diff_sel);
303static const struct snd_kcontrol_new wm8988_diffmux_controls =
304 SOC_DAPM_ENUM("Route", diffmux);
305
306/* Mono ADC Mux */
307static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)",
308 "Mono (Right)", "Digital Mono"};
309static const struct soc_enum monomux =
310 SOC_ENUM_SINGLE(WM8988_ADCIN, 6, 4, wm8988_mono_mux);
311static const struct snd_kcontrol_new wm8988_monomux_controls =
312 SOC_DAPM_ENUM("Route", monomux);
313
314static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = {
315 SND_SOC_DAPM_MICBIAS("Mic Bias", WM8988_PWR1, 1, 0),
316
317 SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
318 &wm8988_diffmux_controls),
319 SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
320 &wm8988_monomux_controls),
321 SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
322 &wm8988_monomux_controls),
323
324 SND_SOC_DAPM_MUX("Left PGA Mux", WM8988_PWR1, 5, 0,
325 &wm8988_left_pga_controls),
326 SND_SOC_DAPM_MUX("Right PGA Mux", WM8988_PWR1, 4, 0,
327 &wm8988_right_pga_controls),
328
329 SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
330 &wm8988_left_line_controls),
331 SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
332 &wm8988_right_line_controls),
333
334 SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0),
335 SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0),
336
337 SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0),
338 SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0),
339
340 SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
341 &wm8988_left_mixer_controls[0],
342 ARRAY_SIZE(wm8988_left_mixer_controls)),
343 SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
344 &wm8988_right_mixer_controls[0],
345 ARRAY_SIZE(wm8988_right_mixer_controls)),
346
347 SND_SOC_DAPM_PGA("Right Out 2", WM8988_PWR2, 3, 0, NULL, 0),
348 SND_SOC_DAPM_PGA("Left Out 2", WM8988_PWR2, 4, 0, NULL, 0),
349 SND_SOC_DAPM_PGA("Right Out 1", WM8988_PWR2, 5, 0, NULL, 0),
350 SND_SOC_DAPM_PGA("Left Out 1", WM8988_PWR2, 6, 0, NULL, 0),
351
352 SND_SOC_DAPM_POST("LRC control", wm8988_lrc_control),
353
354 SND_SOC_DAPM_OUTPUT("LOUT1"),
355 SND_SOC_DAPM_OUTPUT("ROUT1"),
356 SND_SOC_DAPM_OUTPUT("LOUT2"),
357 SND_SOC_DAPM_OUTPUT("ROUT2"),
358 SND_SOC_DAPM_OUTPUT("VREF"),
359
360 SND_SOC_DAPM_INPUT("LINPUT1"),
361 SND_SOC_DAPM_INPUT("LINPUT2"),
362 SND_SOC_DAPM_INPUT("RINPUT1"),
363 SND_SOC_DAPM_INPUT("RINPUT2"),
364};
365
366static const struct snd_soc_dapm_route audio_map[] = {
367
368 { "Left Line Mux", "Line 1", "LINPUT1" },
369 { "Left Line Mux", "Line 2", "LINPUT2" },
370 { "Left Line Mux", "PGA", "Left PGA Mux" },
371 { "Left Line Mux", "Differential", "Differential Mux" },
372
373 { "Right Line Mux", "Line 1", "RINPUT1" },
374 { "Right Line Mux", "Line 2", "RINPUT2" },
375 { "Right Line Mux", "PGA", "Right PGA Mux" },
376 { "Right Line Mux", "Differential", "Differential Mux" },
377
378 { "Left PGA Mux", "Line 1", "LINPUT1" },
379 { "Left PGA Mux", "Line 2", "LINPUT2" },
380 { "Left PGA Mux", "Differential", "Differential Mux" },
381
382 { "Right PGA Mux", "Line 1", "RINPUT1" },
383 { "Right PGA Mux", "Line 2", "RINPUT2" },
384 { "Right PGA Mux", "Differential", "Differential Mux" },
385
386 { "Differential Mux", "Line 1", "LINPUT1" },
387 { "Differential Mux", "Line 1", "RINPUT1" },
388 { "Differential Mux", "Line 2", "LINPUT2" },
389 { "Differential Mux", "Line 2", "RINPUT2" },
390
391 { "Left ADC Mux", "Stereo", "Left PGA Mux" },
392 { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
393 { "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
394
395 { "Right ADC Mux", "Stereo", "Right PGA Mux" },
396 { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
397 { "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
398
399 { "Left ADC", NULL, "Left ADC Mux" },
400 { "Right ADC", NULL, "Right ADC Mux" },
401
402 { "Left Line Mux", "Line 1", "LINPUT1" },
403 { "Left Line Mux", "Line 2", "LINPUT2" },
404 { "Left Line Mux", "PGA", "Left PGA Mux" },
405 { "Left Line Mux", "Differential", "Differential Mux" },
406
407 { "Right Line Mux", "Line 1", "RINPUT1" },
408 { "Right Line Mux", "Line 2", "RINPUT2" },
409 { "Right Line Mux", "PGA", "Right PGA Mux" },
410 { "Right Line Mux", "Differential", "Differential Mux" },
411
412 { "Left Mixer", "Playback Switch", "Left DAC" },
413 { "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
414 { "Left Mixer", "Right Playback Switch", "Right DAC" },
415 { "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
416
417 { "Right Mixer", "Left Playback Switch", "Left DAC" },
418 { "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
419 { "Right Mixer", "Playback Switch", "Right DAC" },
420 { "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
421
422 { "Left Out 1", NULL, "Left Mixer" },
423 { "LOUT1", NULL, "Left Out 1" },
424 { "Right Out 1", NULL, "Right Mixer" },
425 { "ROUT1", NULL, "Right Out 1" },
426
427 { "Left Out 2", NULL, "Left Mixer" },
428 { "LOUT2", NULL, "Left Out 2" },
429 { "Right Out 2", NULL, "Right Mixer" },
430 { "ROUT2", NULL, "Right Out 2" },
431};
432
433struct _coeff_div {
434 u32 mclk;
435 u32 rate;
436 u16 fs;
437 u8 sr:5;
438 u8 usb:1;
439};
440
441/* codec hifi mclk clock divider coefficients */
442static const struct _coeff_div coeff_div[] = {
443 /* 8k */
444 {12288000, 8000, 1536, 0x6, 0x0},
445 {11289600, 8000, 1408, 0x16, 0x0},
446 {18432000, 8000, 2304, 0x7, 0x0},
447 {16934400, 8000, 2112, 0x17, 0x0},
448 {12000000, 8000, 1500, 0x6, 0x1},
449
450 /* 11.025k */
451 {11289600, 11025, 1024, 0x18, 0x0},
452 {16934400, 11025, 1536, 0x19, 0x0},
453 {12000000, 11025, 1088, 0x19, 0x1},
454
455 /* 16k */
456 {12288000, 16000, 768, 0xa, 0x0},
457 {18432000, 16000, 1152, 0xb, 0x0},
458 {12000000, 16000, 750, 0xa, 0x1},
459
460 /* 22.05k */
461 {11289600, 22050, 512, 0x1a, 0x0},
462 {16934400, 22050, 768, 0x1b, 0x0},
463 {12000000, 22050, 544, 0x1b, 0x1},
464
465 /* 32k */
466 {12288000, 32000, 384, 0xc, 0x0},
467 {18432000, 32000, 576, 0xd, 0x0},
468 {12000000, 32000, 375, 0xa, 0x1},
469
470 /* 44.1k */
471 {11289600, 44100, 256, 0x10, 0x0},
472 {16934400, 44100, 384, 0x11, 0x0},
473 {12000000, 44100, 272, 0x11, 0x1},
474
475 /* 48k */
476 {12288000, 48000, 256, 0x0, 0x0},
477 {18432000, 48000, 384, 0x1, 0x0},
478 {12000000, 48000, 250, 0x0, 0x1},
479
480 /* 88.2k */
481 {11289600, 88200, 128, 0x1e, 0x0},
482 {16934400, 88200, 192, 0x1f, 0x0},
483 {12000000, 88200, 136, 0x1f, 0x1},
484
485 /* 96k */
486 {12288000, 96000, 128, 0xe, 0x0},
487 {18432000, 96000, 192, 0xf, 0x0},
488 {12000000, 96000, 125, 0xe, 0x1},
489};
490
491static inline int get_coeff(int mclk, int rate)
492{
493 int i;
494
495 for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
496 if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
497 return i;
498 }
499
500 return -EINVAL;
501}
502
503/* The set of rates we can generate from the above for each SYSCLK */
504
505static unsigned int rates_12288[] = {
506 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
507};
508
509static struct snd_pcm_hw_constraint_list constraints_12288 = {
510 .count = ARRAY_SIZE(rates_12288),
511 .list = rates_12288,
512};
513
514static unsigned int rates_112896[] = {
515 8000, 11025, 22050, 44100,
516};
517
518static struct snd_pcm_hw_constraint_list constraints_112896 = {
519 .count = ARRAY_SIZE(rates_112896),
520 .list = rates_112896,
521};
522
523static unsigned int rates_12[] = {
524 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
525 48000, 88235, 96000,
526};
527
528static struct snd_pcm_hw_constraint_list constraints_12 = {
529 .count = ARRAY_SIZE(rates_12),
530 .list = rates_12,
531};
532
533/*
534 * Note that this should be called from init rather than from hw_params.
535 */
536static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
537 int clk_id, unsigned int freq, int dir)
538{
539 struct snd_soc_codec *codec = codec_dai->codec;
540 struct wm8988_priv *wm8988 = codec->private_data;
541
542 switch (freq) {
543 case 11289600:
544 case 18432000:
545 case 22579200:
546 case 36864000:
547 wm8988->sysclk_constraints = &constraints_112896;
548 wm8988->sysclk = freq;
549 return 0;
550
551 case 12288000:
552 case 16934400:
553 case 24576000:
554 case 33868800:
555 wm8988->sysclk_constraints = &constraints_12288;
556 wm8988->sysclk = freq;
557 return 0;
558
559 case 12000000:
560 case 24000000:
561 wm8988->sysclk_constraints = &constraints_12;
562 wm8988->sysclk = freq;
563 return 0;
564 }
565 return -EINVAL;
566}
567
568static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
569 unsigned int fmt)
570{
571 struct snd_soc_codec *codec = codec_dai->codec;
572 u16 iface = 0;
573
574 /* set master/slave audio interface */
575 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
576 case SND_SOC_DAIFMT_CBM_CFM:
577 iface = 0x0040;
578 break;
579 case SND_SOC_DAIFMT_CBS_CFS:
580 break;
581 default:
582 return -EINVAL;
583 }
584
585 /* interface format */
586 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
587 case SND_SOC_DAIFMT_I2S:
588 iface |= 0x0002;
589 break;
590 case SND_SOC_DAIFMT_RIGHT_J:
591 break;
592 case SND_SOC_DAIFMT_LEFT_J:
593 iface |= 0x0001;
594 break;
595 case SND_SOC_DAIFMT_DSP_A:
596 iface |= 0x0003;
597 break;
598 case SND_SOC_DAIFMT_DSP_B:
599 iface |= 0x0013;
600 break;
601 default:
602 return -EINVAL;
603 }
604
605 /* clock inversion */
606 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
607 case SND_SOC_DAIFMT_NB_NF:
608 break;
609 case SND_SOC_DAIFMT_IB_IF:
610 iface |= 0x0090;
611 break;
612 case SND_SOC_DAIFMT_IB_NF:
613 iface |= 0x0080;
614 break;
615 case SND_SOC_DAIFMT_NB_IF:
616 iface |= 0x0010;
617 break;
618 default:
619 return -EINVAL;
620 }
621
622 wm8988_write(codec, WM8988_IFACE, iface);
623 return 0;
624}
625
626static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
627 struct snd_soc_dai *dai)
628{
629 struct snd_soc_codec *codec = dai->codec;
630 struct wm8988_priv *wm8988 = codec->private_data;
631
632 /* The set of sample rates that can be supported depends on the
633 * MCLK supplied to the CODEC - enforce this.
634 */
635 if (!wm8988->sysclk) {
636 dev_err(codec->dev,
637 "No MCLK configured, call set_sysclk() on init\n");
638 return -EINVAL;
639 }
640
641 snd_pcm_hw_constraint_list(substream->runtime, 0,
642 SNDRV_PCM_HW_PARAM_RATE,
643 wm8988->sysclk_constraints);
644
645 return 0;
646}
647
648static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
649 struct snd_pcm_hw_params *params,
650 struct snd_soc_dai *dai)
651{
652 struct snd_soc_pcm_runtime *rtd = substream->private_data;
653 struct snd_soc_device *socdev = rtd->socdev;
654 struct snd_soc_codec *codec = socdev->card->codec;
655 struct wm8988_priv *wm8988 = codec->private_data;
656 u16 iface = wm8988_read_reg_cache(codec, WM8988_IFACE) & 0x1f3;
657 u16 srate = wm8988_read_reg_cache(codec, WM8988_SRATE) & 0x180;
658 int coeff;
659
660 coeff = get_coeff(wm8988->sysclk, params_rate(params));
661 if (coeff < 0) {
662 coeff = get_coeff(wm8988->sysclk / 2, params_rate(params));
663 srate |= 0x40;
664 }
665 if (coeff < 0) {
666 dev_err(codec->dev,
667 "Unable to configure sample rate %dHz with %dHz MCLK\n",
668 params_rate(params), wm8988->sysclk);
669 return coeff;
670 }
671
672 /* bit size */
673 switch (params_format(params)) {
674 case SNDRV_PCM_FORMAT_S16_LE:
675 break;
676 case SNDRV_PCM_FORMAT_S20_3LE:
677 iface |= 0x0004;
678 break;
679 case SNDRV_PCM_FORMAT_S24_LE:
680 iface |= 0x0008;
681 break;
682 case SNDRV_PCM_FORMAT_S32_LE:
683 iface |= 0x000c;
684 break;
685 }
686
687 /* set iface & srate */
688 wm8988_write(codec, WM8988_IFACE, iface);
689 if (coeff >= 0)
690 wm8988_write(codec, WM8988_SRATE, srate |
691 (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
692
693 return 0;
694}
695
696static int wm8988_mute(struct snd_soc_dai *dai, int mute)
697{
698 struct snd_soc_codec *codec = dai->codec;
699 u16 mute_reg = wm8988_read_reg_cache(codec, WM8988_ADCDAC) & 0xfff7;
700
701 if (mute)
702 wm8988_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
703 else
704 wm8988_write(codec, WM8988_ADCDAC, mute_reg);
705 return 0;
706}
707
708static int wm8988_set_bias_level(struct snd_soc_codec *codec,
709 enum snd_soc_bias_level level)
710{
711 u16 pwr_reg = wm8988_read_reg_cache(codec, WM8988_PWR1) & ~0x1c1;
712
713 switch (level) {
714 case SND_SOC_BIAS_ON:
715 break;
716
717 case SND_SOC_BIAS_PREPARE:
718 /* VREF, VMID=2x50k, digital enabled */
719 wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
720 break;
721
722 case SND_SOC_BIAS_STANDBY:
723 if (codec->bias_level == SND_SOC_BIAS_OFF) {
724 /* VREF, VMID=2x5k */
725 wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
726
727 /* Charge caps */
728 msleep(100);
729 }
730
731 /* VREF, VMID=2*500k, digital stopped */
732 wm8988_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
733 break;
734
735 case SND_SOC_BIAS_OFF:
736 wm8988_write(codec, WM8988_PWR1, 0x0000);
737 break;
738 }
739 codec->bias_level = level;
740 return 0;
741}
742
743#define WM8988_RATES SNDRV_PCM_RATE_8000_96000
744
745#define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
746 SNDRV_PCM_FMTBIT_S24_LE)
747
748static struct snd_soc_dai_ops wm8988_ops = {
749 .startup = wm8988_pcm_startup,
750 .hw_params = wm8988_pcm_hw_params,
751 .set_fmt = wm8988_set_dai_fmt,
752 .set_sysclk = wm8988_set_dai_sysclk,
753 .digital_mute = wm8988_mute,
754};
755
756struct snd_soc_dai wm8988_dai = {
757 .name = "WM8988",
758 .playback = {
759 .stream_name = "Playback",
760 .channels_min = 1,
761 .channels_max = 2,
762 .rates = WM8988_RATES,
763 .formats = WM8988_FORMATS,
764 },
765 .capture = {
766 .stream_name = "Capture",
767 .channels_min = 1,
768 .channels_max = 2,
769 .rates = WM8988_RATES,
770 .formats = WM8988_FORMATS,
771 },
772 .ops = &wm8988_ops,
773 .symmetric_rates = 1,
774};
775EXPORT_SYMBOL_GPL(wm8988_dai);
776
777static int wm8988_suspend(struct platform_device *pdev, pm_message_t state)
778{
779 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
780 struct snd_soc_codec *codec = socdev->card->codec;
781
782 wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
783 return 0;
784}
785
786static int wm8988_resume(struct platform_device *pdev)
787{
788 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
789 struct snd_soc_codec *codec = socdev->card->codec;
790 int i;
791 u8 data[2];
792 u16 *cache = codec->reg_cache;
793
794 /* Sync reg_cache with the hardware */
795 for (i = 0; i < WM8988_NUM_REG; i++) {
796 if (i == WM8988_RESET)
797 continue;
798 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
799 data[1] = cache[i] & 0x00ff;
800 codec->hw_write(codec->control_data, data, 2);
801 }
802
803 wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
804
805 return 0;
806}
807
808static struct snd_soc_codec *wm8988_codec;
809
810static int wm8988_probe(struct platform_device *pdev)
811{
812 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
813 struct snd_soc_codec *codec;
814 int ret = 0;
815
816 if (wm8988_codec == NULL) {
817 dev_err(&pdev->dev, "Codec device not registered\n");
818 return -ENODEV;
819 }
820
821 socdev->card->codec = wm8988_codec;
822 codec = wm8988_codec;
823
824 /* register pcms */
825 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
826 if (ret < 0) {
827 dev_err(codec->dev, "failed to create pcms: %d\n", ret);
828 goto pcm_err;
829 }
830
831 snd_soc_add_controls(codec, wm8988_snd_controls,
832 ARRAY_SIZE(wm8988_snd_controls));
833 snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
834 ARRAY_SIZE(wm8988_dapm_widgets));
835 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
836 snd_soc_dapm_new_widgets(codec);
837
838 ret = snd_soc_init_card(socdev);
839 if (ret < 0) {
840 dev_err(codec->dev, "failed to register card: %d\n", ret);
841 goto card_err;
842 }
843
844 return ret;
845
846card_err:
847 snd_soc_free_pcms(socdev);
848 snd_soc_dapm_free(socdev);
849pcm_err:
850 return ret;
851}
852
853static int wm8988_remove(struct platform_device *pdev)
854{
855 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
856
857 snd_soc_free_pcms(socdev);
858 snd_soc_dapm_free(socdev);
859
860 return 0;
861}
862
863struct snd_soc_codec_device soc_codec_dev_wm8988 = {
864 .probe = wm8988_probe,
865 .remove = wm8988_remove,
866 .suspend = wm8988_suspend,
867 .resume = wm8988_resume,
868};
869EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988);
870
871static int wm8988_register(struct wm8988_priv *wm8988)
872{
873 struct snd_soc_codec *codec = &wm8988->codec;
874 int ret;
875 u16 reg;
876
877 if (wm8988_codec) {
878 dev_err(codec->dev, "Another WM8988 is registered\n");
879 ret = -EINVAL;
880 goto err;
881 }
882
883 mutex_init(&codec->mutex);
884 INIT_LIST_HEAD(&codec->dapm_widgets);
885 INIT_LIST_HEAD(&codec->dapm_paths);
886
887 codec->private_data = wm8988;
888 codec->name = "WM8988";
889 codec->owner = THIS_MODULE;
890 codec->read = wm8988_read_reg_cache;
891 codec->write = wm8988_write;
892 codec->dai = &wm8988_dai;
893 codec->num_dai = 1;
894 codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache);
895 codec->reg_cache = &wm8988->reg_cache;
896 codec->bias_level = SND_SOC_BIAS_OFF;
897 codec->set_bias_level = wm8988_set_bias_level;
898
899 memcpy(codec->reg_cache, wm8988_reg,
900 sizeof(wm8988_reg));
901
902 ret = wm8988_reset(codec);
903 if (ret < 0) {
904 dev_err(codec->dev, "Failed to issue reset\n");
905 return ret;
906 }
907
908 /* set the update bits (we always update left then right) */
909 reg = wm8988_read_reg_cache(codec, WM8988_RADC);
910 wm8988_write(codec, WM8988_RADC, reg | 0x100);
911 reg = wm8988_read_reg_cache(codec, WM8988_RDAC);
912 wm8988_write(codec, WM8988_RDAC, reg | 0x0100);
913 reg = wm8988_read_reg_cache(codec, WM8988_ROUT1V);
914 wm8988_write(codec, WM8988_ROUT1V, reg | 0x0100);
915 reg = wm8988_read_reg_cache(codec, WM8988_ROUT2V);
916 wm8988_write(codec, WM8988_ROUT2V, reg | 0x0100);
917 reg = wm8988_read_reg_cache(codec, WM8988_RINVOL);
918 wm8988_write(codec, WM8988_RINVOL, reg | 0x0100);
919
920 wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY);
921
922 wm8988_dai.dev = codec->dev;
923
924 wm8988_codec = codec;
925
926 ret = snd_soc_register_codec(codec);
927 if (ret != 0) {
928 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
929 return ret;
930 }
931
932 ret = snd_soc_register_dai(&wm8988_dai);
933 if (ret != 0) {
934 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
935 snd_soc_unregister_codec(codec);
936 return ret;
937 }
938
939 return 0;
940
941err:
942 kfree(wm8988);
943 return ret;
944}
945
946static void wm8988_unregister(struct wm8988_priv *wm8988)
947{
948 wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_OFF);
949 snd_soc_unregister_dai(&wm8988_dai);
950 snd_soc_unregister_codec(&wm8988->codec);
951 kfree(wm8988);
952 wm8988_codec = NULL;
953}
954
955#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
956static int wm8988_i2c_probe(struct i2c_client *i2c,
957 const struct i2c_device_id *id)
958{
959 struct wm8988_priv *wm8988;
960 struct snd_soc_codec *codec;
961
962 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
963 if (wm8988 == NULL)
964 return -ENOMEM;
965
966 codec = &wm8988->codec;
967 codec->hw_write = (hw_write_t)i2c_master_send;
968
969 i2c_set_clientdata(i2c, wm8988);
970 codec->control_data = i2c;
971
972 codec->dev = &i2c->dev;
973
974 return wm8988_register(wm8988);
975}
976
977static int wm8988_i2c_remove(struct i2c_client *client)
978{
979 struct wm8988_priv *wm8988 = i2c_get_clientdata(client);
980 wm8988_unregister(wm8988);
981 return 0;
982}
983
984static const struct i2c_device_id wm8988_i2c_id[] = {
985 { "wm8988", 0 },
986 { }
987};
988MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
989
990static struct i2c_driver wm8988_i2c_driver = {
991 .driver = {
992 .name = "WM8988",
993 .owner = THIS_MODULE,
994 },
995 .probe = wm8988_i2c_probe,
996 .remove = wm8988_i2c_remove,
997 .id_table = wm8988_i2c_id,
998};
999#endif
1000
1001#if defined(CONFIG_SPI_MASTER)
1002static int wm8988_spi_write(struct spi_device *spi, const char *data, int len)
1003{
1004 struct spi_transfer t;
1005 struct spi_message m;
1006 u8 msg[2];
1007
1008 if (len <= 0)
1009 return 0;
1010
1011 msg[0] = data[0];
1012 msg[1] = data[1];
1013
1014 spi_message_init(&m);
1015 memset(&t, 0, (sizeof t));
1016
1017 t.tx_buf = &msg[0];
1018 t.len = len;
1019
1020 spi_message_add_tail(&t, &m);
1021 spi_sync(spi, &m);
1022
1023 return len;
1024}
1025
1026static int __devinit wm8988_spi_probe(struct spi_device *spi)
1027{
1028 struct wm8988_priv *wm8988;
1029 struct snd_soc_codec *codec;
1030
1031 wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
1032 if (wm8988 == NULL)
1033 return -ENOMEM;
1034
1035 codec = &wm8988->codec;
1036 codec->hw_write = (hw_write_t)wm8988_spi_write;
1037 codec->control_data = spi;
1038 codec->dev = &spi->dev;
1039
1040 spi->dev.driver_data = wm8988;
1041
1042 return wm8988_register(wm8988);
1043}
1044
1045static int __devexit wm8988_spi_remove(struct spi_device *spi)
1046{
1047 struct wm8988_priv *wm8988 = spi->dev.driver_data;
1048
1049 wm8988_unregister(wm8988);
1050
1051 return 0;
1052}
1053
1054static struct spi_driver wm8988_spi_driver = {
1055 .driver = {
1056 .name = "wm8988",
1057 .bus = &spi_bus_type,
1058 .owner = THIS_MODULE,
1059 },
1060 .probe = wm8988_spi_probe,
1061 .remove = __devexit_p(wm8988_spi_remove),
1062};
1063#endif
1064
1065static int __init wm8988_modinit(void)
1066{
1067 int ret;
1068
1069#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1070 ret = i2c_add_driver(&wm8988_i2c_driver);
1071 if (ret != 0)
1072 pr_err("WM8988: Unable to register I2C driver: %d\n", ret);
1073#endif
1074#if defined(CONFIG_SPI_MASTER)
1075 ret = spi_register_driver(&wm8988_spi_driver);
1076 if (ret != 0)
1077 pr_err("WM8988: Unable to register SPI driver: %d\n", ret);
1078#endif
1079 return ret;
1080}
1081module_init(wm8988_modinit);
1082
1083static void __exit wm8988_exit(void)
1084{
1085#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1086 i2c_del_driver(&wm8988_i2c_driver);
1087#endif
1088#if defined(CONFIG_SPI_MASTER)
1089 spi_unregister_driver(&wm8988_spi_driver);
1090#endif
1091}
1092module_exit(wm8988_exit);
1093
1094
1095MODULE_DESCRIPTION("ASoC WM8988 driver");
1096MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
1097MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8988.h b/sound/soc/codecs/wm8988.h
new file mode 100644
index 000000000000..4552d37fdd41
--- /dev/null
+++ b/sound/soc/codecs/wm8988.h
@@ -0,0 +1,60 @@
1/*
2 * Copyright 2005 Openedhand Ltd.
3 *
4 * Author: Richard Purdie <richard@openedhand.com>
5 *
6 * Based on WM8753.h
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#ifndef _WM8988_H
15#define _WM8988_H
16
17/* WM8988 register space */
18
19#define WM8988_LINVOL 0x00
20#define WM8988_RINVOL 0x01
21#define WM8988_LOUT1V 0x02
22#define WM8988_ROUT1V 0x03
23#define WM8988_ADCDAC 0x05
24#define WM8988_IFACE 0x07
25#define WM8988_SRATE 0x08
26#define WM8988_LDAC 0x0a
27#define WM8988_RDAC 0x0b
28#define WM8988_BASS 0x0c
29#define WM8988_TREBLE 0x0d
30#define WM8988_RESET 0x0f
31#define WM8988_3D 0x10
32#define WM8988_ALC1 0x11
33#define WM8988_ALC2 0x12
34#define WM8988_ALC3 0x13
35#define WM8988_NGATE 0x14
36#define WM8988_LADC 0x15
37#define WM8988_RADC 0x16
38#define WM8988_ADCTL1 0x17
39#define WM8988_ADCTL2 0x18
40#define WM8988_PWR1 0x19
41#define WM8988_PWR2 0x1a
42#define WM8988_ADCTL3 0x1b
43#define WM8988_ADCIN 0x1f
44#define WM8988_LADCIN 0x20
45#define WM8988_RADCIN 0x21
46#define WM8988_LOUTM1 0x22
47#define WM8988_LOUTM2 0x23
48#define WM8988_ROUTM1 0x24
49#define WM8988_ROUTM2 0x25
50#define WM8988_LOUT2V 0x28
51#define WM8988_ROUT2V 0x29
52#define WM8988_LPPB 0x43
53#define WM8988_NUM_REG 0x44
54
55#define WM8988_SYSCLK 0
56
57extern struct snd_soc_dai wm8988_dai;
58extern struct snd_soc_codec_device soc_codec_dev_wm8988;
59
60#endif