xref: /openbmc/linux/sound/soc/codecs/wm8960.c (revision 913d7b4c)
1f2644a2cSMark Brown /*
2f2644a2cSMark Brown  * wm8960.c  --  WM8960 ALSA SoC Audio driver
3f2644a2cSMark Brown  *
4f2644a2cSMark Brown  * Author: Liam Girdwood
5f2644a2cSMark Brown  *
6f2644a2cSMark Brown  * This program is free software; you can redistribute it and/or modify
7f2644a2cSMark Brown  * it under the terms of the GNU General Public License version 2 as
8f2644a2cSMark Brown  * published by the Free Software Foundation.
9f2644a2cSMark Brown  */
10f2644a2cSMark Brown 
11f2644a2cSMark Brown #include <linux/module.h>
12f2644a2cSMark Brown #include <linux/moduleparam.h>
13f2644a2cSMark Brown #include <linux/init.h>
14f2644a2cSMark Brown #include <linux/delay.h>
15f2644a2cSMark Brown #include <linux/pm.h>
16f2644a2cSMark Brown #include <linux/i2c.h>
17f2644a2cSMark Brown #include <linux/platform_device.h>
18f2644a2cSMark Brown #include <sound/core.h>
19f2644a2cSMark Brown #include <sound/pcm.h>
20f2644a2cSMark Brown #include <sound/pcm_params.h>
21f2644a2cSMark Brown #include <sound/soc.h>
22f2644a2cSMark Brown #include <sound/soc-dapm.h>
23f2644a2cSMark Brown #include <sound/initval.h>
24f2644a2cSMark Brown #include <sound/tlv.h>
25b6877a47SMark Brown #include <sound/wm8960.h>
26f2644a2cSMark Brown 
27f2644a2cSMark Brown #include "wm8960.h"
28f2644a2cSMark Brown 
29f2644a2cSMark Brown #define AUDIO_NAME "wm8960"
30f2644a2cSMark Brown 
31f2644a2cSMark Brown struct snd_soc_codec_device soc_codec_dev_wm8960;
32f2644a2cSMark Brown 
33f2644a2cSMark Brown /* R25 - Power 1 */
34913d7b4cSMark Brown #define WM8960_VMID_MASK 0x180
35f2644a2cSMark Brown #define WM8960_VREF      0x40
36f2644a2cSMark Brown 
37913d7b4cSMark Brown /* R26 - Power 2 */
38913d7b4cSMark Brown #define WM8960_PWR2_LOUT1	0x40
39913d7b4cSMark Brown #define WM8960_PWR2_ROUT1	0x20
40913d7b4cSMark Brown #define WM8960_PWR2_OUT3	0x02
41913d7b4cSMark Brown 
42f2644a2cSMark Brown /* R28 - Anti-pop 1 */
43f2644a2cSMark Brown #define WM8960_POBCTRL   0x80
44f2644a2cSMark Brown #define WM8960_BUFDCOPEN 0x10
45f2644a2cSMark Brown #define WM8960_BUFIOEN   0x08
46f2644a2cSMark Brown #define WM8960_SOFT_ST   0x04
47f2644a2cSMark Brown #define WM8960_HPSTBY    0x01
48f2644a2cSMark Brown 
49f2644a2cSMark Brown /* R29 - Anti-pop 2 */
50f2644a2cSMark Brown #define WM8960_DISOP     0x40
51913d7b4cSMark Brown #define WM8960_DRES_MASK 0x30
52f2644a2cSMark Brown 
53f2644a2cSMark Brown /*
54f2644a2cSMark Brown  * wm8960 register cache
55f2644a2cSMark Brown  * We can't read the WM8960 register space when we are
56f2644a2cSMark Brown  * using 2 wire for device control, so we cache them instead.
57f2644a2cSMark Brown  */
58f2644a2cSMark Brown static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
59f2644a2cSMark Brown 	0x0097, 0x0097, 0x0000, 0x0000,
60f2644a2cSMark Brown 	0x0000, 0x0008, 0x0000, 0x000a,
61f2644a2cSMark Brown 	0x01c0, 0x0000, 0x00ff, 0x00ff,
62f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
63f2644a2cSMark Brown 	0x0000, 0x007b, 0x0100, 0x0032,
64f2644a2cSMark Brown 	0x0000, 0x00c3, 0x00c3, 0x01c0,
65f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
66f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
67f2644a2cSMark Brown 	0x0100, 0x0100, 0x0050, 0x0050,
68f2644a2cSMark Brown 	0x0050, 0x0050, 0x0000, 0x0000,
69f2644a2cSMark Brown 	0x0000, 0x0000, 0x0040, 0x0000,
70f2644a2cSMark Brown 	0x0000, 0x0050, 0x0050, 0x0000,
71f2644a2cSMark Brown 	0x0002, 0x0037, 0x004d, 0x0080,
72f2644a2cSMark Brown 	0x0008, 0x0031, 0x0026, 0x00e9,
73f2644a2cSMark Brown };
74f2644a2cSMark Brown 
75f2644a2cSMark Brown struct wm8960_priv {
76f2644a2cSMark Brown 	u16 reg_cache[WM8960_CACHEREGNUM];
77f2644a2cSMark Brown 	struct snd_soc_codec codec;
78913d7b4cSMark Brown 	struct snd_soc_dapm_widget *lout1;
79913d7b4cSMark Brown 	struct snd_soc_dapm_widget *rout1;
80913d7b4cSMark Brown 	struct snd_soc_dapm_widget *out3;
81f2644a2cSMark Brown };
82f2644a2cSMark Brown 
8317a52fd6SMark Brown #define wm8960_reset(c)	snd_soc_write(c, WM8960_RESET, 0)
84f2644a2cSMark Brown 
85f2644a2cSMark Brown /* enumerated controls */
86f2644a2cSMark Brown static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
87f2644a2cSMark Brown static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
88f2644a2cSMark Brown 	"Right Inverted", "Stereo Inversion"};
89f2644a2cSMark Brown static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
90f2644a2cSMark Brown static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
91f2644a2cSMark Brown static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
92f2644a2cSMark Brown static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
93f2644a2cSMark Brown 
94f2644a2cSMark Brown static const struct soc_enum wm8960_enum[] = {
95f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
96f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
97f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
98f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
99f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
100f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
101f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
102f2644a2cSMark Brown };
103f2644a2cSMark Brown 
104f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
105f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
106f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
107f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
108f2644a2cSMark Brown 
109f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = {
110f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
111f2644a2cSMark Brown 		 0, 63, 0, adc_tlv),
112f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
113f2644a2cSMark Brown 	6, 1, 0),
114f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
115f2644a2cSMark Brown 	7, 1, 0),
116f2644a2cSMark Brown 
117f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
118f2644a2cSMark Brown 		 0, 255, 0, dac_tlv),
119f2644a2cSMark Brown 
120f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
121f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
122f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
123f2644a2cSMark Brown 	7, 1, 0),
124f2644a2cSMark Brown 
125f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
126f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
127f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
128f2644a2cSMark Brown 	7, 1, 0),
129f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
130f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
131f2644a2cSMark Brown 
132f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
133f2644a2cSMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[1]),
134f2644a2cSMark Brown SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
135f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
136f2644a2cSMark Brown 
137f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]),
138f2644a2cSMark Brown 
139f2644a2cSMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
140f2644a2cSMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
141f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
142f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
143f2644a2cSMark Brown 
144f2644a2cSMark Brown SOC_ENUM("ALC Function", wm8960_enum[5]),
145f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
146f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
147f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
148f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
149f2644a2cSMark Brown SOC_ENUM("ALC Mode", wm8960_enum[6]),
150f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
151f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
152f2644a2cSMark Brown 
153f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
154f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
155f2644a2cSMark Brown 
156f2644a2cSMark Brown SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
157f2644a2cSMark Brown 	0, 127, 0),
158f2644a2cSMark Brown 
159f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
160f2644a2cSMark Brown 	       WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
161f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
162f2644a2cSMark Brown 	       WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
163f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
164f2644a2cSMark Brown 	       WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
165f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
166f2644a2cSMark Brown 	       WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
167f2644a2cSMark Brown };
168f2644a2cSMark Brown 
169f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = {
170f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
171f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
172f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
173f2644a2cSMark Brown };
174f2644a2cSMark Brown 
175f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = {
176f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
177f2644a2cSMark Brown };
178f2644a2cSMark Brown 
179f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = {
180f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
181f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
182f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
183f2644a2cSMark Brown };
184f2644a2cSMark Brown 
185f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = {
186f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
187f2644a2cSMark Brown };
188f2644a2cSMark Brown 
189f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
190f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
191f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
192f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
193f2644a2cSMark Brown };
194f2644a2cSMark Brown 
195f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
196f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
197f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
198f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
199f2644a2cSMark Brown };
200f2644a2cSMark Brown 
201f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = {
202f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
203f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
204f2644a2cSMark Brown };
205f2644a2cSMark Brown 
206f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
207f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"),
208f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"),
209f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"),
210f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"),
211f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"),
212f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"),
213f2644a2cSMark Brown 
214f2644a2cSMark Brown SND_SOC_DAPM_MICBIAS("MICB", WM8960_POWER1, 1, 0),
215f2644a2cSMark Brown 
216f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
217f2644a2cSMark Brown 		   wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
218f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
219f2644a2cSMark Brown 		   wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
220f2644a2cSMark Brown 
221f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
222f2644a2cSMark Brown 		   wm8960_lin, ARRAY_SIZE(wm8960_lin)),
223f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
224f2644a2cSMark Brown 		   wm8960_rin, ARRAY_SIZE(wm8960_rin)),
225f2644a2cSMark Brown 
226f2644a2cSMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0),
227f2644a2cSMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0),
228f2644a2cSMark Brown 
229f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
230f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
231f2644a2cSMark Brown 
232f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
233f2644a2cSMark Brown 	&wm8960_loutput_mixer[0],
234f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_loutput_mixer)),
235f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
236f2644a2cSMark Brown 	&wm8960_routput_mixer[0],
237f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_routput_mixer)),
238f2644a2cSMark Brown 
239f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
240f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
241f2644a2cSMark Brown 
242f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
243f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
244f2644a2cSMark Brown 
245f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
246f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
247f2644a2cSMark Brown 
248f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"),
249f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"),
250f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"),
251f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"),
252f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"),
253f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"),
254f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"),
255f2644a2cSMark Brown };
256f2644a2cSMark Brown 
257913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = {
258913d7b4cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
259913d7b4cSMark Brown 	&wm8960_mono_out[0],
260913d7b4cSMark Brown 	ARRAY_SIZE(wm8960_mono_out)),
261913d7b4cSMark Brown };
262913d7b4cSMark Brown 
263913d7b4cSMark Brown /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */
264913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = {
265913d7b4cSMark Brown SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0),
266913d7b4cSMark Brown };
267913d7b4cSMark Brown 
268f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = {
269f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
270f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
271f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
272f2644a2cSMark Brown 
273f2644a2cSMark Brown 	{ "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
274f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT1", },  /* Really Boost Switch */
275f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT2" },
276f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT3" },
277f2644a2cSMark Brown 
278f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
279f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
280f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
281f2644a2cSMark Brown 
282f2644a2cSMark Brown 	{ "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
283f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT1", },  /* Really Boost Switch */
284f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT2" },
285f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "LINPUT3" },
286f2644a2cSMark Brown 
287f2644a2cSMark Brown 	{ "Left ADC", NULL, "Left Input Mixer" },
288f2644a2cSMark Brown 	{ "Right ADC", NULL, "Right Input Mixer" },
289f2644a2cSMark Brown 
290f2644a2cSMark Brown 	{ "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
291f2644a2cSMark Brown 	{ "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
292f2644a2cSMark Brown 	{ "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
293f2644a2cSMark Brown 
294f2644a2cSMark Brown 	{ "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
295f2644a2cSMark Brown 	{ "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
296f2644a2cSMark Brown 	{ "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
297f2644a2cSMark Brown 
298f2644a2cSMark Brown 	{ "LOUT1 PGA", NULL, "Left Output Mixer" },
299f2644a2cSMark Brown 	{ "ROUT1 PGA", NULL, "Right Output Mixer" },
300f2644a2cSMark Brown 
301f2644a2cSMark Brown 	{ "HP_L", NULL, "LOUT1 PGA" },
302f2644a2cSMark Brown 	{ "HP_R", NULL, "ROUT1 PGA" },
303f2644a2cSMark Brown 
304f2644a2cSMark Brown 	{ "Left Speaker PGA", NULL, "Left Output Mixer" },
305f2644a2cSMark Brown 	{ "Right Speaker PGA", NULL, "Right Output Mixer" },
306f2644a2cSMark Brown 
307f2644a2cSMark Brown 	{ "Left Speaker Output", NULL, "Left Speaker PGA" },
308f2644a2cSMark Brown 	{ "Right Speaker Output", NULL, "Right Speaker PGA" },
309f2644a2cSMark Brown 
310f2644a2cSMark Brown 	{ "SPK_LN", NULL, "Left Speaker Output" },
311f2644a2cSMark Brown 	{ "SPK_LP", NULL, "Left Speaker Output" },
312f2644a2cSMark Brown 	{ "SPK_RN", NULL, "Right Speaker Output" },
313f2644a2cSMark Brown 	{ "SPK_RP", NULL, "Right Speaker Output" },
314913d7b4cSMark Brown };
315913d7b4cSMark Brown 
316913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_out3[] = {
317913d7b4cSMark Brown 	{ "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
318913d7b4cSMark Brown 	{ "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
319f2644a2cSMark Brown 
320f2644a2cSMark Brown 	{ "OUT3", NULL, "Mono Output Mixer", }
321f2644a2cSMark Brown };
322f2644a2cSMark Brown 
323913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_capless[] = {
324913d7b4cSMark Brown 	{ "HP_L", NULL, "OUT3 VMID" },
325913d7b4cSMark Brown 	{ "HP_R", NULL, "OUT3 VMID" },
326913d7b4cSMark Brown 
327913d7b4cSMark Brown 	{ "OUT3 VMID", NULL, "Left Output Mixer" },
328913d7b4cSMark Brown 	{ "OUT3 VMID", NULL, "Right Output Mixer" },
329913d7b4cSMark Brown };
330913d7b4cSMark Brown 
331f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec)
332f2644a2cSMark Brown {
333913d7b4cSMark Brown 	struct wm8960_data *pdata = codec->dev->platform_data;
334913d7b4cSMark Brown 	struct wm8960_priv *wm8960 = codec->private_data;
335913d7b4cSMark Brown 	struct snd_soc_dapm_widget *w;
336913d7b4cSMark Brown 
337f2644a2cSMark Brown 	snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
338f2644a2cSMark Brown 				  ARRAY_SIZE(wm8960_dapm_widgets));
339f2644a2cSMark Brown 
340f2644a2cSMark Brown 	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
341f2644a2cSMark Brown 
342913d7b4cSMark Brown 	/* In capless mode OUT3 is used to provide VMID for the
343913d7b4cSMark Brown 	 * headphone outputs, otherwise it is used as a mono mixer.
344913d7b4cSMark Brown 	 */
345913d7b4cSMark Brown 	if (pdata && pdata->capless) {
346913d7b4cSMark Brown 		snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_capless,
347913d7b4cSMark Brown 					  ARRAY_SIZE(wm8960_dapm_widgets_capless));
348913d7b4cSMark Brown 
349913d7b4cSMark Brown 		snd_soc_dapm_add_routes(codec, audio_paths_capless,
350913d7b4cSMark Brown 					ARRAY_SIZE(audio_paths_capless));
351913d7b4cSMark Brown 	} else {
352913d7b4cSMark Brown 		snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_out3,
353913d7b4cSMark Brown 					  ARRAY_SIZE(wm8960_dapm_widgets_out3));
354913d7b4cSMark Brown 
355913d7b4cSMark Brown 		snd_soc_dapm_add_routes(codec, audio_paths_out3,
356913d7b4cSMark Brown 					ARRAY_SIZE(audio_paths_out3));
357913d7b4cSMark Brown 	}
358913d7b4cSMark Brown 
359913d7b4cSMark Brown 	/* We need to power up the headphone output stage out of
360913d7b4cSMark Brown 	 * sequence for capless mode.  To save scanning the widget
361913d7b4cSMark Brown 	 * list each time to find the desired power state do so now
362913d7b4cSMark Brown 	 * and save the result.
363913d7b4cSMark Brown 	 */
364913d7b4cSMark Brown 	list_for_each_entry(w, &codec->dapm_widgets, list) {
365913d7b4cSMark Brown 		if (strcmp(w->name, "LOUT1 PGA") == 0)
366913d7b4cSMark Brown 			wm8960->lout1 = w;
367913d7b4cSMark Brown 		if (strcmp(w->name, "ROUT1 PGA") == 0)
368913d7b4cSMark Brown 			wm8960->rout1 = w;
369913d7b4cSMark Brown 		if (strcmp(w->name, "OUT3 VMID") == 0)
370913d7b4cSMark Brown 			wm8960->out3 = w;
371913d7b4cSMark Brown 	}
372913d7b4cSMark Brown 
373f2644a2cSMark Brown 	return 0;
374f2644a2cSMark Brown }
375f2644a2cSMark Brown 
376f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
377f2644a2cSMark Brown 		unsigned int fmt)
378f2644a2cSMark Brown {
379f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
380f2644a2cSMark Brown 	u16 iface = 0;
381f2644a2cSMark Brown 
382f2644a2cSMark Brown 	/* set master/slave audio interface */
383f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
384f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
385f2644a2cSMark Brown 		iface |= 0x0040;
386f2644a2cSMark Brown 		break;
387f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
388f2644a2cSMark Brown 		break;
389f2644a2cSMark Brown 	default:
390f2644a2cSMark Brown 		return -EINVAL;
391f2644a2cSMark Brown 	}
392f2644a2cSMark Brown 
393f2644a2cSMark Brown 	/* interface format */
394f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
395f2644a2cSMark Brown 	case SND_SOC_DAIFMT_I2S:
396f2644a2cSMark Brown 		iface |= 0x0002;
397f2644a2cSMark Brown 		break;
398f2644a2cSMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
399f2644a2cSMark Brown 		break;
400f2644a2cSMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
401f2644a2cSMark Brown 		iface |= 0x0001;
402f2644a2cSMark Brown 		break;
403f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_A:
404f2644a2cSMark Brown 		iface |= 0x0003;
405f2644a2cSMark Brown 		break;
406f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_B:
407f2644a2cSMark Brown 		iface |= 0x0013;
408f2644a2cSMark Brown 		break;
409f2644a2cSMark Brown 	default:
410f2644a2cSMark Brown 		return -EINVAL;
411f2644a2cSMark Brown 	}
412f2644a2cSMark Brown 
413f2644a2cSMark Brown 	/* clock inversion */
414f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
415f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_NF:
416f2644a2cSMark Brown 		break;
417f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_IF:
418f2644a2cSMark Brown 		iface |= 0x0090;
419f2644a2cSMark Brown 		break;
420f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_NF:
421f2644a2cSMark Brown 		iface |= 0x0080;
422f2644a2cSMark Brown 		break;
423f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_IF:
424f2644a2cSMark Brown 		iface |= 0x0010;
425f2644a2cSMark Brown 		break;
426f2644a2cSMark Brown 	default:
427f2644a2cSMark Brown 		return -EINVAL;
428f2644a2cSMark Brown 	}
429f2644a2cSMark Brown 
430f2644a2cSMark Brown 	/* set iface */
43117a52fd6SMark Brown 	snd_soc_write(codec, WM8960_IFACE1, iface);
432f2644a2cSMark Brown 	return 0;
433f2644a2cSMark Brown }
434f2644a2cSMark Brown 
435f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream,
436f2644a2cSMark Brown 			    struct snd_pcm_hw_params *params,
437f2644a2cSMark Brown 			    struct snd_soc_dai *dai)
438f2644a2cSMark Brown {
439f2644a2cSMark Brown 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
440f2644a2cSMark Brown 	struct snd_soc_device *socdev = rtd->socdev;
441f2644a2cSMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
44217a52fd6SMark Brown 	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
443f2644a2cSMark Brown 
444f2644a2cSMark Brown 	/* bit size */
445f2644a2cSMark Brown 	switch (params_format(params)) {
446f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
447f2644a2cSMark Brown 		break;
448f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
449f2644a2cSMark Brown 		iface |= 0x0004;
450f2644a2cSMark Brown 		break;
451f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
452f2644a2cSMark Brown 		iface |= 0x0008;
453f2644a2cSMark Brown 		break;
454f2644a2cSMark Brown 	}
455f2644a2cSMark Brown 
456f2644a2cSMark Brown 	/* set iface */
45717a52fd6SMark Brown 	snd_soc_write(codec, WM8960_IFACE1, iface);
458f2644a2cSMark Brown 	return 0;
459f2644a2cSMark Brown }
460f2644a2cSMark Brown 
461f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute)
462f2644a2cSMark Brown {
463f2644a2cSMark Brown 	struct snd_soc_codec *codec = dai->codec;
46417a52fd6SMark Brown 	u16 mute_reg = snd_soc_read(codec, WM8960_DACCTL1) & 0xfff7;
465f2644a2cSMark Brown 
466f2644a2cSMark Brown 	if (mute)
46717a52fd6SMark Brown 		snd_soc_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
468f2644a2cSMark Brown 	else
46917a52fd6SMark Brown 		snd_soc_write(codec, WM8960_DACCTL1, mute_reg);
470f2644a2cSMark Brown 	return 0;
471f2644a2cSMark Brown }
472f2644a2cSMark Brown 
473913d7b4cSMark Brown static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
474f2644a2cSMark Brown 				      enum snd_soc_bias_level level)
475f2644a2cSMark Brown {
476f2644a2cSMark Brown 	u16 reg;
477f2644a2cSMark Brown 
478f2644a2cSMark Brown 	switch (level) {
479f2644a2cSMark Brown 	case SND_SOC_BIAS_ON:
480f2644a2cSMark Brown 		break;
481f2644a2cSMark Brown 
482f2644a2cSMark Brown 	case SND_SOC_BIAS_PREPARE:
483f2644a2cSMark Brown 		/* Set VMID to 2x50k */
48417a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_POWER1);
485f2644a2cSMark Brown 		reg &= ~0x180;
486f2644a2cSMark Brown 		reg |= 0x80;
48717a52fd6SMark Brown 		snd_soc_write(codec, WM8960_POWER1, reg);
488f2644a2cSMark Brown 		break;
489f2644a2cSMark Brown 
490f2644a2cSMark Brown 	case SND_SOC_BIAS_STANDBY:
491f2644a2cSMark Brown 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
492f2644a2cSMark Brown 			/* Enable anti-pop features */
49317a52fd6SMark Brown 			snd_soc_write(codec, WM8960_APOP1,
494f2644a2cSMark Brown 				      WM8960_POBCTRL | WM8960_SOFT_ST |
495f2644a2cSMark Brown 				      WM8960_BUFDCOPEN | WM8960_BUFIOEN);
496f2644a2cSMark Brown 
497f2644a2cSMark Brown 			/* Enable & ramp VMID at 2x50k */
49817a52fd6SMark Brown 			reg = snd_soc_read(codec, WM8960_POWER1);
499f2644a2cSMark Brown 			reg |= 0x80;
50017a52fd6SMark Brown 			snd_soc_write(codec, WM8960_POWER1, reg);
501f2644a2cSMark Brown 			msleep(100);
502f2644a2cSMark Brown 
503f2644a2cSMark Brown 			/* Enable VREF */
50417a52fd6SMark Brown 			snd_soc_write(codec, WM8960_POWER1, reg | WM8960_VREF);
505f2644a2cSMark Brown 
506f2644a2cSMark Brown 			/* Disable anti-pop features */
50717a52fd6SMark Brown 			snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
508f2644a2cSMark Brown 		}
509f2644a2cSMark Brown 
510f2644a2cSMark Brown 		/* Set VMID to 2x250k */
51117a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_POWER1);
512f2644a2cSMark Brown 		reg &= ~0x180;
513f2644a2cSMark Brown 		reg |= 0x100;
51417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_POWER1, reg);
515f2644a2cSMark Brown 		break;
516f2644a2cSMark Brown 
517f2644a2cSMark Brown 	case SND_SOC_BIAS_OFF:
518f2644a2cSMark Brown 		/* Enable anti-pop features */
51917a52fd6SMark Brown 		snd_soc_write(codec, WM8960_APOP1,
520f2644a2cSMark Brown 			     WM8960_POBCTRL | WM8960_SOFT_ST |
521f2644a2cSMark Brown 			     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
522f2644a2cSMark Brown 
523f2644a2cSMark Brown 		/* Disable VMID and VREF, let them discharge */
52417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_POWER1, 0);
525f2644a2cSMark Brown 		msleep(600);
526913d7b4cSMark Brown 		break;
527913d7b4cSMark Brown 	}
528f2644a2cSMark Brown 
529913d7b4cSMark Brown 	codec->bias_level = level;
530913d7b4cSMark Brown 
531913d7b4cSMark Brown 	return 0;
532913d7b4cSMark Brown }
533913d7b4cSMark Brown 
534913d7b4cSMark Brown static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
535913d7b4cSMark Brown 					 enum snd_soc_bias_level level)
536913d7b4cSMark Brown {
537913d7b4cSMark Brown 	struct wm8960_priv *wm8960 = codec->private_data;
538913d7b4cSMark Brown 	int reg;
539913d7b4cSMark Brown 
540913d7b4cSMark Brown 	switch (level) {
541913d7b4cSMark Brown 	case SND_SOC_BIAS_ON:
542913d7b4cSMark Brown 		break;
543913d7b4cSMark Brown 
544913d7b4cSMark Brown 	case SND_SOC_BIAS_PREPARE:
545913d7b4cSMark Brown 		switch (codec->bias_level) {
546913d7b4cSMark Brown 		case SND_SOC_BIAS_STANDBY:
547913d7b4cSMark Brown 			/* Enable anti pop mode */
548913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
549913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
550913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
551913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
552913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
553913d7b4cSMark Brown 
554913d7b4cSMark Brown 			/* Enable LOUT1, ROUT1 and OUT3 if they're enabled */
555913d7b4cSMark Brown 			reg = 0;
556913d7b4cSMark Brown 			if (wm8960->lout1 && wm8960->lout1->power)
557913d7b4cSMark Brown 				reg |= WM8960_PWR2_LOUT1;
558913d7b4cSMark Brown 			if (wm8960->rout1 && wm8960->rout1->power)
559913d7b4cSMark Brown 				reg |= WM8960_PWR2_ROUT1;
560913d7b4cSMark Brown 			if (wm8960->out3 && wm8960->out3->power)
561913d7b4cSMark Brown 				reg |= WM8960_PWR2_OUT3;
562913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER2,
563913d7b4cSMark Brown 					    WM8960_PWR2_LOUT1 |
564913d7b4cSMark Brown 					    WM8960_PWR2_ROUT1 |
565913d7b4cSMark Brown 					    WM8960_PWR2_OUT3, reg);
566913d7b4cSMark Brown 
567913d7b4cSMark Brown 			/* Enable VMID at 2*50k */
568913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
569913d7b4cSMark Brown 					    WM8960_VMID_MASK, 0x80);
570913d7b4cSMark Brown 
571913d7b4cSMark Brown 			/* Ramp */
572913d7b4cSMark Brown 			msleep(100);
573913d7b4cSMark Brown 
574913d7b4cSMark Brown 			/* Enable VREF */
575913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
576913d7b4cSMark Brown 					    WM8960_VREF, WM8960_VREF);
577913d7b4cSMark Brown 
578913d7b4cSMark Brown 			msleep(100);
579913d7b4cSMark Brown 			break;
580913d7b4cSMark Brown 
581913d7b4cSMark Brown 		case SND_SOC_BIAS_ON:
582913d7b4cSMark Brown 			/* Enable anti-pop mode */
583913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
584913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
585913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
586913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
587913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
588913d7b4cSMark Brown 
589913d7b4cSMark Brown 			/* Disable VMID and VREF */
590913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
591913d7b4cSMark Brown 					    WM8960_VREF | WM8960_VMID_MASK, 0);
592913d7b4cSMark Brown 			break;
593913d7b4cSMark Brown 
594913d7b4cSMark Brown 		default:
595913d7b4cSMark Brown 			break;
596913d7b4cSMark Brown 		}
597913d7b4cSMark Brown 		break;
598913d7b4cSMark Brown 
599913d7b4cSMark Brown 	case SND_SOC_BIAS_STANDBY:
600913d7b4cSMark Brown 		switch (codec->bias_level) {
601913d7b4cSMark Brown 		case SND_SOC_BIAS_PREPARE:
602913d7b4cSMark Brown 			/* Disable HP discharge */
603913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP2,
604913d7b4cSMark Brown 					    WM8960_DISOP | WM8960_DRES_MASK,
605913d7b4cSMark Brown 					    0);
606913d7b4cSMark Brown 
607913d7b4cSMark Brown 			/* Disable anti-pop features */
608913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
609913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
610913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
611913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
612913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
613913d7b4cSMark Brown 			break;
614913d7b4cSMark Brown 
615913d7b4cSMark Brown 		default:
616913d7b4cSMark Brown 			break;
617913d7b4cSMark Brown 		}
618913d7b4cSMark Brown 		break;
619913d7b4cSMark Brown 
620913d7b4cSMark Brown 	case SND_SOC_BIAS_OFF:
621f2644a2cSMark Brown 		break;
622f2644a2cSMark Brown 	}
623f2644a2cSMark Brown 
624f2644a2cSMark Brown 	codec->bias_level = level;
625f2644a2cSMark Brown 
626f2644a2cSMark Brown 	return 0;
627f2644a2cSMark Brown }
628f2644a2cSMark Brown 
629f2644a2cSMark Brown /* PLL divisors */
630f2644a2cSMark Brown struct _pll_div {
631f2644a2cSMark Brown 	u32 pre_div:1;
632f2644a2cSMark Brown 	u32 n:4;
633f2644a2cSMark Brown 	u32 k:24;
634f2644a2cSMark Brown };
635f2644a2cSMark Brown 
636f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10
637f2644a2cSMark Brown  * to allow rounding later */
638f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
639f2644a2cSMark Brown 
640f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target,
641f2644a2cSMark Brown 		       struct _pll_div *pll_div)
642f2644a2cSMark Brown {
643f2644a2cSMark Brown 	unsigned long long Kpart;
644f2644a2cSMark Brown 	unsigned int K, Ndiv, Nmod;
645f2644a2cSMark Brown 
646f2644a2cSMark Brown 	pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
647f2644a2cSMark Brown 
648f2644a2cSMark Brown 	/* Scale up target to PLL operating frequency */
649f2644a2cSMark Brown 	target *= 4;
650f2644a2cSMark Brown 
651f2644a2cSMark Brown 	Ndiv = target / source;
652f2644a2cSMark Brown 	if (Ndiv < 6) {
653f2644a2cSMark Brown 		source >>= 1;
654f2644a2cSMark Brown 		pll_div->pre_div = 1;
655f2644a2cSMark Brown 		Ndiv = target / source;
656f2644a2cSMark Brown 	} else
657f2644a2cSMark Brown 		pll_div->pre_div = 0;
658f2644a2cSMark Brown 
659f2644a2cSMark Brown 	if ((Ndiv < 6) || (Ndiv > 12)) {
660f2644a2cSMark Brown 		pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
661f2644a2cSMark Brown 		return -EINVAL;
662f2644a2cSMark Brown 	}
663f2644a2cSMark Brown 
664f2644a2cSMark Brown 	pll_div->n = Ndiv;
665f2644a2cSMark Brown 	Nmod = target % source;
666f2644a2cSMark Brown 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
667f2644a2cSMark Brown 
668f2644a2cSMark Brown 	do_div(Kpart, source);
669f2644a2cSMark Brown 
670f2644a2cSMark Brown 	K = Kpart & 0xFFFFFFFF;
671f2644a2cSMark Brown 
672f2644a2cSMark Brown 	/* Check if we need to round */
673f2644a2cSMark Brown 	if ((K % 10) >= 5)
674f2644a2cSMark Brown 		K += 5;
675f2644a2cSMark Brown 
676f2644a2cSMark Brown 	/* Move down to proper range now rounding is done */
677f2644a2cSMark Brown 	K /= 10;
678f2644a2cSMark Brown 
679f2644a2cSMark Brown 	pll_div->k = K;
680f2644a2cSMark Brown 
681f2644a2cSMark Brown 	pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
682f2644a2cSMark Brown 		 pll_div->n, pll_div->k, pll_div->pre_div);
683f2644a2cSMark Brown 
684f2644a2cSMark Brown 	return 0;
685f2644a2cSMark Brown }
686f2644a2cSMark Brown 
68785488037SMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
68885488037SMark Brown 		int source, unsigned int freq_in, unsigned int freq_out)
689f2644a2cSMark Brown {
690f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
691f2644a2cSMark Brown 	u16 reg;
692f2644a2cSMark Brown 	static struct _pll_div pll_div;
693f2644a2cSMark Brown 	int ret;
694f2644a2cSMark Brown 
695f2644a2cSMark Brown 	if (freq_in && freq_out) {
696f2644a2cSMark Brown 		ret = pll_factors(freq_in, freq_out, &pll_div);
697f2644a2cSMark Brown 		if (ret != 0)
698f2644a2cSMark Brown 			return ret;
699f2644a2cSMark Brown 	}
700f2644a2cSMark Brown 
701f2644a2cSMark Brown 	/* Disable the PLL: even if we are changing the frequency the
702f2644a2cSMark Brown 	 * PLL needs to be disabled while we do so. */
70317a52fd6SMark Brown 	snd_soc_write(codec, WM8960_CLOCK1,
70417a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_CLOCK1) & ~1);
70517a52fd6SMark Brown 	snd_soc_write(codec, WM8960_POWER2,
70617a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_POWER2) & ~1);
707f2644a2cSMark Brown 
708f2644a2cSMark Brown 	if (!freq_in || !freq_out)
709f2644a2cSMark Brown 		return 0;
710f2644a2cSMark Brown 
71117a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
712f2644a2cSMark Brown 	reg |= pll_div.pre_div << 4;
713f2644a2cSMark Brown 	reg |= pll_div.n;
714f2644a2cSMark Brown 
715f2644a2cSMark Brown 	if (pll_div.k) {
716f2644a2cSMark Brown 		reg |= 0x20;
717f2644a2cSMark Brown 
71817a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
71917a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
72017a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
721f2644a2cSMark Brown 	}
72217a52fd6SMark Brown 	snd_soc_write(codec, WM8960_PLL1, reg);
723f2644a2cSMark Brown 
724f2644a2cSMark Brown 	/* Turn it on */
72517a52fd6SMark Brown 	snd_soc_write(codec, WM8960_POWER2,
72617a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_POWER2) | 1);
727f2644a2cSMark Brown 	msleep(250);
72817a52fd6SMark Brown 	snd_soc_write(codec, WM8960_CLOCK1,
72917a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_CLOCK1) | 1);
730f2644a2cSMark Brown 
731f2644a2cSMark Brown 	return 0;
732f2644a2cSMark Brown }
733f2644a2cSMark Brown 
734f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
735f2644a2cSMark Brown 		int div_id, int div)
736f2644a2cSMark Brown {
737f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
738f2644a2cSMark Brown 	u16 reg;
739f2644a2cSMark Brown 
740f2644a2cSMark Brown 	switch (div_id) {
741f2644a2cSMark Brown 	case WM8960_SYSCLKSEL:
74217a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1fe;
74317a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
744f2644a2cSMark Brown 		break;
745f2644a2cSMark Brown 	case WM8960_SYSCLKDIV:
74617a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
74717a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
748f2644a2cSMark Brown 		break;
749f2644a2cSMark Brown 	case WM8960_DACDIV:
75017a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
75117a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
752f2644a2cSMark Brown 		break;
753f2644a2cSMark Brown 	case WM8960_OPCLKDIV:
75417a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
75517a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL1, reg | div);
756f2644a2cSMark Brown 		break;
757f2644a2cSMark Brown 	case WM8960_DCLKDIV:
75817a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
75917a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK2, reg | div);
760f2644a2cSMark Brown 		break;
761f2644a2cSMark Brown 	case WM8960_TOCLKSEL:
76217a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
76317a52fd6SMark Brown 		snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
764f2644a2cSMark Brown 		break;
765f2644a2cSMark Brown 	default:
766f2644a2cSMark Brown 		return -EINVAL;
767f2644a2cSMark Brown 	}
768f2644a2cSMark Brown 
769f2644a2cSMark Brown 	return 0;
770f2644a2cSMark Brown }
771f2644a2cSMark Brown 
772f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
773f2644a2cSMark Brown 
774f2644a2cSMark Brown #define WM8960_FORMATS \
775f2644a2cSMark Brown 	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
776f2644a2cSMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
777f2644a2cSMark Brown 
778f2644a2cSMark Brown static struct snd_soc_dai_ops wm8960_dai_ops = {
779f2644a2cSMark Brown 	.hw_params = wm8960_hw_params,
780f2644a2cSMark Brown 	.digital_mute = wm8960_mute,
781f2644a2cSMark Brown 	.set_fmt = wm8960_set_dai_fmt,
782f2644a2cSMark Brown 	.set_clkdiv = wm8960_set_dai_clkdiv,
783f2644a2cSMark Brown 	.set_pll = wm8960_set_dai_pll,
784f2644a2cSMark Brown };
785f2644a2cSMark Brown 
786f2644a2cSMark Brown struct snd_soc_dai wm8960_dai = {
787f2644a2cSMark Brown 	.name = "WM8960",
788f2644a2cSMark Brown 	.playback = {
789f2644a2cSMark Brown 		.stream_name = "Playback",
790f2644a2cSMark Brown 		.channels_min = 1,
791f2644a2cSMark Brown 		.channels_max = 2,
792f2644a2cSMark Brown 		.rates = WM8960_RATES,
793f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
794f2644a2cSMark Brown 	.capture = {
795f2644a2cSMark Brown 		.stream_name = "Capture",
796f2644a2cSMark Brown 		.channels_min = 1,
797f2644a2cSMark Brown 		.channels_max = 2,
798f2644a2cSMark Brown 		.rates = WM8960_RATES,
799f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
800f2644a2cSMark Brown 	.ops = &wm8960_dai_ops,
801f2644a2cSMark Brown 	.symmetric_rates = 1,
802f2644a2cSMark Brown };
803f2644a2cSMark Brown EXPORT_SYMBOL_GPL(wm8960_dai);
804f2644a2cSMark Brown 
805f2644a2cSMark Brown static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
806f2644a2cSMark Brown {
807f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
808f2644a2cSMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
809f2644a2cSMark Brown 
810913d7b4cSMark Brown 	codec->set_bias_level(codec, SND_SOC_BIAS_OFF);
811f2644a2cSMark Brown 	return 0;
812f2644a2cSMark Brown }
813f2644a2cSMark Brown 
814f2644a2cSMark Brown static int wm8960_resume(struct platform_device *pdev)
815f2644a2cSMark Brown {
816f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
817f2644a2cSMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
818f2644a2cSMark Brown 	int i;
819f2644a2cSMark Brown 	u8 data[2];
820f2644a2cSMark Brown 	u16 *cache = codec->reg_cache;
821f2644a2cSMark Brown 
822f2644a2cSMark Brown 	/* Sync reg_cache with the hardware */
823f2644a2cSMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
824f2644a2cSMark Brown 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
825f2644a2cSMark Brown 		data[1] = cache[i] & 0x00ff;
826f2644a2cSMark Brown 		codec->hw_write(codec->control_data, data, 2);
827f2644a2cSMark Brown 	}
828f2644a2cSMark Brown 
829913d7b4cSMark Brown 	codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
830913d7b4cSMark Brown 	codec->set_bias_level(codec, codec->suspend_bias_level);
831f2644a2cSMark Brown 	return 0;
832f2644a2cSMark Brown }
833f2644a2cSMark Brown 
834f2644a2cSMark Brown static struct snd_soc_codec *wm8960_codec;
835f2644a2cSMark Brown 
836f2644a2cSMark Brown static int wm8960_probe(struct platform_device *pdev)
837f2644a2cSMark Brown {
838f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
839f2644a2cSMark Brown 	struct snd_soc_codec *codec;
840f2644a2cSMark Brown 	int ret = 0;
841f2644a2cSMark Brown 
842f2644a2cSMark Brown 	if (wm8960_codec == NULL) {
843f2644a2cSMark Brown 		dev_err(&pdev->dev, "Codec device not registered\n");
844f2644a2cSMark Brown 		return -ENODEV;
845f2644a2cSMark Brown 	}
846f2644a2cSMark Brown 
847f2644a2cSMark Brown 	socdev->card->codec = wm8960_codec;
848f2644a2cSMark Brown 	codec = wm8960_codec;
849f2644a2cSMark Brown 
850f2644a2cSMark Brown 	/* register pcms */
851f2644a2cSMark Brown 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
852f2644a2cSMark Brown 	if (ret < 0) {
853f2644a2cSMark Brown 		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
854f2644a2cSMark Brown 		goto pcm_err;
855f2644a2cSMark Brown 	}
856f2644a2cSMark Brown 
857f2644a2cSMark Brown 	snd_soc_add_controls(codec, wm8960_snd_controls,
858f2644a2cSMark Brown 			     ARRAY_SIZE(wm8960_snd_controls));
859f2644a2cSMark Brown 	wm8960_add_widgets(codec);
860f2644a2cSMark Brown 
861f2644a2cSMark Brown 	return ret;
862f2644a2cSMark Brown 
863f2644a2cSMark Brown pcm_err:
864f2644a2cSMark Brown 	return ret;
865f2644a2cSMark Brown }
866f2644a2cSMark Brown 
867f2644a2cSMark Brown /* power down chip */
868f2644a2cSMark Brown static int wm8960_remove(struct platform_device *pdev)
869f2644a2cSMark Brown {
870f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
871f2644a2cSMark Brown 
872f2644a2cSMark Brown 	snd_soc_free_pcms(socdev);
873f2644a2cSMark Brown 	snd_soc_dapm_free(socdev);
874f2644a2cSMark Brown 
875f2644a2cSMark Brown 	return 0;
876f2644a2cSMark Brown }
877f2644a2cSMark Brown 
878f2644a2cSMark Brown struct snd_soc_codec_device soc_codec_dev_wm8960 = {
879f2644a2cSMark Brown 	.probe = 	wm8960_probe,
880f2644a2cSMark Brown 	.remove = 	wm8960_remove,
881f2644a2cSMark Brown 	.suspend = 	wm8960_suspend,
882f2644a2cSMark Brown 	.resume =	wm8960_resume,
883f2644a2cSMark Brown };
884f2644a2cSMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
885f2644a2cSMark Brown 
8867084a42bSMark Brown static int wm8960_register(struct wm8960_priv *wm8960,
8877084a42bSMark Brown 			   enum snd_soc_control_type control)
888f2644a2cSMark Brown {
889f2644a2cSMark Brown 	struct wm8960_data *pdata = wm8960->codec.dev->platform_data;
890f2644a2cSMark Brown 	struct snd_soc_codec *codec = &wm8960->codec;
891f2644a2cSMark Brown 	int ret;
892f2644a2cSMark Brown 	u16 reg;
893f2644a2cSMark Brown 
894f2644a2cSMark Brown 	if (wm8960_codec) {
895f2644a2cSMark Brown 		dev_err(codec->dev, "Another WM8960 is registered\n");
8961a01417eSMark Brown 		ret = -EINVAL;
8971a01417eSMark Brown 		goto err;
898f2644a2cSMark Brown 	}
899f2644a2cSMark Brown 
900913d7b4cSMark Brown 	codec->set_bias_level = wm8960_set_bias_level_out3;
901913d7b4cSMark Brown 
902f2644a2cSMark Brown 	if (!pdata) {
903f2644a2cSMark Brown 		dev_warn(codec->dev, "No platform data supplied\n");
904f2644a2cSMark Brown 	} else {
905f2644a2cSMark Brown 		if (pdata->dres > WM8960_DRES_MAX) {
906f2644a2cSMark Brown 			dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
907f2644a2cSMark Brown 			pdata->dres = 0;
908f2644a2cSMark Brown 		}
909913d7b4cSMark Brown 
910913d7b4cSMark Brown 		if (pdata->capless)
911913d7b4cSMark Brown 			codec->set_bias_level = wm8960_set_bias_level_capless;
912f2644a2cSMark Brown 	}
913f2644a2cSMark Brown 
914f2644a2cSMark Brown 	mutex_init(&codec->mutex);
915f2644a2cSMark Brown 	INIT_LIST_HEAD(&codec->dapm_widgets);
916f2644a2cSMark Brown 	INIT_LIST_HEAD(&codec->dapm_paths);
917f2644a2cSMark Brown 
918f2644a2cSMark Brown 	codec->private_data = wm8960;
919f2644a2cSMark Brown 	codec->name = "WM8960";
920f2644a2cSMark Brown 	codec->owner = THIS_MODULE;
921f2644a2cSMark Brown 	codec->bias_level = SND_SOC_BIAS_OFF;
922f2644a2cSMark Brown 	codec->dai = &wm8960_dai;
923f2644a2cSMark Brown 	codec->num_dai = 1;
924f2644a2cSMark Brown 	codec->reg_cache_size = WM8960_CACHEREGNUM;
925f2644a2cSMark Brown 	codec->reg_cache = &wm8960->reg_cache;
926f2644a2cSMark Brown 
927f2644a2cSMark Brown 	memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
928f2644a2cSMark Brown 
9297084a42bSMark Brown 	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
93017a52fd6SMark Brown 	if (ret < 0) {
93117a52fd6SMark Brown 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
93217a52fd6SMark Brown 		goto err;
93317a52fd6SMark Brown 	}
93417a52fd6SMark Brown 
935f2644a2cSMark Brown 	ret = wm8960_reset(codec);
936f2644a2cSMark Brown 	if (ret < 0) {
937f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to issue reset\n");
9381a01417eSMark Brown 		goto err;
939f2644a2cSMark Brown 	}
940f2644a2cSMark Brown 
941f2644a2cSMark Brown 	wm8960_dai.dev = codec->dev;
942f2644a2cSMark Brown 
943913d7b4cSMark Brown 	codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
944f2644a2cSMark Brown 
945f2644a2cSMark Brown 	/* Latch the update bits */
94617a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LINVOL);
94717a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LINVOL, reg | 0x100);
94817a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_RINVOL);
94917a52fd6SMark Brown 	snd_soc_write(codec, WM8960_RINVOL, reg | 0x100);
95017a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LADC);
95117a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LADC, reg | 0x100);
95217a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_RADC);
95317a52fd6SMark Brown 	snd_soc_write(codec, WM8960_RADC, reg | 0x100);
95417a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LDAC);
95517a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LDAC, reg | 0x100);
95617a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_RDAC);
95717a52fd6SMark Brown 	snd_soc_write(codec, WM8960_RDAC, reg | 0x100);
95817a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LOUT1);
95917a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LOUT1, reg | 0x100);
96017a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_ROUT1);
96117a52fd6SMark Brown 	snd_soc_write(codec, WM8960_ROUT1, reg | 0x100);
96217a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LOUT2);
96317a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LOUT2, reg | 0x100);
96417a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_ROUT2);
96517a52fd6SMark Brown 	snd_soc_write(codec, WM8960_ROUT2, reg | 0x100);
966f2644a2cSMark Brown 
967f2644a2cSMark Brown 	wm8960_codec = codec;
968f2644a2cSMark Brown 
969f2644a2cSMark Brown 	ret = snd_soc_register_codec(codec);
970f2644a2cSMark Brown 	if (ret != 0) {
971f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
9721a01417eSMark Brown 		goto err;
973f2644a2cSMark Brown 	}
974f2644a2cSMark Brown 
975f2644a2cSMark Brown 	ret = snd_soc_register_dai(&wm8960_dai);
976f2644a2cSMark Brown 	if (ret != 0) {
977f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
9781a01417eSMark Brown 		goto err_codec;
979f2644a2cSMark Brown 	}
980f2644a2cSMark Brown 
981f2644a2cSMark Brown 	return 0;
9821a01417eSMark Brown 
9831a01417eSMark Brown err_codec:
9841a01417eSMark Brown 	snd_soc_unregister_codec(codec);
9851a01417eSMark Brown err:
9861a01417eSMark Brown 	kfree(wm8960);
9871a01417eSMark Brown 	return ret;
988f2644a2cSMark Brown }
989f2644a2cSMark Brown 
990f2644a2cSMark Brown static void wm8960_unregister(struct wm8960_priv *wm8960)
991f2644a2cSMark Brown {
992913d7b4cSMark Brown 	wm8960->codec.set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
993f2644a2cSMark Brown 	snd_soc_unregister_dai(&wm8960_dai);
994f2644a2cSMark Brown 	snd_soc_unregister_codec(&wm8960->codec);
995f2644a2cSMark Brown 	kfree(wm8960);
996f2644a2cSMark Brown 	wm8960_codec = NULL;
997f2644a2cSMark Brown }
998f2644a2cSMark Brown 
999f2644a2cSMark Brown static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
1000f2644a2cSMark Brown 				      const struct i2c_device_id *id)
1001f2644a2cSMark Brown {
1002f2644a2cSMark Brown 	struct wm8960_priv *wm8960;
1003f2644a2cSMark Brown 	struct snd_soc_codec *codec;
1004f2644a2cSMark Brown 
1005f2644a2cSMark Brown 	wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);
1006f2644a2cSMark Brown 	if (wm8960 == NULL)
1007f2644a2cSMark Brown 		return -ENOMEM;
1008f2644a2cSMark Brown 
1009f2644a2cSMark Brown 	codec = &wm8960->codec;
1010f2644a2cSMark Brown 
1011f2644a2cSMark Brown 	i2c_set_clientdata(i2c, wm8960);
1012f2644a2cSMark Brown 	codec->control_data = i2c;
1013f2644a2cSMark Brown 
1014f2644a2cSMark Brown 	codec->dev = &i2c->dev;
1015f2644a2cSMark Brown 
10167084a42bSMark Brown 	return wm8960_register(wm8960, SND_SOC_I2C);
1017f2644a2cSMark Brown }
1018f2644a2cSMark Brown 
1019f2644a2cSMark Brown static __devexit int wm8960_i2c_remove(struct i2c_client *client)
1020f2644a2cSMark Brown {
1021f2644a2cSMark Brown 	struct wm8960_priv *wm8960 = i2c_get_clientdata(client);
1022f2644a2cSMark Brown 	wm8960_unregister(wm8960);
1023f2644a2cSMark Brown 	return 0;
1024f2644a2cSMark Brown }
1025f2644a2cSMark Brown 
1026f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = {
1027f2644a2cSMark Brown 	{ "wm8960", 0 },
1028f2644a2cSMark Brown 	{ }
1029f2644a2cSMark Brown };
1030f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
1031f2644a2cSMark Brown 
1032f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = {
1033f2644a2cSMark Brown 	.driver = {
1034a24d62d2SMark Brown 		.name = "wm8960",
1035f2644a2cSMark Brown 		.owner = THIS_MODULE,
1036f2644a2cSMark Brown 	},
1037f2644a2cSMark Brown 	.probe =    wm8960_i2c_probe,
1038f2644a2cSMark Brown 	.remove =   __devexit_p(wm8960_i2c_remove),
1039f2644a2cSMark Brown 	.id_table = wm8960_i2c_id,
1040f2644a2cSMark Brown };
1041f2644a2cSMark Brown 
1042f2644a2cSMark Brown static int __init wm8960_modinit(void)
1043f2644a2cSMark Brown {
1044f2644a2cSMark Brown 	int ret;
1045f2644a2cSMark Brown 
1046f2644a2cSMark Brown 	ret = i2c_add_driver(&wm8960_i2c_driver);
1047f2644a2cSMark Brown 	if (ret != 0) {
1048f2644a2cSMark Brown 		printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
1049f2644a2cSMark Brown 		       ret);
1050f2644a2cSMark Brown 	}
1051f2644a2cSMark Brown 
1052f2644a2cSMark Brown 	return ret;
1053f2644a2cSMark Brown }
1054f2644a2cSMark Brown module_init(wm8960_modinit);
1055f2644a2cSMark Brown 
1056f2644a2cSMark Brown static void __exit wm8960_exit(void)
1057f2644a2cSMark Brown {
1058f2644a2cSMark Brown 	i2c_del_driver(&wm8960_i2c_driver);
1059f2644a2cSMark Brown }
1060f2644a2cSMark Brown module_exit(wm8960_exit);
1061f2644a2cSMark Brown 
1062f2644a2cSMark Brown 
1063f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver");
1064f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood");
1065f2644a2cSMark Brown MODULE_LICENSE("GPL");
1066