aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig15
-rw-r--r--sound/soc/codecs/Makefile9
-rw-r--r--sound/soc/codecs/ac97.c156
-rw-r--r--sound/soc/codecs/ac97.h18
-rw-r--r--sound/soc/codecs/wm8731.c758
-rw-r--r--sound/soc/codecs/wm8731.h44
-rw-r--r--sound/soc/codecs/wm8750.c1049
-rw-r--r--sound/soc/codecs/wm8750.h67
-rw-r--r--sound/soc/codecs/wm9712.c771
-rw-r--r--sound/soc/codecs/wm9712.h14
10 files changed, 2901 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
new file mode 100644
index 000000000000..78ac2688e124
--- /dev/null
+++ b/sound/soc/codecs/Kconfig
@@ -0,0 +1,15 @@
1config SND_SOC_AC97_CODEC
2 tristate
3 depends SND_SOC
4
5config SND_SOC_WM8731
6 tristate
7 depends SND_SOC
8
9config SND_SOC_WM8750
10 tristate
11 depends SND_SOC
12
13config SND_SOC_WM9712
14 tristate
15 depends SND_SOC
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
new file mode 100644
index 000000000000..3249a6e4f1d0
--- /dev/null
+++ b/sound/soc/codecs/Makefile
@@ -0,0 +1,9 @@
1snd-soc-ac97-objs := ac97.o
2snd-soc-wm8731-objs := wm8731.o
3snd-soc-wm8750-objs := wm8750.o
4snd-soc-wm9712-objs := wm9712.o
5
6obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
7obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
8obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
9obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
new file mode 100644
index 000000000000..55bc55eb6e24
--- /dev/null
+++ b/sound/soc/codecs/ac97.c
@@ -0,0 +1,156 @@
1/*
2 * ac97.c -- ALSA Soc AC97 codec support
3 *
4 * Copyright 2005 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood
6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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 * Revision history
14 * 17th Oct 2005 Initial version.
15 *
16 * Generic AC97 support.
17 */
18
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/device.h>
22#include <sound/driver.h>
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/ac97_codec.h>
26#include <sound/initval.h>
27#include <sound/soc.h>
28
29#define AC97_VERSION "0.6"
30
31static int ac97_prepare(struct snd_pcm_substream *substream)
32{
33 struct snd_pcm_runtime *runtime = substream->runtime;
34 struct snd_soc_pcm_runtime *rtd = substream->private_data;
35 struct snd_soc_device *socdev = rtd->socdev;
36 struct snd_soc_codec *codec = socdev->codec;
37
38 int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
39 AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
40 return snd_ac97_set_rate(codec->ac97, reg, runtime->rate);
41}
42
43#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
44 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
45
46static struct snd_soc_codec_dai ac97_dai = {
47 .name = "AC97 HiFi",
48 .playback = {
49 .stream_name = "AC97 Playback",
50 .channels_min = 1,
51 .channels_max = 2,
52 .rates = STD_AC97_RATES,
53 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
54 .capture = {
55 .stream_name = "AC97 Capture",
56 .channels_min = 1,
57 .channels_max = 2,
58 .rates = STD_AC97_RATES,
59 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
60 .ops = {
61 .prepare = ac97_prepare,},
62};
63
64static unsigned int ac97_read(struct snd_soc_codec *codec,
65 unsigned int reg)
66{
67 return soc_ac97_ops.read(codec->ac97, reg);
68}
69
70static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
71 unsigned int val)
72{
73 soc_ac97_ops.write(codec->ac97, reg, val);
74 return 0;
75}
76
77static int ac97_soc_probe(struct platform_device *pdev)
78{
79 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
80 struct snd_soc_codec *codec;
81 struct snd_ac97_bus *ac97_bus;
82 struct snd_ac97_template ac97_template;
83 int ret = 0;
84
85 printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
86
87 socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
88 if (socdev->codec == NULL)
89 return -ENOMEM;
90 codec = socdev->codec;
91 mutex_init(&codec->mutex);
92
93 codec->name = "AC97";
94 codec->owner = THIS_MODULE;
95 codec->dai = &ac97_dai;
96 codec->num_dai = 1;
97 codec->write = ac97_write;
98 codec->read = ac97_read;
99 INIT_LIST_HEAD(&codec->dapm_widgets);
100 INIT_LIST_HEAD(&codec->dapm_paths);
101
102 /* register pcms */
103 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
104 if(ret < 0)
105 goto err;
106
107 /* add codec as bus device for standard ac97 */
108 ret = snd_ac97_bus(codec->card, 0, &soc_ac97_ops, NULL, &ac97_bus);
109 if(ret < 0)
110 goto bus_err;
111
112 memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
113 ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
114 if(ret < 0)
115 goto bus_err;
116
117 ret = snd_soc_register_card(socdev);
118 if (ret < 0)
119 goto bus_err;
120 return 0;
121
122bus_err:
123 snd_soc_free_pcms(socdev);
124
125err:
126 kfree(socdev->codec->reg_cache);
127 kfree(socdev->codec);
128 socdev->codec = NULL;
129 return ret;
130}
131
132static int ac97_soc_remove(struct platform_device *pdev)
133{
134 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
135 struct snd_soc_codec *codec = socdev->codec;
136
137 if(codec == NULL)
138 return 0;
139
140 snd_soc_free_pcms(socdev);
141 kfree(socdev->codec->reg_cache);
142 kfree(socdev->codec);
143
144 return 0;
145}
146
147struct snd_soc_codec_device soc_codec_dev_ac97= {
148 .probe = ac97_soc_probe,
149 .remove = ac97_soc_remove,
150};
151
152EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
153
154MODULE_DESCRIPTION("Soc Generic AC97 driver");
155MODULE_AUTHOR("Liam Girdwood");
156MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ac97.h b/sound/soc/codecs/ac97.h
new file mode 100644
index 000000000000..930ddfc2321a
--- /dev/null
+++ b/sound/soc/codecs/ac97.h
@@ -0,0 +1,18 @@
1/*
2 * linux/sound/codecs/ac97.h -- ALSA SoC Layer
3 *
4 * Author: Liam Girdwood
5 * Created: Dec 1st 2005
6 * Copyright: Wolfson Microelectronics. PLC.
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#ifndef __LINUX_SND_SOC_AC97_H
14#define __LINUX_SND_SOC_AC97_H
15
16extern struct snd_soc_codec_device soc_codec_dev_ac97;
17
18#endif
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
new file mode 100644
index 000000000000..7ca0b5268289
--- /dev/null
+++ b/sound/soc/codecs/wm8731.c
@@ -0,0 +1,758 @@
1/*
2 * wm8731.c -- WM8731 ALSA SoC Audio driver
3 *
4 * Copyright 2005 Openedhand Ltd.
5 *
6 * Author: Richard Purdie <richard@openedhand.com>
7 *
8 * Based on wm8753.c by Liam Girdwood
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/init.h>
18#include <linux/delay.h>
19#include <linux/pm.h>
20#include <linux/i2c.h>
21#include <linux/platform_device.h>
22#include <sound/driver.h>
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/pcm_params.h>
26#include <sound/soc.h>
27#include <sound/soc-dapm.h>
28#include <sound/initval.h>
29
30#include "wm8731.h"
31
32#define AUDIO_NAME "wm8731"
33#define WM8731_VERSION "0.13"
34
35/*
36 * Debug
37 */
38
39#define WM8731_DEBUG 0
40
41#ifdef WM8731_DEBUG
42#define dbg(format, arg...) \
43 printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
44#else
45#define dbg(format, arg...) do {} while (0)
46#endif
47#define err(format, arg...) \
48 printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
49#define info(format, arg...) \
50 printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
51#define warn(format, arg...) \
52 printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
53
54struct snd_soc_codec_device soc_codec_dev_wm8731;
55
56/* codec private data */
57struct wm8731_priv {
58 unsigned int sysclk;
59};
60
61/*
62 * wm8731 register cache
63 * We can't read the WM8731 register space when we are
64 * using 2 wire for device control, so we cache them instead.
65 * There is no point in caching the reset register
66 */
67static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
68 0x0097, 0x0097, 0x0079, 0x0079,
69 0x000a, 0x0008, 0x009f, 0x000a,
70 0x0000, 0x0000
71};
72
73/*
74 * read wm8731 register cache
75 */
76static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
77 unsigned int reg)
78{
79 u16 *cache = codec->reg_cache;
80 if (reg == WM8731_RESET)
81 return 0;
82 if (reg >= WM8731_CACHEREGNUM)
83 return -1;
84 return cache[reg];
85}
86
87/*
88 * write wm8731 register cache
89 */
90static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
91 u16 reg, unsigned int value)
92{
93 u16 *cache = codec->reg_cache;
94 if (reg >= WM8731_CACHEREGNUM)
95 return;
96 cache[reg] = value;
97}
98
99/*
100 * write to the WM8731 register space
101 */
102static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
103 unsigned int value)
104{
105 u8 data[2];
106
107 /* data is
108 * D15..D9 WM8731 register offset
109 * D8...D0 register data
110 */
111 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
112 data[1] = value & 0x00ff;
113
114 wm8731_write_reg_cache (codec, reg, value);
115 if (codec->hw_write(codec->control_data, data, 2) == 2)
116 return 0;
117 else
118 return -EIO;
119}
120
121#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0)
122
123static const char *wm8731_input_select[] = {"Line In", "Mic"};
124static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
125
126static const struct soc_enum wm8731_enum[] = {
127 SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select),
128 SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
129};
130
131static const struct snd_kcontrol_new wm8731_snd_controls[] = {
132
133SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
134 0, 127, 0),
135SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
136 7, 1, 0),
137
138SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
139SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
140
141SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
142SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
143
144SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
145
146SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
147SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
148
149SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
150};
151
152/* add non dapm controls */
153static int wm8731_add_controls(struct snd_soc_codec *codec)
154{
155 int err, i;
156
157 for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
158 if ((err = snd_ctl_add(codec->card,
159 snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0)
160 return err;
161 }
162
163 return 0;
164}
165
166/* Output Mixer */
167static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
168SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
169SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
170SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
171};
172
173/* Input mux */
174static const struct snd_kcontrol_new wm8731_input_mux_controls =
175SOC_DAPM_ENUM("Input Select", wm8731_enum[0]);
176
177static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
178SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
179 &wm8731_output_mixer_controls[0],
180 ARRAY_SIZE(wm8731_output_mixer_controls)),
181SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),
182SND_SOC_DAPM_OUTPUT("LOUT"),
183SND_SOC_DAPM_OUTPUT("LHPOUT"),
184SND_SOC_DAPM_OUTPUT("ROUT"),
185SND_SOC_DAPM_OUTPUT("RHPOUT"),
186SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),
187SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),
188SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),
189SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),
190SND_SOC_DAPM_INPUT("MICIN"),
191SND_SOC_DAPM_INPUT("RLINEIN"),
192SND_SOC_DAPM_INPUT("LLINEIN"),
193};
194
195static const char *intercon[][3] = {
196 /* output mixer */
197 {"Output Mixer", "Line Bypass Switch", "Line Input"},
198 {"Output Mixer", "HiFi Playback Switch", "DAC"},
199 {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
200
201 /* outputs */
202 {"RHPOUT", NULL, "Output Mixer"},
203 {"ROUT", NULL, "Output Mixer"},
204 {"LHPOUT", NULL, "Output Mixer"},
205 {"LOUT", NULL, "Output Mixer"},
206
207 /* input mux */
208 {"Input Mux", "Line In", "Line Input"},
209 {"Input Mux", "Mic", "Mic Bias"},
210 {"ADC", NULL, "Input Mux"},
211
212 /* inputs */
213 {"Line Input", NULL, "LLINEIN"},
214 {"Line Input", NULL, "RLINEIN"},
215 {"Mic Bias", NULL, "MICIN"},
216
217 /* terminator */
218 {NULL, NULL, NULL},
219};
220
221static int wm8731_add_widgets(struct snd_soc_codec *codec)
222{
223 int i;
224
225 for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
226 snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
227 }
228
229 /* set up audio path interconnects */
230 for(i = 0; intercon[i][0] != NULL; i++) {
231 snd_soc_dapm_connect_input(codec, intercon[i][0],
232 intercon[i][1], intercon[i][2]);
233 }
234
235 snd_soc_dapm_new_widgets(codec);
236 return 0;
237}
238
239struct _coeff_div {
240 u32 mclk;
241 u32 rate;
242 u16 fs;
243 u8 sr:4;
244 u8 bosr:1;
245 u8 usb:1;
246};
247
248/* codec mclk clock divider coefficients */
249static const struct _coeff_div coeff_div[] = {
250 /* 48k */
251 {12288000, 48000, 256, 0x0, 0x0, 0x0},
252 {18432000, 48000, 384, 0x0, 0x1, 0x0},
253 {12000000, 48000, 250, 0x0, 0x0, 0x1},
254
255 /* 32k */
256 {12288000, 32000, 384, 0x6, 0x0, 0x0},
257 {18432000, 32000, 576, 0x6, 0x1, 0x0},
258 {12000000, 32000, 375, 0x6, 0x0, 0x1},
259
260 /* 8k */
261 {12288000, 8000, 1536, 0x3, 0x0, 0x0},
262 {18432000, 8000, 2304, 0x3, 0x1, 0x0},
263 {11289600, 8000, 1408, 0xb, 0x0, 0x0},
264 {16934400, 8000, 2112, 0xb, 0x1, 0x0},
265 {12000000, 8000, 1500, 0x3, 0x0, 0x1},
266
267 /* 96k */
268 {12288000, 96000, 128, 0x7, 0x0, 0x0},
269 {18432000, 96000, 192, 0x7, 0x1, 0x0},
270 {12000000, 96000, 125, 0x7, 0x0, 0x1},
271
272 /* 44.1k */
273 {11289600, 44100, 256, 0x8, 0x0, 0x0},
274 {16934400, 44100, 384, 0x8, 0x1, 0x0},
275 {12000000, 44100, 272, 0x8, 0x1, 0x1},
276
277 /* 88.2k */
278 {11289600, 88200, 128, 0xf, 0x0, 0x0},
279 {16934400, 88200, 192, 0xf, 0x1, 0x0},
280 {12000000, 88200, 136, 0xf, 0x1, 0x1},
281};
282
283static inline int get_coeff(int mclk, int rate)
284{
285 int i;
286
287 for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
288 if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
289 return i;
290 }
291 return 0;
292}
293
294static int wm8731_hw_params(struct snd_pcm_substream *substream,
295 struct snd_pcm_hw_params *params)
296{
297 struct snd_soc_pcm_runtime *rtd = substream->private_data;
298 struct snd_soc_device *socdev = rtd->socdev;
299 struct snd_soc_codec *codec = socdev->codec;
300 struct wm8731_priv *wm8731 = codec->private_data;
301 u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;
302 int i = get_coeff(wm8731->sysclk, params_rate(params));
303 u16 srate = (coeff_div[i].sr << 2) |
304 (coeff_div[i].bosr << 1) | coeff_div[i].usb;
305
306 wm8731_write(codec, WM8731_SRATE, srate);
307
308 /* bit size */
309 switch (params_format(params)) {
310 case SNDRV_PCM_FORMAT_S16_LE:
311 break;
312 case SNDRV_PCM_FORMAT_S20_3LE:
313 iface |= 0x0004;
314 break;
315 case SNDRV_PCM_FORMAT_S24_LE:
316 iface |= 0x0008;
317 break;
318 }
319
320 wm8731_write(codec, WM8731_IFACE, iface);
321 return 0;
322}
323
324static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
325{
326 struct snd_soc_pcm_runtime *rtd = substream->private_data;
327 struct snd_soc_device *socdev = rtd->socdev;
328 struct snd_soc_codec *codec = socdev->codec;
329
330 /* set active */
331 wm8731_write(codec, WM8731_ACTIVE, 0x0001);
332
333 return 0;
334}
335
336static void wm8731_shutdown(struct snd_pcm_substream *substream)
337{
338 struct snd_soc_pcm_runtime *rtd = substream->private_data;
339 struct snd_soc_device *socdev = rtd->socdev;
340 struct snd_soc_codec *codec = socdev->codec;
341
342 /* deactivate */
343 if (!codec->active) {
344 udelay(50);
345 wm8731_write(codec, WM8731_ACTIVE, 0x0);
346 }
347}
348
349static int wm8731_mute(struct snd_soc_codec_dai *dai, int mute)
350{
351 struct snd_soc_codec *codec = dai->codec;
352 u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
353
354 if (mute)
355 wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8);
356 else
357 wm8731_write(codec, WM8731_APDIGI, mute_reg);
358 return 0;
359}
360
361static int wm8731_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
362 int clk_id, unsigned int freq, int dir)
363{
364 struct snd_soc_codec *codec = codec_dai->codec;
365 struct wm8731_priv *wm8731 = codec->private_data;
366
367 switch (freq) {
368 case 11289600:
369 case 12000000:
370 case 12288000:
371 case 16934400:
372 case 18432000:
373 wm8731->sysclk = freq;
374 return 0;
375 }
376 return -EINVAL;
377}
378
379
380static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
381 unsigned int fmt)
382{
383 struct snd_soc_codec *codec = codec_dai->codec;
384 u16 iface = 0;
385
386 /* set master/slave audio interface */
387 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
388 case SND_SOC_DAIFMT_CBM_CFM:
389 iface |= 0x0040;
390 break;
391 case SND_SOC_DAIFMT_CBS_CFS:
392 break;
393 default:
394 return -EINVAL;
395 }
396
397 /* interface format */
398 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
399 case SND_SOC_DAIFMT_I2S:
400 iface |= 0x0002;
401 break;
402 case SND_SOC_DAIFMT_RIGHT_J:
403 break;
404 case SND_SOC_DAIFMT_LEFT_J:
405 iface |= 0x0001;
406 break;
407 case SND_SOC_DAIFMT_DSP_A:
408 iface |= 0x0003;
409 break;
410 case SND_SOC_DAIFMT_DSP_B:
411 iface |= 0x0013;
412 break;
413 default:
414 return -EINVAL;
415 }
416
417 /* clock inversion */
418 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
419 case SND_SOC_DAIFMT_NB_NF:
420 break;
421 case SND_SOC_DAIFMT_IB_IF:
422 iface |= 0x0090;
423 break;
424 case SND_SOC_DAIFMT_IB_NF:
425 iface |= 0x0080;
426 break;
427 case SND_SOC_DAIFMT_NB_IF:
428 iface |= 0x0010;
429 break;
430 default:
431 return -EINVAL;
432 }
433
434 /* set iface */
435 wm8731_write(codec, WM8731_IFACE, iface);
436 return 0;
437}
438
439static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
440{
441 u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
442
443 switch (event) {
444 case SNDRV_CTL_POWER_D0: /* full On */
445 /* vref/mid, osc on, dac unmute */
446 wm8731_write(codec, WM8731_PWR, reg);
447 break;
448 case SNDRV_CTL_POWER_D1: /* partial On */
449 case SNDRV_CTL_POWER_D2: /* partial On */
450 break;
451 case SNDRV_CTL_POWER_D3hot: /* Off, with power */
452 /* everything off except vref/vmid, */
453 wm8731_write(codec, WM8731_PWR, reg | 0x0040);
454 break;
455 case SNDRV_CTL_POWER_D3cold: /* Off, without power */
456 /* everything off, dac mute, inactive */
457 wm8731_write(codec, WM8731_ACTIVE, 0x0);
458 wm8731_write(codec, WM8731_PWR, 0xffff);
459 break;
460 }
461 codec->dapm_state = event;
462 return 0;
463}
464
465#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
466 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
467 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
468 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
469 SNDRV_PCM_RATE_96000)
470
471#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
472 SNDRV_PCM_FMTBIT_S24_LE)
473
474struct snd_soc_codec_dai wm8731_dai = {
475 .name = "WM8731",
476 .playback = {
477 .stream_name = "Playback",
478 .channels_min = 1,
479 .channels_max = 2,
480 .rates = WM8731_RATES,
481 .formats = WM8731_FORMATS,},
482 .capture = {
483 .stream_name = "Capture",
484 .channels_min = 1,
485 .channels_max = 2,
486 .rates = WM8731_RATES,
487 .formats = WM8731_FORMATS,},
488 .ops = {
489 .prepare = wm8731_pcm_prepare,
490 .hw_params = wm8731_hw_params,
491 .shutdown = wm8731_shutdown,
492 },
493 .dai_ops = {
494 .digital_mute = wm8731_mute,
495 .set_sysclk = wm8731_set_dai_sysclk,
496 .set_fmt = wm8731_set_dai_fmt,
497 }
498};
499EXPORT_SYMBOL_GPL(wm8731_dai);
500
501static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
502{
503 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
504 struct snd_soc_codec *codec = socdev->codec;
505
506 wm8731_write(codec, WM8731_ACTIVE, 0x0);
507 wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
508 return 0;
509}
510
511static int wm8731_resume(struct platform_device *pdev)
512{
513 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
514 struct snd_soc_codec *codec = socdev->codec;
515 int i;
516 u8 data[2];
517 u16 *cache = codec->reg_cache;
518
519 /* Sync reg_cache with the hardware */
520 for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
521 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
522 data[1] = cache[i] & 0x00ff;
523 codec->hw_write(codec->control_data, data, 2);
524 }
525 wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
526 wm8731_dapm_event(codec, codec->suspend_dapm_state);
527 return 0;
528}
529
530/*
531 * initialise the WM8731 driver
532 * register the mixer and dsp interfaces with the kernel
533 */
534static int wm8731_init(struct snd_soc_device *socdev)
535{
536 struct snd_soc_codec *codec = socdev->codec;
537 int reg, ret = 0;
538
539 codec->name = "WM8731";
540 codec->owner = THIS_MODULE;
541 codec->read = wm8731_read_reg_cache;
542 codec->write = wm8731_write;
543 codec->dapm_event = wm8731_dapm_event;
544 codec->dai = &wm8731_dai;
545 codec->num_dai = 1;
546 codec->reg_cache_size = sizeof(wm8731_reg);
547 codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
548 if (codec->reg_cache == NULL)
549 return -ENOMEM;
550
551 wm8731_reset(codec);
552
553 /* register pcms */
554 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
555 if (ret < 0) {
556 printk(KERN_ERR "wm8731: failed to create pcms\n");
557 goto pcm_err;
558 }
559
560 /* power on device */
561 wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
562
563 /* set the update bits */
564 reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
565 wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100);
566 reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
567 wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100);
568 reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
569 wm8731_write(codec, WM8731_LINVOL, reg | 0x0100);
570 reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
571 wm8731_write(codec, WM8731_RINVOL, reg | 0x0100);
572
573 wm8731_add_controls(codec);
574 wm8731_add_widgets(codec);
575 ret = snd_soc_register_card(socdev);
576 if (ret < 0) {
577 printk(KERN_ERR "wm8731: failed to register card\n");
578 goto card_err;
579 }
580
581 return ret;
582
583card_err:
584 snd_soc_free_pcms(socdev);
585 snd_soc_dapm_free(socdev);
586pcm_err:
587 kfree(codec->reg_cache);
588 return ret;
589}
590
591static struct snd_soc_device *wm8731_socdev;
592
593#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
594
595/*
596 * WM8731 2 wire address is determined by GPIO5
597 * state during powerup.
598 * low = 0x1a
599 * high = 0x1b
600 */
601static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
602
603/* Magic definition of all other variables and things */
604I2C_CLIENT_INSMOD;
605
606static struct i2c_driver wm8731_i2c_driver;
607static struct i2c_client client_template;
608
609/* If the i2c layer weren't so broken, we could pass this kind of data
610 around */
611
612static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind)
613{
614 struct snd_soc_device *socdev = wm8731_socdev;
615 struct wm8731_setup_data *setup = socdev->codec_data;
616 struct snd_soc_codec *codec = socdev->codec;
617 struct i2c_client *i2c;
618 int ret;
619
620 if (addr != setup->i2c_address)
621 return -ENODEV;
622
623 client_template.adapter = adap;
624 client_template.addr = addr;
625
626 i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
627 if (i2c == NULL) {
628 kfree(codec);
629 return -ENOMEM;
630 }
631 i2c_set_clientdata(i2c, codec);
632 codec->control_data = i2c;
633
634 ret = i2c_attach_client(i2c);
635 if (ret < 0) {
636 err("failed to attach codec at addr %x\n", addr);
637 goto err;
638 }
639
640 ret = wm8731_init(socdev);
641 if (ret < 0) {
642 err("failed to initialise WM8731\n");
643 goto err;
644 }
645 return ret;
646
647err:
648 kfree(codec);
649 kfree(i2c);
650 return ret;
651}
652
653static int wm8731_i2c_detach(struct i2c_client *client)
654{
655 struct snd_soc_codec* codec = i2c_get_clientdata(client);
656 i2c_detach_client(client);
657 kfree(codec->reg_cache);
658 kfree(client);
659 return 0;
660}
661
662static int wm8731_i2c_attach(struct i2c_adapter *adap)
663{
664 return i2c_probe(adap, &addr_data, wm8731_codec_probe);
665}
666
667/* corgi i2c codec control layer */
668static struct i2c_driver wm8731_i2c_driver = {
669 .driver = {
670 .name = "WM8731 I2C Codec",
671 .owner = THIS_MODULE,
672 },
673 .id = I2C_DRIVERID_WM8731,
674 .attach_adapter = wm8731_i2c_attach,
675 .detach_client = wm8731_i2c_detach,
676 .command = NULL,
677};
678
679static struct i2c_client client_template = {
680 .name = "WM8731",
681 .driver = &wm8731_i2c_driver,
682};
683#endif
684
685static int wm8731_probe(struct platform_device *pdev)
686{
687 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
688 struct wm8731_setup_data *setup;
689 struct snd_soc_codec *codec;
690 struct wm8731_priv *wm8731;
691 int ret = 0;
692
693 info("WM8731 Audio Codec %s", WM8731_VERSION);
694
695 setup = socdev->codec_data;
696 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
697 if (codec == NULL)
698 return -ENOMEM;
699
700 wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
701 if (wm8731 == NULL) {
702 kfree(codec);
703 return -ENOMEM;
704 }
705
706 codec->private_data = wm8731;
707 socdev->codec = codec;
708 mutex_init(&codec->mutex);
709 INIT_LIST_HEAD(&codec->dapm_widgets);
710 INIT_LIST_HEAD(&codec->dapm_paths);
711
712 wm8731_socdev = socdev;
713#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
714 if (setup->i2c_address) {
715 normal_i2c[0] = setup->i2c_address;
716 codec->hw_write = (hw_write_t)i2c_master_send;
717 ret = i2c_add_driver(&wm8731_i2c_driver);
718 if (ret != 0)
719 printk(KERN_ERR "can't add i2c driver");
720 }
721#else
722 /* Add other interfaces here */
723#endif
724 return ret;
725}
726
727/* power down chip */
728static int wm8731_remove(struct platform_device *pdev)
729{
730 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
731 struct snd_soc_codec *codec = socdev->codec;
732
733 if (codec->control_data)
734 wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
735
736 snd_soc_free_pcms(socdev);
737 snd_soc_dapm_free(socdev);
738#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
739 i2c_del_driver(&wm8731_i2c_driver);
740#endif
741 kfree(codec->private_data);
742 kfree(codec);
743
744 return 0;
745}
746
747struct snd_soc_codec_device soc_codec_dev_wm8731 = {
748 .probe = wm8731_probe,
749 .remove = wm8731_remove,
750 .suspend = wm8731_suspend,
751 .resume = wm8731_resume,
752};
753
754EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
755
756MODULE_DESCRIPTION("ASoC WM8731 driver");
757MODULE_AUTHOR("Richard Purdie");
758MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
new file mode 100644
index 000000000000..5bcab6a7afb4
--- /dev/null
+++ b/sound/soc/codecs/wm8731.h
@@ -0,0 +1,44 @@
1/*
2 * wm8731.h -- WM8731 Soc Audio driver
3 *
4 * Copyright 2005 Openedhand Ltd.
5 *
6 * Author: Richard Purdie <richard@openedhand.com>
7 *
8 * Based on wm8753.h
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#ifndef _WM8731_H
16#define _WM8731_H
17
18/* WM8731 register space */
19
20#define WM8731_LINVOL 0x00
21#define WM8731_RINVOL 0x01
22#define WM8731_LOUT1V 0x02
23#define WM8731_ROUT1V 0x03
24#define WM8731_APANA 0x04
25#define WM8731_APDIGI 0x05
26#define WM8731_PWR 0x06
27#define WM8731_IFACE 0x07
28#define WM8731_SRATE 0x08
29#define WM8731_ACTIVE 0x09
30#define WM8731_RESET 0x0f
31
32#define WM8731_CACHEREGNUM 10
33
34#define WM8731_SYSCLK 0
35#define WM8731_DAI 0
36
37struct wm8731_setup_data {
38 unsigned short i2c_address;
39};
40
41extern struct snd_soc_codec_dai wm8731_dai;
42extern struct snd_soc_codec_device soc_codec_dev_wm8731;
43
44#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
new file mode 100644
index 000000000000..7073e8e294fc
--- /dev/null
+++ b/sound/soc/codecs/wm8750.c
@@ -0,0 +1,1049 @@
1/*
2 * wm8750.c -- WM8750 ALSA SoC audio driver
3 *
4 * Copyright 2005 Openedhand Ltd.
5 *
6 * Author: Richard Purdie <richard@openedhand.com>
7 *
8 * Based on WM8753.c
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/init.h>
18#include <linux/delay.h>
19#include <linux/pm.h>
20#include <linux/i2c.h>
21#include <linux/platform_device.h>
22#include <sound/driver.h>
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/pcm_params.h>
26#include <sound/soc.h>
27#include <sound/soc-dapm.h>
28#include <sound/initval.h>
29
30#include "wm8750.h"
31
32#define AUDIO_NAME "WM8750"
33#define WM8750_VERSION "0.12"
34
35/*
36 * Debug
37 */
38
39#define WM8750_DEBUG 0
40
41#ifdef WM8750_DEBUG
42#define dbg(format, arg...) \
43 printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
44#else
45#define dbg(format, arg...) do {} while (0)
46#endif
47#define err(format, arg...) \
48 printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
49#define info(format, arg...) \
50 printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
51#define warn(format, arg...) \
52 printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
53
54/* codec private data */
55struct wm8750_priv {
56 unsigned int sysclk;
57};
58
59/*
60 * wm8750 register cache
61 * We can't read the WM8750 register space when we
62 * are using 2 wire for device control, so we cache them instead.
63 */
64static const u16 wm8750_reg[] = {
65 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
66 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
67 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
68 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
69 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
70 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
71 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
72 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
73 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
74 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
75 0x0079, 0x0079, 0x0079, /* 40 */
76};
77
78/*
79 * read wm8750 register cache
80 */
81static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec,
82 unsigned int reg)
83{
84 u16 *cache = codec->reg_cache;
85 if (reg > WM8750_CACHE_REGNUM)
86 return -1;
87 return cache[reg];
88}
89
90/*
91 * write wm8750 register cache
92 */
93static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec,
94 unsigned int reg, unsigned int value)
95{
96 u16 *cache = codec->reg_cache;
97 if (reg > WM8750_CACHE_REGNUM)
98 return;
99 cache[reg] = value;
100}
101
102static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg,
103 unsigned int value)
104{
105 u8 data[2];
106
107 /* data is
108 * D15..D9 WM8753 register offset
109 * D8...D0 register data
110 */
111 data[0] = (reg << 1) | ((value >> 8) & 0x0001);
112 data[1] = value & 0x00ff;
113
114 wm8750_write_reg_cache (codec, reg, value);
115 if (codec->hw_write(codec->control_data, data, 2) == 2)
116 return 0;
117 else
118 return -EIO;
119}
120
121#define wm8750_reset(c) wm8750_write(c, WM8750_RESET, 0)
122
123/*
124 * WM8750 Controls
125 */
126static const char *wm8750_bass[] = {"Linear Control", "Adaptive Boost"};
127static const char *wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
128static const char *wm8750_treble[] = {"8kHz", "4kHz"};
129static const char *wm8750_3d_lc[] = {"200Hz", "500Hz"};
130static const char *wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"};
131static const char *wm8750_3d_func[] = {"Capture", "Playback"};
132static const char *wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"};
133static const char *wm8750_ng_type[] = {"Constant PGA Gain",
134 "Mute ADC Output"};
135static const char *wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA",
136 "Differential"};
137static const char *wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3",
138 "Differential"};
139static const char *wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut",
140 "ROUT1"};
141static const char *wm8750_diff_sel[] = {"Line 1", "Line 2"};
142static const char *wm8750_adcpol[] = {"Normal", "L Invert", "R Invert",
143 "L + R Invert"};
144static const char *wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
145static const char *wm8750_mono_mux[] = {"Stereo", "Mono (Left)",
146 "Mono (Right)", "Digital Mono"};
147
148static const struct soc_enum wm8750_enum[] = {
149SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass),
150SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter),
151SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble),
152SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc),
153SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc),
154SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func),
155SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func),
156SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type),
157SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux),
158SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux),
159SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */
160SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel),
161SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3),
162SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel),
163SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol),
164SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph),
165SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */
166
167};
168
169static const struct snd_kcontrol_new wm8750_snd_controls[] = {
170
171SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0),
172SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0),
173SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1),
174
175SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8750_LOUT1V,
176 WM8750_ROUT1V, 7, 1, 0),
177SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8750_LOUT2V,
178 WM8750_ROUT2V, 7, 1, 0),
179
180SOC_ENUM("Playback De-emphasis", wm8750_enum[15]),
181
182SOC_ENUM("Capture Polarity", wm8750_enum[14]),
183SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0),
184SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0),
185
186SOC_DOUBLE_R("PCM Volume", WM8750_LDAC, WM8750_RDAC, 0, 255, 0),
187
188SOC_ENUM("Bass Boost", wm8750_enum[0]),
189SOC_ENUM("Bass Filter", wm8750_enum[1]),
190SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1),
191
192SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0),
193SOC_ENUM("Treble Cut-off", wm8750_enum[2]),
194
195SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0),
196SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0),
197SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]),
198SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]),
199SOC_ENUM("3D Mode", wm8750_enum[5]),
200
201SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0),
202SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0),
203SOC_ENUM("ALC Capture Function", wm8750_enum[6]),
204SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0),
205SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0),
206SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0),
207SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0),
208SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0),
209SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]),
210SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0),
211
212SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0),
213SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0),
214
215SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0),
216SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0),
217
218SOC_SINGLE("Right Speaker Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0),
219
220/* Unimplemented */
221/* ADCDAC Bit 0 - ADCHPD */
222/* ADCDAC Bit 4 - HPOR */
223/* ADCTL1 Bit 2,3 - DATSEL */
224/* ADCTL1 Bit 4,5 - DMONOMIX */
225/* ADCTL1 Bit 6,7 - VSEL */
226/* ADCTL2 Bit 2 - LRCM */
227/* ADCTL2 Bit 3 - TRI */
228/* ADCTL3 Bit 5 - HPFLREN */
229/* ADCTL3 Bit 6 - VROI */
230/* ADCTL3 Bit 7,8 - ADCLRM */
231/* ADCIN Bit 4 - LDCM */
232/* ADCIN Bit 5 - RDCM */
233
234SOC_DOUBLE_R("Mic Boost", WM8750_LADCIN, WM8750_RADCIN, 4, 3, 0),
235
236SOC_DOUBLE_R("Bypass Left Playback Volume", WM8750_LOUTM1,
237 WM8750_LOUTM2, 4, 7, 1),
238SOC_DOUBLE_R("Bypass Right Playback Volume", WM8750_ROUTM1,
239 WM8750_ROUTM2, 4, 7, 1),
240SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1,
241 WM8750_MOUTM2, 4, 7, 1),
242
243SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0),
244
245SOC_DOUBLE_R("Headphone Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V,
246 0, 127, 0),
247SOC_DOUBLE_R("Speaker Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V,
248 0, 127, 0),
249
250SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0),
251
252};
253
254/* add non dapm controls */
255static int wm8750_add_controls(struct snd_soc_codec *codec)
256{
257 int err, i;
258
259 for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
260 err = snd_ctl_add(codec->card,
261 snd_soc_cnew(&wm8750_snd_controls[i],codec, NULL));
262 if (err < 0)
263 return err;
264 }
265 return 0;
266}
267
268/*
269 * DAPM Controls
270 */
271
272/* Left Mixer */
273static const struct snd_kcontrol_new wm8750_left_mixer_controls[] = {
274SOC_DAPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0),
275SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0),
276SOC_DAPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0),
277SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0),
278};
279
280/* Right Mixer */
281static const struct snd_kcontrol_new wm8750_right_mixer_controls[] = {
282SOC_DAPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0),
283SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0),
284SOC_DAPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0),
285SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0),
286};
287
288/* Mono Mixer */
289static const struct snd_kcontrol_new wm8750_mono_mixer_controls[] = {
290SOC_DAPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0),
291SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0),
292SOC_DAPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0),
293SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0),
294};
295
296/* Left Line Mux */
297static const struct snd_kcontrol_new wm8750_left_line_controls =
298SOC_DAPM_ENUM("Route", wm8750_enum[8]);
299
300/* Right Line Mux */
301static const struct snd_kcontrol_new wm8750_right_line_controls =
302SOC_DAPM_ENUM("Route", wm8750_enum[9]);
303
304/* Left PGA Mux */
305static const struct snd_kcontrol_new wm8750_left_pga_controls =
306SOC_DAPM_ENUM("Route", wm8750_enum[10]);
307
308/* Right PGA Mux */
309static const struct snd_kcontrol_new wm8750_right_pga_controls =
310SOC_DAPM_ENUM("Route", wm8750_enum[11]);
311
312/* Out 3 Mux */
313static const struct snd_kcontrol_new wm8750_out3_controls =
314SOC_DAPM_ENUM("Route", wm8750_enum[12]);
315
316/* Differential Mux */
317static const struct snd_kcontrol_new wm8750_diffmux_controls =
318SOC_DAPM_ENUM("Route", wm8750_enum[13]);
319
320/* Mono ADC Mux */
321static const struct snd_kcontrol_new wm8750_monomux_controls =
322SOC_DAPM_ENUM("Route", wm8750_enum[16]);
323
324static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
325 SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
326 &wm8750_left_mixer_controls[0],
327 ARRAY_SIZE(wm8750_left_mixer_controls)),
328 SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
329 &wm8750_right_mixer_controls[0],
330 ARRAY_SIZE(wm8750_right_mixer_controls)),
331 SND_SOC_DAPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0,
332 &wm8750_mono_mixer_controls[0],
333 ARRAY_SIZE(wm8750_mono_mixer_controls)),
334
335 SND_SOC_DAPM_PGA("Right Out 2", WM8750_PWR2, 3, 0, NULL, 0),
336 SND_SOC_DAPM_PGA("Left Out 2", WM8750_PWR2, 4, 0, NULL, 0),
337 SND_SOC_DAPM_PGA("Right Out 1", WM8750_PWR2, 5, 0, NULL, 0),
338 SND_SOC_DAPM_PGA("Left Out 1", WM8750_PWR2, 6, 0, NULL, 0),
339 SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0),
340 SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0),
341
342 SND_SOC_DAPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0),
343 SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0),
344 SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0),
345
346 SND_SOC_DAPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0,
347 &wm8750_left_pga_controls),
348 SND_SOC_DAPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0,
349 &wm8750_right_pga_controls),
350 SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
351 &wm8750_left_line_controls),
352 SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
353 &wm8750_right_line_controls),
354
355 SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8750_out3_controls),
356 SND_SOC_DAPM_PGA("Out 3", WM8750_PWR2, 1, 0, NULL, 0),
357 SND_SOC_DAPM_PGA("Mono Out 1", WM8750_PWR2, 2, 0, NULL, 0),
358
359 SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
360 &wm8750_diffmux_controls),
361 SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
362 &wm8750_monomux_controls),
363 SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
364 &wm8750_monomux_controls),
365
366 SND_SOC_DAPM_OUTPUT("LOUT1"),
367 SND_SOC_DAPM_OUTPUT("ROUT1"),
368 SND_SOC_DAPM_OUTPUT("LOUT2"),
369 SND_SOC_DAPM_OUTPUT("ROUT2"),
370 SND_SOC_DAPM_OUTPUT("MONO"),
371 SND_SOC_DAPM_OUTPUT("OUT3"),
372
373 SND_SOC_DAPM_INPUT("LINPUT1"),
374 SND_SOC_DAPM_INPUT("LINPUT2"),
375 SND_SOC_DAPM_INPUT("LINPUT3"),
376 SND_SOC_DAPM_INPUT("RINPUT1"),
377 SND_SOC_DAPM_INPUT("RINPUT2"),
378 SND_SOC_DAPM_INPUT("RINPUT3"),
379};
380
381static const char *audio_map[][3] = {
382 /* left mixer */
383 {"Left Mixer", "Playback Switch", "Left DAC"},
384 {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
385 {"Left Mixer", "Right Playback Switch", "Right DAC"},
386 {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
387
388 /* right mixer */
389 {"Right Mixer", "Left Playback Switch", "Left DAC"},
390 {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
391 {"Right Mixer", "Playback Switch", "Right DAC"},
392 {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
393
394 /* left out 1 */
395 {"Left Out 1", NULL, "Left Mixer"},
396 {"LOUT1", NULL, "Left Out 1"},
397
398 /* left out 2 */
399 {"Left Out 2", NULL, "Left Mixer"},
400 {"LOUT2", NULL, "Left Out 2"},
401
402 /* right out 1 */
403 {"Right Out 1", NULL, "Right Mixer"},
404 {"ROUT1", NULL, "Right Out 1"},
405
406 /* right out 2 */
407 {"Right Out 2", NULL, "Right Mixer"},
408 {"ROUT2", NULL, "Right Out 2"},
409
410 /* mono mixer */
411 {"Mono Mixer", "Left Playback Switch", "Left DAC"},
412 {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
413 {"Mono Mixer", "Right Playback Switch", "Right DAC"},
414 {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
415
416 /* mono out */
417 {"Mono Out 1", NULL, "Mono Mixer"},
418 {"MONO1", NULL, "Mono Out 1"},
419
420 /* out 3 */
421 {"Out3 Mux", "VREF", "VREF"},
422 {"Out3 Mux", "ROUT1 + Vol", "ROUT1"},
423 {"Out3 Mux", "ROUT1", "Right Mixer"},
424 {"Out3 Mux", "MonoOut", "MONO1"},
425 {"Out 3", NULL, "Out3 Mux"},
426 {"OUT3", NULL, "Out 3"},
427
428 /* Left Line Mux */
429 {"Left Line Mux", "Line 1", "LINPUT1"},
430 {"Left Line Mux", "Line 2", "LINPUT2"},
431 {"Left Line Mux", "Line 3", "LINPUT3"},
432 {"Left Line Mux", "PGA", "Left PGA Mux"},
433 {"Left Line Mux", "Differential", "Differential Mux"},
434
435 /* Right Line Mux */
436 {"Right Line Mux", "Line 1", "RINPUT1"},
437 {"Right Line Mux", "Line 2", "RINPUT2"},
438 {"Right Line Mux", "Line 3", "RINPUT3"},
439 {"Right Line Mux", "PGA", "Right PGA Mux"},
440 {"Right Line Mux", "Differential", "Differential Mux"},
441
442 /* Left PGA Mux */
443 {"Left PGA Mux", "Line 1", "LINPUT1"},
444 {"Left PGA Mux", "Line 2", "LINPUT2"},
445 {"Left PGA Mux", "Line 3", "LINPUT3"},
446 {"Left PGA Mux", "Differential", "Differential Mux"},
447
448 /* Right PGA Mux */
449 {"Right PGA Mux", "Line 1", "RINPUT1"},
450 {"Right PGA Mux", "Line 2", "RINPUT2"},
451 {"Right PGA Mux", "Line 3", "RINPUT3"},
452 {"Right PGA Mux", "Differential", "Differential Mux"},
453
454 /* Differential Mux */
455 {"Differential Mux", "Line 1", "LINPUT1"},
456 {"Differential Mux", "Line 1", "RINPUT1"},
457 {"Differential Mux", "Line 2", "LINPUT2"},
458 {"Differential Mux", "Line 2", "RINPUT2"},
459
460 /* Left ADC Mux */
461 {"Left ADC Mux", "Stereo", "Left PGA Mux"},
462 {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
463 {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
464
465 /* Right ADC Mux */
466 {"Right ADC Mux", "Stereo", "Right PGA Mux"},
467 {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
468 {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
469
470 /* ADC */
471 {"Left ADC", NULL, "Left ADC Mux"},
472 {"Right ADC", NULL, "Right ADC Mux"},
473
474 /* terminator */
475 {NULL, NULL, NULL},
476};
477
478static int wm8750_add_widgets(struct snd_soc_codec *codec)
479{
480 int i;
481
482 for(i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) {
483 snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
484 }
485
486 /* set up audio path audio_mapnects */
487 for(i = 0; audio_map[i][0] != NULL; i++) {
488 snd_soc_dapm_connect_input(codec, audio_map[i][0],
489 audio_map[i][1], audio_map[i][2]);
490 }
491
492 snd_soc_dapm_new_widgets(codec);
493 return 0;
494}
495
496struct _coeff_div {
497 u32 mclk;
498 u32 rate;
499 u16 fs;
500 u8 sr:5;
501 u8 usb:1;
502};
503
504/* codec hifi mclk clock divider coefficients */
505static const struct _coeff_div coeff_div[] = {
506 /* 8k */
507 {12288000, 8000, 1536, 0x6, 0x0},
508 {11289600, 8000, 1408, 0x16, 0x0},
509 {18432000, 8000, 2304, 0x7, 0x0},
510 {16934400, 8000, 2112, 0x17, 0x0},
511 {12000000, 8000, 1500, 0x6, 0x1},
512
513 /* 11.025k */
514 {11289600, 11025, 1024, 0x18, 0x0},
515 {16934400, 11025, 1536, 0x19, 0x0},
516 {12000000, 11025, 1088, 0x19, 0x1},
517
518 /* 16k */
519 {12288000, 16000, 768, 0xa, 0x0},
520 {18432000, 16000, 1152, 0xb, 0x0},
521 {12000000, 16000, 750, 0xa, 0x1},
522
523 /* 22.05k */
524 {11289600, 22050, 512, 0x1a, 0x0},
525 {16934400, 22050, 768, 0x1b, 0x0},
526 {12000000, 22050, 544, 0x1b, 0x1},
527
528 /* 32k */
529 {12288000, 32000, 384, 0xc, 0x0},
530 {18432000, 32000, 576, 0xd, 0x0},
531 {12000000, 32000, 375, 0xa, 0x1},
532
533 /* 44.1k */
534 {11289600, 44100, 256, 0x10, 0x0},
535 {16934400, 44100, 384, 0x11, 0x0},
536 {12000000, 44100, 272, 0x11, 0x1},
537
538 /* 48k */
539 {12288000, 48000, 256, 0x0, 0x0},
540 {18432000, 48000, 384, 0x1, 0x0},
541 {12000000, 48000, 250, 0x0, 0x1},
542
543 /* 88.2k */
544 {11289600, 88200, 128, 0x1e, 0x0},
545 {16934400, 88200, 192, 0x1f, 0x0},
546 {12000000, 88200, 136, 0x1f, 0x1},
547
548 /* 96k */
549 {12288000, 96000, 128, 0xe, 0x0},
550 {18432000, 96000, 192, 0xf, 0x0},
551 {12000000, 96000, 125, 0xe, 0x1},
552};
553
554static inline int get_coeff(int mclk, int rate)
555{
556 int i;
557
558 for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
559 if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
560 return i;
561 }
562
563 printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n",
564 mclk, rate);
565 return -EINVAL;
566}
567
568static int wm8750_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
569 int clk_id, unsigned int freq, int dir)
570{
571 struct snd_soc_codec *codec = codec_dai->codec;
572 struct wm8750_priv *wm8750 = codec->private_data;
573
574 switch (freq) {
575 case 11289600:
576 case 12000000:
577 case 12288000:
578 case 16934400:
579 case 18432000:
580 wm8750->sysclk = freq;
581 return 0;
582 }
583 return -EINVAL;
584}
585
586static int wm8750_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
587 unsigned int fmt)
588{
589 struct snd_soc_codec *codec = codec_dai->codec;
590 u16 iface = 0;
591
592 /* set master/slave audio interface */
593 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
594 case SND_SOC_DAIFMT_CBM_CFM:
595 iface = 0x0040;
596 break;
597 case SND_SOC_DAIFMT_CBS_CFS:
598 break;
599 default:
600 return -EINVAL;
601 }
602
603 /* interface format */
604 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
605 case SND_SOC_DAIFMT_I2S:
606 iface |= 0x0002;
607 break;
608 case SND_SOC_DAIFMT_RIGHT_J:
609 break;
610 case SND_SOC_DAIFMT_LEFT_J:
611 iface |= 0x0001;
612 break;
613 case SND_SOC_DAIFMT_DSP_A:
614 iface |= 0x0003;
615 break;
616 case SND_SOC_DAIFMT_DSP_B:
617 iface |= 0x0013;
618 break;
619 default:
620 return -EINVAL;
621 }
622
623 /* clock inversion */
624 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
625 case SND_SOC_DAIFMT_NB_NF:
626 break;
627 case SND_SOC_DAIFMT_IB_IF:
628 iface |= 0x0090;
629 break;
630 case SND_SOC_DAIFMT_IB_NF:
631 iface |= 0x0080;
632 break;
633 case SND_SOC_DAIFMT_NB_IF:
634 iface |= 0x0010;
635 break;
636 default:
637 return -EINVAL;
638 }
639
640 wm8750_write(codec, WM8750_IFACE, iface);
641 return 0;
642}
643
644static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
645 struct snd_pcm_hw_params *params)
646{
647 struct snd_soc_pcm_runtime *rtd = substream->private_data;
648 struct snd_soc_device *socdev = rtd->socdev;
649 struct snd_soc_codec *codec = socdev->codec;
650 struct wm8750_priv *wm8750 = codec->private_data;
651 u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;
652 u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;
653 int coeff = get_coeff(wm8750->sysclk, params_rate(params));
654
655 /* bit size */
656 switch (params_format(params)) {
657 case SNDRV_PCM_FORMAT_S16_LE:
658 break;
659 case SNDRV_PCM_FORMAT_S20_3LE:
660 iface |= 0x0004;
661 break;
662 case SNDRV_PCM_FORMAT_S24_LE:
663 iface |= 0x0008;
664 break;
665 case SNDRV_PCM_FORMAT_S32_LE:
666 iface |= 0x000c;
667 break;
668 }
669
670 /* set iface & srate */
671 wm8750_write(codec, WM8750_IFACE, iface);
672 if (coeff >= 0)
673 wm8750_write(codec, WM8750_SRATE, srate |
674 (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
675
676 return 0;
677}
678
679static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute)
680{
681 struct snd_soc_codec *codec = dai->codec;
682 u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
683
684 if (mute)
685 wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
686 else
687 wm8750_write(codec, WM8750_ADCDAC, mute_reg);
688 return 0;
689}
690
691static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
692{
693 u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
694
695 switch (event) {
696 case SNDRV_CTL_POWER_D0: /* full On */
697 /* set vmid to 50k and unmute dac */
698 wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
699 break;
700 case SNDRV_CTL_POWER_D1: /* partial On */
701 case SNDRV_CTL_POWER_D2: /* partial On */
702 /* set vmid to 5k for quick power up */
703 wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
704 break;
705 case SNDRV_CTL_POWER_D3hot: /* Off, with power */
706 /* mute dac and set vmid to 500k, enable VREF */
707 wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
708 break;
709 case SNDRV_CTL_POWER_D3cold: /* Off, without power */
710 wm8750_write(codec, WM8750_PWR1, 0x0001);
711 break;
712 }
713 codec->dapm_state = event;
714 return 0;
715}
716
717#define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
718 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
719 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
720
721#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
722 SNDRV_PCM_FMTBIT_S24_LE)
723
724struct snd_soc_codec_dai wm8750_dai = {
725 .name = "WM8750",
726 .playback = {
727 .stream_name = "Playback",
728 .channels_min = 1,
729 .channels_max = 2,
730 .rates = WM8750_RATES,
731 .formats = WM8750_FORMATS,},
732 .capture = {
733 .stream_name = "Capture",
734 .channels_min = 1,
735 .channels_max = 2,
736 .rates = WM8750_RATES,
737 .formats = WM8750_FORMATS,},
738 .ops = {
739 .hw_params = wm8750_pcm_hw_params,
740 },
741 .dai_ops = {
742 .digital_mute = wm8750_mute,
743 .set_fmt = wm8750_set_dai_fmt,
744 .set_sysclk = wm8750_set_dai_sysclk,
745 },
746};
747EXPORT_SYMBOL_GPL(wm8750_dai);
748
749static void wm8750_work(struct work_struct *work)
750{
751 struct snd_soc_codec *codec =
752 container_of(work, struct snd_soc_codec, delayed_work.work);
753 wm8750_dapm_event(codec, codec->dapm_state);
754}
755
756static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
757{
758 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
759 struct snd_soc_codec *codec = socdev->codec;
760
761 wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
762 return 0;
763}
764
765static int wm8750_resume(struct platform_device *pdev)
766{
767 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
768 struct snd_soc_codec *codec = socdev->codec;
769 int i;
770 u8 data[2];
771 u16 *cache = codec->reg_cache;
772
773 /* Sync reg_cache with the hardware */
774 for (i = 0; i < ARRAY_SIZE(wm8750_reg); i++) {
775 if (i == WM8750_RESET)
776 continue;
777 data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
778 data[1] = cache[i] & 0x00ff;
779 codec->hw_write(codec->control_data, data, 2);
780 }
781
782 wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
783
784 /* charge wm8750 caps */
785 if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
786 wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
787 codec->dapm_state = SNDRV_CTL_POWER_D0;
788 schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
789 }
790
791 return 0;
792}
793
794/*
795 * initialise the WM8750 driver
796 * register the mixer and dsp interfaces with the kernel
797 */
798static int wm8750_init(struct snd_soc_device *socdev)
799{
800 struct snd_soc_codec *codec = socdev->codec;
801 int reg, ret = 0;
802
803 codec->name = "WM8750";
804 codec->owner = THIS_MODULE;
805 codec->read = wm8750_read_reg_cache;
806 codec->write = wm8750_write;
807 codec->dapm_event = wm8750_dapm_event;
808 codec->dai = &wm8750_dai;
809 codec->num_dai = 1;
810 codec->reg_cache_size = sizeof(wm8750_reg);
811 codec->reg_cache = kmemdup(wm8750_reg, sizeof(wm8750_reg), GFP_KRENEL);
812 if (codec->reg_cache == NULL)
813 return -ENOMEM;
814
815 wm8750_reset(codec);
816
817 /* register pcms */
818 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
819 if (ret < 0) {
820 printk(KERN_ERR "wm8750: failed to create pcms\n");
821 goto pcm_err;
822 }
823
824 /* charge output caps */
825 wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
826 codec->dapm_state = SNDRV_CTL_POWER_D3hot;
827 schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
828
829 /* set the update bits */
830 reg = wm8750_read_reg_cache(codec, WM8750_LDAC);
831 wm8750_write(codec, WM8750_LDAC, reg | 0x0100);
832 reg = wm8750_read_reg_cache(codec, WM8750_RDAC);
833 wm8750_write(codec, WM8750_RDAC, reg | 0x0100);
834 reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);
835 wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);
836 reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);
837 wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);
838 reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);
839 wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);
840 reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);
841 wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);
842 reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);
843 wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);
844 reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
845 wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
846
847 wm8750_add_controls(codec);
848 wm8750_add_widgets(codec);
849 ret = snd_soc_register_card(socdev);
850 if (ret < 0) {
851 printk(KERN_ERR "wm8750: failed to register card\n");
852 goto card_err;
853 }
854 return ret;
855
856card_err:
857 snd_soc_free_pcms(socdev);
858 snd_soc_dapm_free(socdev);
859pcm_err:
860 kfree(codec->reg_cache);
861 return ret;
862}
863
864/* If the i2c layer weren't so broken, we could pass this kind of data
865 around */
866static struct snd_soc_device *wm8750_socdev;
867
868#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
869
870/*
871 * WM8731 2 wire address is determined by GPIO5
872 * state during powerup.
873 * low = 0x1a
874 * high = 0x1b
875 */
876static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
877
878/* Magic definition of all other variables and things */
879I2C_CLIENT_INSMOD;
880
881static struct i2c_driver wm8750_i2c_driver;
882static struct i2c_client client_template;
883
884static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind)
885{
886 struct snd_soc_device *socdev = wm8750_socdev;
887 struct wm8750_setup_data *setup = socdev->codec_data;
888 struct snd_soc_codec *codec = socdev->codec;
889 struct i2c_client *i2c;
890 int ret;
891
892 if (addr != setup->i2c_address)
893 return -ENODEV;
894
895 client_template.adapter = adap;
896 client_template.addr = addr;
897
898 i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
899 if (i2c == NULL) {
900 kfree(codec);
901 return -ENOMEM;
902 }
903 i2c_set_clientdata(i2c, codec);
904 codec->control_data = i2c;
905
906 ret = i2c_attach_client(i2c);
907 if (ret < 0) {
908 err("failed to attach codec at addr %x\n", addr);
909 goto err;
910 }
911
912 ret = wm8750_init(socdev);
913 if (ret < 0) {
914 err("failed to initialise WM8750\n");
915 goto err;
916 }
917 return ret;
918
919err:
920 kfree(codec);
921 kfree(i2c);
922 return ret;
923}
924
925static int wm8750_i2c_detach(struct i2c_client *client)
926{
927 struct snd_soc_codec *codec = i2c_get_clientdata(client);
928 i2c_detach_client(client);
929 kfree(codec->reg_cache);
930 kfree(client);
931 return 0;
932}
933
934static int wm8750_i2c_attach(struct i2c_adapter *adap)
935{
936 return i2c_probe(adap, &addr_data, wm8750_codec_probe);
937}
938
939/* corgi i2c codec control layer */
940static struct i2c_driver wm8750_i2c_driver = {
941 .driver = {
942 .name = "WM8750 I2C Codec",
943 .owner = THIS_MODULE,
944 },
945 .id = I2C_DRIVERID_WM8750,
946 .attach_adapter = wm8750_i2c_attach,
947 .detach_client = wm8750_i2c_detach,
948 .command = NULL,
949};
950
951static struct i2c_client client_template = {
952 .name = "WM8750",
953 .driver = &wm8750_i2c_driver,
954};
955#endif
956
957static int wm8750_probe(struct platform_device *pdev)
958{
959 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
960 struct wm8750_setup_data *setup = socdev->codec_data;
961 struct snd_soc_codec *codec;
962 struct wm8750_priv *wm8750;
963 int ret = 0;
964
965 info("WM8750 Audio Codec %s", WM8750_VERSION);
966 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
967 if (codec == NULL)
968 return -ENOMEM;
969
970 wm8750 = kzalloc(sizeof(struct wm8750_priv), GFP_KERNEL);
971 if (wm8750 == NULL) {
972 kfree(codec);
973 return -ENOMEM;
974 }
975
976 codec->private_data = wm8750;
977 socdev->codec = codec;
978 mutex_init(&codec->mutex);
979 INIT_LIST_HEAD(&codec->dapm_widgets);
980 INIT_LIST_HEAD(&codec->dapm_paths);
981 wm8750_socdev = socdev;
982 INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work);
983
984#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
985 if (setup->i2c_address) {
986 normal_i2c[0] = setup->i2c_address;
987 codec->hw_write = (hw_write_t)i2c_master_send;
988 ret = i2c_add_driver(&wm8750_i2c_driver);
989 if (ret != 0)
990 printk(KERN_ERR "can't add i2c driver");
991 }
992#else
993 /* Add other interfaces here */
994#endif
995
996 return ret;
997}
998
999/*
1000 * This function forces any delayed work to be queued and run.
1001 */
1002static int run_delayed_work(struct delayed_work *dwork)
1003{
1004 int ret;
1005
1006 /* cancel any work waiting to be queued. */
1007 ret = cancel_delayed_work(dwork);
1008
1009 /* if there was any work waiting then we run it now and
1010 * wait for it's completion */
1011 if (ret) {
1012 schedule_delayed_work(dwork, 0);
1013 flush_scheduled_work();
1014 }
1015 return ret;
1016}
1017
1018/* power down chip */
1019static int wm8750_remove(struct platform_device *pdev)
1020{
1021 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
1022 struct snd_soc_codec *codec = socdev->codec;
1023
1024 if (codec->control_data)
1025 wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
1026 run_delayed_work(&codec->delayed_work);
1027 snd_soc_free_pcms(socdev);
1028 snd_soc_dapm_free(socdev);
1029#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
1030 i2c_del_driver(&wm8750_i2c_driver);
1031#endif
1032 kfree(codec->private_data);
1033 kfree(codec);
1034
1035 return 0;
1036}
1037
1038struct snd_soc_codec_device soc_codec_dev_wm8750 = {
1039 .probe = wm8750_probe,
1040 .remove = wm8750_remove,
1041 .suspend = wm8750_suspend,
1042 .resume = wm8750_resume,
1043};
1044
1045EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
1046
1047MODULE_DESCRIPTION("ASoC WM8750 driver");
1048MODULE_AUTHOR("Liam Girdwood");
1049MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
new file mode 100644
index 000000000000..a97a54a6348e
--- /dev/null
+++ b/sound/soc/codecs/wm8750.h
@@ -0,0 +1,67 @@
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 _WM8750_H
15#define _WM8750_H
16
17/* WM8750 register space */
18
19#define WM8750_LINVOL 0x00
20#define WM8750_RINVOL 0x01
21#define WM8750_LOUT1V 0x02
22#define WM8750_ROUT1V 0x03
23#define WM8750_ADCDAC 0x05
24#define WM8750_IFACE 0x07
25#define WM8750_SRATE 0x08
26#define WM8750_LDAC 0x0a
27#define WM8750_RDAC 0x0b
28#define WM8750_BASS 0x0c
29#define WM8750_TREBLE 0x0d
30#define WM8750_RESET 0x0f
31#define WM8750_3D 0x10
32#define WM8750_ALC1 0x11
33#define WM8750_ALC2 0x12
34#define WM8750_ALC3 0x13
35#define WM8750_NGATE 0x14
36#define WM8750_LADC 0x15
37#define WM8750_RADC 0x16
38#define WM8750_ADCTL1 0x17
39#define WM8750_ADCTL2 0x18
40#define WM8750_PWR1 0x19
41#define WM8750_PWR2 0x1a
42#define WM8750_ADCTL3 0x1b
43#define WM8750_ADCIN 0x1f
44#define WM8750_LADCIN 0x20
45#define WM8750_RADCIN 0x21
46#define WM8750_LOUTM1 0x22
47#define WM8750_LOUTM2 0x23
48#define WM8750_ROUTM1 0x24
49#define WM8750_ROUTM2 0x25
50#define WM8750_MOUTM1 0x26
51#define WM8750_MOUTM2 0x27
52#define WM8750_LOUT2V 0x28
53#define WM8750_ROUT2V 0x29
54#define WM8750_MOUTV 0x2a
55
56#define WM8750_CACHE_REGNUM 0x2a
57
58#define WM8750_SYSCLK 0
59
60struct wm8750_setup_data {
61 unsigned short i2c_address;
62};
63
64extern struct snd_soc_codec_dai wm8750_dai;
65extern struct snd_soc_codec_device soc_codec_dev_wm8750;
66
67#endif
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
new file mode 100644
index 000000000000..92a64871bcd0
--- /dev/null
+++ b/sound/soc/codecs/wm9712.c
@@ -0,0 +1,771 @@
1/*
2 * wm9712.c -- ALSA Soc WM9712 codec support
3 *
4 * Copyright 2006 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood
6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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 * Revision history
14 * 4th Feb 2006 Initial version.
15 */
16
17#include <linux/init.h>
18#include <linux/module.h>
19#include <linux/version.h>
20#include <linux/kernel.h>
21#include <linux/device.h>
22#include <sound/driver.h>
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/ac97_codec.h>
26#include <sound/initval.h>
27#include <sound/soc.h>
28#include <sound/soc-dapm.h>
29
30#define WM9712_VERSION "0.4"
31
32static unsigned int ac97_read(struct snd_soc_codec *codec,
33 unsigned int reg);
34static int ac97_write(struct snd_soc_codec *codec,
35 unsigned int reg, unsigned int val);
36
37/*
38 * WM9712 register cache
39 */
40static const u16 wm9712_reg[] = {
41 0x6174, 0x8000, 0x8000, 0x8000, // 6
42 0xf0f0, 0xaaa0, 0xc008, 0x6808, // e
43 0xe808, 0xaaa0, 0xad00, 0x8000, // 16
44 0xe808, 0x3000, 0x8000, 0x0000, // 1e
45 0x0000, 0x0000, 0x0000, 0x000f, // 26
46 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
47 0x0000, 0xbb80, 0x0000, 0x0000, // 36
48 0x0000, 0x2000, 0x0000, 0x0000, // 3e
49 0x0000, 0x0000, 0x0000, 0x0000, // 46
50 0x0000, 0x0000, 0xf83e, 0xffff, // 4e
51 0x0000, 0x0000, 0x0000, 0xf83e, // 56
52 0x0008, 0x0000, 0x0000, 0x0000, // 5e
53 0xb032, 0x3e00, 0x0000, 0x0000, // 66
54 0x0000, 0x0000, 0x0000, 0x0000, // 6e
55 0x0000, 0x0000, 0x0000, 0x0006, // 76
56 0x0001, 0x0000, 0x574d, 0x4c12, // 7e
57 0x0000, 0x0000 // virtual hp mixers
58};
59
60/* virtual HP mixers regs */
61#define HPL_MIXER 0x80
62#define HPR_MIXER 0x82
63
64static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
65static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
66static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right",
67 "Mono"};
68static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"};
69static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
70static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"};
71static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
72static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2",
73 "Stereo"};
74static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer",
75 "Line", "Headphone Mixer", "Phone Mixer", "Phone"};
76static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"};
77static const char *wm9712_diff_sel[] = {"Mic", "Line"};
78
79static const struct soc_enum wm9712_enum[] = {
80SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select),
81SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux),
82SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src),
83SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src),
84SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc),
85SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base),
86SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain),
87SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic),
88SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel),
89SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel),
90SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type),
91SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel),
92};
93
94static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
95SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
96SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
97SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
98SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE,15, 1, 1),
99
100SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
101SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
102SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
103SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
104SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0),
105
106SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
107SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
108SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
109SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
110SOC_ENUM("ALC Function", wm9712_enum[0]),
111SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
112SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
113SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
114SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
115SOC_ENUM("ALC NG Type", wm9712_enum[10]),
116SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
117
118SOC_SINGLE("Mic Headphone Volume", AC97_VIDEO, 12, 7, 1),
119SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
120
121SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
122SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1),
123SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
124
125SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
126SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1),
127SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1),
128
129SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
130SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
131SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
132
133SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0),
134SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
135
136SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
137SOC_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1),
138
139SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
140SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
141SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0),
142
143SOC_ENUM("Bass Control", wm9712_enum[5]),
144SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
145SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
146SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
147SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0),
148SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0),
149
150SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
151SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
152SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
153SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
154
155SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
156SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
157SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
158};
159
160/* add non dapm controls */
161static int wm9712_add_controls(struct snd_soc_codec *codec)
162{
163 int err, i;
164
165 for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
166 err = snd_ctl_add(codec->card,
167 snd_soc_cnew(&wm9712_snd_ac97_controls[i],codec, NULL));
168 if (err < 0)
169 return err;
170 }
171 return 0;
172}
173
174/* We have to create a fake left and right HP mixers because
175 * the codec only has a single control that is shared by both channels.
176 * This makes it impossible to determine the audio path.
177 */
178static int mixer_event (struct snd_soc_dapm_widget *w, int event)
179{
180 u16 l, r, beep, line, phone, mic, pcm, aux;
181
182 l = ac97_read(w->codec, HPL_MIXER);
183 r = ac97_read(w->codec, HPR_MIXER);
184 beep = ac97_read(w->codec, AC97_PC_BEEP);
185 mic = ac97_read(w->codec, AC97_VIDEO);
186 phone = ac97_read(w->codec, AC97_PHONE);
187 line = ac97_read(w->codec, AC97_LINE);
188 pcm = ac97_read(w->codec, AC97_PCM);
189 aux = ac97_read(w->codec, AC97_CD);
190
191 if (l & 0x1 || r & 0x1)
192 ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
193 else
194 ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
195
196 if (l & 0x2 || r & 0x2)
197 ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
198 else
199 ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
200
201 if (l & 0x4 || r & 0x4)
202 ac97_write(w->codec, AC97_LINE, line & 0x7fff);
203 else
204 ac97_write(w->codec, AC97_LINE, line | 0x8000);
205
206 if (l & 0x8 || r & 0x8)
207 ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
208 else
209 ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
210
211 if (l & 0x10 || r & 0x10)
212 ac97_write(w->codec, AC97_CD, aux & 0x7fff);
213 else
214 ac97_write(w->codec, AC97_CD, aux | 0x8000);
215
216 if (l & 0x20 || r & 0x20)
217 ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
218 else
219 ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
220
221 return 0;
222}
223
224/* Left Headphone Mixers */
225static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
226 SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
227 SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
228 SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
229 SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
230 SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
231 SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
232};
233
234/* Right Headphone Mixers */
235static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
236 SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
237 SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
238 SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
239 SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
240 SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
241 SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
242};
243
244/* Speaker Mixer */
245static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = {
246 SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1),
247 SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1),
248 SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1),
249 SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1),
250 SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1),
251};
252
253/* Phone Mixer */
254static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = {
255 SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1),
256 SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1),
257 SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1),
258 SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1),
259 SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1),
260 SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1),
261};
262
263/* ALC headphone mux */
264static const struct snd_kcontrol_new wm9712_alc_mux_controls =
265SOC_DAPM_ENUM("Route", wm9712_enum[1]);
266
267/* out 3 mux */
268static const struct snd_kcontrol_new wm9712_out3_mux_controls =
269SOC_DAPM_ENUM("Route", wm9712_enum[2]);
270
271/* spk mux */
272static const struct snd_kcontrol_new wm9712_spk_mux_controls =
273SOC_DAPM_ENUM("Route", wm9712_enum[3]);
274
275/* Capture to Phone mux */
276static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls =
277SOC_DAPM_ENUM("Route", wm9712_enum[4]);
278
279/* Capture left select */
280static const struct snd_kcontrol_new wm9712_capture_selectl_controls =
281SOC_DAPM_ENUM("Route", wm9712_enum[8]);
282
283/* Capture right select */
284static const struct snd_kcontrol_new wm9712_capture_selectr_controls =
285SOC_DAPM_ENUM("Route", wm9712_enum[9]);
286
287/* Mic select */
288static const struct snd_kcontrol_new wm9712_mic_src_controls =
289SOC_DAPM_ENUM("Route", wm9712_enum[7]);
290
291/* diff select */
292static const struct snd_kcontrol_new wm9712_diff_sel_controls =
293SOC_DAPM_ENUM("Route", wm9712_enum[11]);
294
295static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = {
296SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0,
297 &wm9712_alc_mux_controls),
298SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0,
299 &wm9712_out3_mux_controls),
300SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
301 &wm9712_spk_mux_controls),
302SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0,
303 &wm9712_capture_phone_mux_controls),
304SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
305 &wm9712_capture_selectl_controls),
306SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
307 &wm9712_capture_selectr_controls),
308SND_SOC_DAPM_MUX("Mic Select Source", SND_SOC_NOPM, 0, 0,
309 &wm9712_mic_src_controls),
310SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
311 &wm9712_diff_sel_controls),
312SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
313SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
314 &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
315 mixer_event, SND_SOC_DAPM_POST_REG),
316SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
317 &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
318 mixer_event, SND_SOC_DAPM_POST_REG),
319SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
320 &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
321SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
322 &wm9712_speaker_mixer_controls[0],
323 ARRAY_SIZE(wm9712_speaker_mixer_controls)),
324SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
325SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1),
326SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1),
327SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0),
328SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1),
329SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1),
330SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0),
331SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0),
332SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
333SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
334SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
335SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
336SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
337SND_SOC_DAPM_OUTPUT("MONOOUT"),
338SND_SOC_DAPM_OUTPUT("HPOUTL"),
339SND_SOC_DAPM_OUTPUT("HPOUTR"),
340SND_SOC_DAPM_OUTPUT("LOUT2"),
341SND_SOC_DAPM_OUTPUT("ROUT2"),
342SND_SOC_DAPM_OUTPUT("OUT3"),
343SND_SOC_DAPM_INPUT("LINEINL"),
344SND_SOC_DAPM_INPUT("LINEINR"),
345SND_SOC_DAPM_INPUT("PHONE"),
346SND_SOC_DAPM_INPUT("PCBEEP"),
347SND_SOC_DAPM_INPUT("MIC1"),
348SND_SOC_DAPM_INPUT("MIC2"),
349};
350
351static const char *audio_map[][3] = {
352 /* virtual mixer - mixes left & right channels for spk and mono */
353 {"AC97 Mixer", NULL, "Left DAC"},
354 {"AC97 Mixer", NULL, "Right DAC"},
355
356 /* Left HP mixer */
357 {"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
358 {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
359 {"Left HP Mixer", "Phone Bypass Switch", "Phone PGA"},
360 {"Left HP Mixer", "Line Bypass Switch", "Line PGA"},
361 {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
362 {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
363 {"Left HP Mixer", NULL, "ALC Sidetone Mux"},
364 //{"Right HP Mixer", NULL, "HP Mixer"},
365
366 /* Right HP mixer */
367 {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
368 {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
369 {"Right HP Mixer", "Phone Bypass Switch", "Phone PGA"},
370 {"Right HP Mixer", "Line Bypass Switch", "Line PGA"},
371 {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
372 {"Right HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
373 {"Right HP Mixer", NULL, "ALC Sidetone Mux"},
374
375 /* speaker mixer */
376 {"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"},
377 {"Speaker Mixer", "Line Bypass Switch", "Line PGA"},
378 {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
379 {"Speaker Mixer", "Phone Bypass Switch", "Phone PGA"},
380 {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
381
382 /* Phone mixer */
383 {"Phone Mixer", "PCBeep Bypass Switch", "PCBEEP"},
384 {"Phone Mixer", "Line Bypass Switch", "Line PGA"},
385 {"Phone Mixer", "Aux Playback Switch", "Aux DAC"},
386 {"Phone Mixer", "PCM Playback Switch", "AC97 Mixer"},
387 {"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"},
388 {"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"},
389
390 /* inputs */
391 {"Line PGA", NULL, "LINEINL"},
392 {"Line PGA", NULL, "LINEINR"},
393 {"Phone PGA", NULL, "PHONE"},
394 {"Mic PGA", NULL, "MIC1"},
395 {"Mic PGA", NULL, "MIC2"},
396
397 /* left capture selector */
398 {"Left Capture Select", "Mic", "MIC1"},
399 {"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
400 {"Left Capture Select", "Line", "LINEINL"},
401 {"Left Capture Select", "Headphone Mixer", "Left HP Mixer"},
402 {"Left Capture Select", "Phone Mixer", "Phone Mixer"},
403 {"Left Capture Select", "Phone", "PHONE"},
404
405 /* right capture selector */
406 {"Right Capture Select", "Mic", "MIC2"},
407 {"Right Capture Select", "Speaker Mixer", "Speaker Mixer"},
408 {"Right Capture Select", "Line", "LINEINR"},
409 {"Right Capture Select", "Headphone Mixer", "Right HP Mixer"},
410 {"Right Capture Select", "Phone Mixer", "Phone Mixer"},
411 {"Right Capture Select", "Phone", "PHONE"},
412
413 /* ALC Sidetone */
414 {"ALC Sidetone Mux", "Stereo", "Left Capture Select"},
415 {"ALC Sidetone Mux", "Stereo", "Right Capture Select"},
416 {"ALC Sidetone Mux", "Left", "Left Capture Select"},
417 {"ALC Sidetone Mux", "Right", "Right Capture Select"},
418
419 /* ADC's */
420 {"Left ADC", NULL, "Left Capture Select"},
421 {"Right ADC", NULL, "Right Capture Select"},
422
423 /* outputs */
424 {"MONOOUT", NULL, "Phone Mixer"},
425 {"HPOUTL", NULL, "Headphone PGA"},
426 {"Headphone PGA", NULL, "Left HP Mixer"},
427 {"HPOUTR", NULL, "Headphone PGA"},
428 {"Headphone PGA", NULL, "Right HP Mixer"},
429
430 /* mono hp mixer */
431 {"Mono HP Mixer", NULL, "Left HP Mixer"},
432 {"Mono HP Mixer", NULL, "Right HP Mixer"},
433
434 /* Out3 Mux */
435 {"Out3 Mux", "Left", "Left HP Mixer"},
436 {"Out3 Mux", "Mono", "Phone Mixer"},
437 {"Out3 Mux", "Left + Right", "Mono HP Mixer"},
438 {"Out 3 PGA", NULL, "Out3 Mux"},
439 {"OUT3", NULL, "Out 3 PGA"},
440
441 /* speaker Mux */
442 {"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
443 {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"},
444 {"Speaker PGA", NULL, "Speaker Mux"},
445 {"LOUT2", NULL, "Speaker PGA"},
446 {"ROUT2", NULL, "Speaker PGA"},
447
448 {NULL, NULL, NULL},
449};
450
451static int wm9712_add_widgets(struct snd_soc_codec *codec)
452{
453 int i;
454
455 for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) {
456 snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
457 }
458
459 /* set up audio path audio_mapnects */
460 for(i = 0; audio_map[i][0] != NULL; i++) {
461 snd_soc_dapm_connect_input(codec, audio_map[i][0],
462 audio_map[i][1], audio_map[i][2]);
463 }
464
465 snd_soc_dapm_new_widgets(codec);
466 return 0;
467}
468
469static unsigned int ac97_read(struct snd_soc_codec *codec,
470 unsigned int reg)
471{
472 u16 *cache = codec->reg_cache;
473
474 if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
475 reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
476 reg == AC97_REC_GAIN)
477 return soc_ac97_ops.read(codec->ac97, reg);
478 else {
479 reg = reg >> 1;
480
481 if (reg > (ARRAY_SIZE(wm9712_reg)))
482 return -EIO;
483
484 return cache[reg];
485 }
486}
487
488static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
489 unsigned int val)
490{
491 u16 *cache = codec->reg_cache;
492
493 soc_ac97_ops.write(codec->ac97, reg, val);
494 reg = reg >> 1;
495 if (reg <= (ARRAY_SIZE(wm9712_reg)))
496 cache[reg] = val;
497
498 return 0;
499}
500
501static int ac97_prepare(struct snd_pcm_substream *substream)
502{
503 struct snd_pcm_runtime *runtime = substream->runtime;
504 struct snd_soc_pcm_runtime *rtd = substream->private_data;
505 struct snd_soc_device *socdev = rtd->socdev;
506 struct snd_soc_codec *codec = socdev->codec;
507 int reg;
508 u16 vra;
509
510 vra = ac97_read(codec, AC97_EXTENDED_STATUS);
511 ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
512
513 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
514 reg = AC97_PCM_FRONT_DAC_RATE;
515 else
516 reg = AC97_PCM_LR_ADC_RATE;
517
518 return ac97_write(codec, reg, runtime->rate);
519}
520
521static int ac97_aux_prepare(struct snd_pcm_substream *substream)
522{
523 struct snd_pcm_runtime *runtime = substream->runtime;
524 struct snd_soc_pcm_runtime *rtd = substream->private_data;
525 struct snd_soc_device *socdev = rtd->socdev;
526 struct snd_soc_codec *codec = socdev->codec;
527 u16 vra, xsle;
528
529 vra = ac97_read(codec, AC97_EXTENDED_STATUS);
530 ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
531 xsle = ac97_read(codec, AC97_PCI_SID);
532 ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
533
534 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
535 return -ENODEV;
536
537 return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
538}
539
540#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
541 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
542
543struct snd_soc_codec_dai wm9712_dai[] = {
544{
545 .name = "AC97 HiFi",
546 .playback = {
547 .stream_name = "HiFi Playback",
548 .channels_min = 1,
549 .channels_max = 2,
550 .rates = WM9712_AC97_RATES,
551 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
552 .capture = {
553 .stream_name = "HiFi Capture",
554 .channels_min = 1,
555 .channels_max = 2,
556 .rates = WM9712_AC97_RATES,
557 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
558 .ops = {
559 .prepare = ac97_prepare,},
560},
561{
562 .name = "AC97 Aux",
563 .playback = {
564 .stream_name = "Aux Playback",
565 .channels_min = 1,
566 .channels_max = 1,
567 .rates = WM9712_AC97_RATES,
568 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
569 .ops = {
570 .prepare = ac97_aux_prepare,},
571}
572};
573EXPORT_SYMBOL_GPL(wm9712_dai);
574
575static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
576{
577 u16 reg;
578
579 switch (event) {
580 case SNDRV_CTL_POWER_D0: /* full On */
581 /* liam - maybe enable thermal shutdown */
582 reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff;
583 ac97_write(codec, AC97_EXTENDED_MID, reg);
584 break;
585 case SNDRV_CTL_POWER_D1: /* partial On */
586 case SNDRV_CTL_POWER_D2: /* partial On */
587 break;
588 case SNDRV_CTL_POWER_D3hot: /* Off, with power */
589 /* enable master bias and vmid */
590 reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff;
591 ac97_write(codec, AC97_EXTENDED_MID, reg);
592 ac97_write(codec, AC97_POWERDOWN, 0x0000);
593 break;
594 case SNDRV_CTL_POWER_D3cold: /* Off, without power */
595 /* disable everything including AC link */
596 ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
597 ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
598 ac97_write(codec, AC97_POWERDOWN, 0xffff);
599 break;
600 }
601 codec->dapm_state = event;
602 return 0;
603}
604
605static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
606{
607 if (try_warm && soc_ac97_ops.warm_reset) {
608 soc_ac97_ops.warm_reset(codec->ac97);
609 if (!(ac97_read(codec, 0) & 0x8000))
610 return 1;
611 }
612
613 soc_ac97_ops.reset(codec->ac97);
614 if (ac97_read(codec, 0) & 0x8000)
615 goto err;
616 return 0;
617
618err:
619 printk(KERN_ERR "WM9712 AC97 reset failed\n");
620 return -EIO;
621}
622
623static int wm9712_soc_suspend(struct platform_device *pdev,
624 pm_message_t state)
625{
626 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
627 struct snd_soc_codec *codec = socdev->codec;
628
629 wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
630 return 0;
631}
632
633static int wm9712_soc_resume(struct platform_device *pdev)
634{
635 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
636 struct snd_soc_codec *codec = socdev->codec;
637 int i, ret;
638 u16 *cache = codec->reg_cache;
639
640 ret = wm9712_reset(codec, 1);
641 if (ret < 0){
642 printk(KERN_ERR "could not reset AC97 codec\n");
643 return ret;
644 }
645
646 wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
647
648 if (ret == 0) {
649 /* Sync reg_cache with the hardware after cold reset */
650 for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) {
651 if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
652 (i > 0x58 && i != 0x5c))
653 continue;
654 soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
655 }
656 }
657
658 if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
659 wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
660
661 return ret;
662}
663
664static int wm9712_soc_probe(struct platform_device *pdev)
665{
666 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
667 struct snd_soc_codec *codec;
668 int ret = 0;
669
670 printk(KERN_INFO "WM9711/WM9712 SoC Audio Codec %s\n", WM9712_VERSION);
671
672 socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
673 if (socdev->codec == NULL)
674 return -ENOMEM;
675 codec = socdev->codec;
676 mutex_init(&codec->mutex);
677
678 codec->reg_cache =
679 kzalloc(sizeof(u16) * ARRAY_SIZE(wm9712_reg), GFP_KERNEL);
680 if (codec->reg_cache == NULL) {
681 ret = -ENOMEM;
682 goto cache_err;
683 }
684 memcpy(codec->reg_cache, wm9712_reg, sizeof(u16) * ARRAY_SIZE(wm9712_reg));
685 codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9712_reg);
686 codec->reg_cache_step = 2;
687
688 codec->name = "WM9712";
689 codec->owner = THIS_MODULE;
690 codec->dai = wm9712_dai;
691 codec->num_dai = ARRAY_SIZE(wm9712_dai);
692 codec->write = ac97_write;
693 codec->read = ac97_read;
694 codec->dapm_event = wm9712_dapm_event;
695 INIT_LIST_HEAD(&codec->dapm_widgets);
696 INIT_LIST_HEAD(&codec->dapm_paths);
697
698 ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
699 if (ret < 0) {
700 printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
701 goto codec_err;
702 }
703
704 /* register pcms */
705 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
706 if (ret < 0)
707 goto pcm_err;
708
709 ret = wm9712_reset(codec, 0);
710 if (ret < 0) {
711 printk(KERN_ERR "AC97 link error\n");
712 goto reset_err;
713 }
714
715 /* set alc mux to none */
716 ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
717
718 wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
719 wm9712_add_controls(codec);
720 wm9712_add_widgets(codec);
721 ret = snd_soc_register_card(socdev);
722 if (ret < 0) {
723 printk(KERN_ERR "wm9712: failed to register card\n");
724 goto reset_err;
725 }
726
727 return 0;
728
729reset_err:
730 snd_soc_free_pcms(socdev);
731
732pcm_err:
733 snd_soc_free_ac97_codec(codec);
734
735codec_err:
736 kfree(codec->reg_cache);
737
738cache_err:
739 kfree(socdev->codec);
740 socdev->codec = NULL;
741 return ret;
742}
743
744static int wm9712_soc_remove(struct platform_device *pdev)
745{
746 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
747 struct snd_soc_codec *codec = socdev->codec;
748
749 if (codec == NULL)
750 return 0;
751
752 snd_soc_dapm_free(socdev);
753 snd_soc_free_pcms(socdev);
754 snd_soc_free_ac97_codec(codec);
755 kfree(codec->reg_cache);
756 kfree(codec);
757 return 0;
758}
759
760struct snd_soc_codec_device soc_codec_dev_wm9712 = {
761 .probe = wm9712_soc_probe,
762 .remove = wm9712_soc_remove,
763 .suspend = wm9712_soc_suspend,
764 .resume = wm9712_soc_resume,
765};
766
767EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
768
769MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
770MODULE_AUTHOR("Liam Girdwood");
771MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm9712.h b/sound/soc/codecs/wm9712.h
new file mode 100644
index 000000000000..719105d61e65
--- /dev/null
+++ b/sound/soc/codecs/wm9712.h
@@ -0,0 +1,14 @@
1/*
2 * wm9712.h -- WM9712 Soc Audio driver
3 */
4
5#ifndef _WM9712_H
6#define _WM9712_H
7
8#define WM9712_DAI_AC97_HIFI 0
9#define WM9712_DAI_AC97_AUX 1
10
11extern struct snd_soc_codec_dai wm9712_dai[2];
12extern struct snd_soc_codec_device soc_codec_dev_wm9712;
13
14#endif