xref: /openbmc/linux/sound/soc/codecs/wm8960.c (revision ce6120cc)
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>
185a0e3ad6STejun Heo #include <linux/slab.h>
19f2644a2cSMark Brown #include <sound/core.h>
20f2644a2cSMark Brown #include <sound/pcm.h>
21f2644a2cSMark Brown #include <sound/pcm_params.h>
22f2644a2cSMark Brown #include <sound/soc.h>
23f2644a2cSMark Brown #include <sound/soc-dapm.h>
24f2644a2cSMark Brown #include <sound/initval.h>
25f2644a2cSMark Brown #include <sound/tlv.h>
26b6877a47SMark Brown #include <sound/wm8960.h>
27f2644a2cSMark Brown 
28f2644a2cSMark Brown #include "wm8960.h"
29f2644a2cSMark Brown 
30f2644a2cSMark Brown #define AUDIO_NAME "wm8960"
31f2644a2cSMark Brown 
32f2644a2cSMark Brown /* R25 - Power 1 */
33913d7b4cSMark Brown #define WM8960_VMID_MASK 0x180
34f2644a2cSMark Brown #define WM8960_VREF      0x40
35f2644a2cSMark Brown 
36913d7b4cSMark Brown /* R26 - Power 2 */
37913d7b4cSMark Brown #define WM8960_PWR2_LOUT1	0x40
38913d7b4cSMark Brown #define WM8960_PWR2_ROUT1	0x20
39913d7b4cSMark Brown #define WM8960_PWR2_OUT3	0x02
40913d7b4cSMark Brown 
41f2644a2cSMark Brown /* R28 - Anti-pop 1 */
42f2644a2cSMark Brown #define WM8960_POBCTRL   0x80
43f2644a2cSMark Brown #define WM8960_BUFDCOPEN 0x10
44f2644a2cSMark Brown #define WM8960_BUFIOEN   0x08
45f2644a2cSMark Brown #define WM8960_SOFT_ST   0x04
46f2644a2cSMark Brown #define WM8960_HPSTBY    0x01
47f2644a2cSMark Brown 
48f2644a2cSMark Brown /* R29 - Anti-pop 2 */
49f2644a2cSMark Brown #define WM8960_DISOP     0x40
50913d7b4cSMark Brown #define WM8960_DRES_MASK 0x30
51f2644a2cSMark Brown 
52f2644a2cSMark Brown /*
53f2644a2cSMark Brown  * wm8960 register cache
54f2644a2cSMark Brown  * We can't read the WM8960 register space when we are
55f2644a2cSMark Brown  * using 2 wire for device control, so we cache them instead.
56f2644a2cSMark Brown  */
57f2644a2cSMark Brown static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
58f2644a2cSMark Brown 	0x0097, 0x0097, 0x0000, 0x0000,
59f2644a2cSMark Brown 	0x0000, 0x0008, 0x0000, 0x000a,
60f2644a2cSMark Brown 	0x01c0, 0x0000, 0x00ff, 0x00ff,
61f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
62f2644a2cSMark Brown 	0x0000, 0x007b, 0x0100, 0x0032,
63f2644a2cSMark Brown 	0x0000, 0x00c3, 0x00c3, 0x01c0,
64f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
65f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
66f2644a2cSMark Brown 	0x0100, 0x0100, 0x0050, 0x0050,
67f2644a2cSMark Brown 	0x0050, 0x0050, 0x0000, 0x0000,
68f2644a2cSMark Brown 	0x0000, 0x0000, 0x0040, 0x0000,
69f2644a2cSMark Brown 	0x0000, 0x0050, 0x0050, 0x0000,
70f2644a2cSMark Brown 	0x0002, 0x0037, 0x004d, 0x0080,
71f2644a2cSMark Brown 	0x0008, 0x0031, 0x0026, 0x00e9,
72f2644a2cSMark Brown };
73f2644a2cSMark Brown 
74f2644a2cSMark Brown struct wm8960_priv {
75f2644a2cSMark Brown 	u16 reg_cache[WM8960_CACHEREGNUM];
76f0fba2adSLiam Girdwood 	enum snd_soc_control_type control_type;
77f0fba2adSLiam Girdwood 	void *control_data;
78f0fba2adSLiam Girdwood 	int (*set_bias_level)(struct snd_soc_codec *,
79f0fba2adSLiam Girdwood 			      enum snd_soc_bias_level level);
80913d7b4cSMark Brown 	struct snd_soc_dapm_widget *lout1;
81913d7b4cSMark Brown 	struct snd_soc_dapm_widget *rout1;
82913d7b4cSMark Brown 	struct snd_soc_dapm_widget *out3;
83afd6d36aSMark Brown 	bool deemph;
84afd6d36aSMark Brown 	int playback_fs;
85f2644a2cSMark Brown };
86f2644a2cSMark Brown 
8717a52fd6SMark Brown #define wm8960_reset(c)	snd_soc_write(c, WM8960_RESET, 0)
88f2644a2cSMark Brown 
89f2644a2cSMark Brown /* enumerated controls */
90f2644a2cSMark Brown static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
91f2644a2cSMark Brown 	"Right Inverted", "Stereo Inversion"};
92f2644a2cSMark Brown static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
93f2644a2cSMark Brown static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
94f2644a2cSMark Brown static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
95f2644a2cSMark Brown static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
96f2644a2cSMark Brown 
97f2644a2cSMark Brown static const struct soc_enum wm8960_enum[] = {
98f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
99f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
100f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
101f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
102f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
103f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
104f2644a2cSMark Brown };
105f2644a2cSMark Brown 
106afd6d36aSMark Brown static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
107afd6d36aSMark Brown 
108afd6d36aSMark Brown static int wm8960_set_deemph(struct snd_soc_codec *codec)
109afd6d36aSMark Brown {
110afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
111afd6d36aSMark Brown 	int val, i, best;
112afd6d36aSMark Brown 
113afd6d36aSMark Brown 	/* If we're using deemphasis select the nearest available sample
114afd6d36aSMark Brown 	 * rate.
115afd6d36aSMark Brown 	 */
116afd6d36aSMark Brown 	if (wm8960->deemph) {
117afd6d36aSMark Brown 		best = 1;
118afd6d36aSMark Brown 		for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
119afd6d36aSMark Brown 			if (abs(deemph_settings[i] - wm8960->playback_fs) <
120afd6d36aSMark Brown 			    abs(deemph_settings[best] - wm8960->playback_fs))
121afd6d36aSMark Brown 				best = i;
122afd6d36aSMark Brown 		}
123afd6d36aSMark Brown 
124afd6d36aSMark Brown 		val = best << 1;
125afd6d36aSMark Brown 	} else {
126afd6d36aSMark Brown 		val = 0;
127afd6d36aSMark Brown 	}
128afd6d36aSMark Brown 
129afd6d36aSMark Brown 	dev_dbg(codec->dev, "Set deemphasis %d\n", val);
130afd6d36aSMark Brown 
131afd6d36aSMark Brown 	return snd_soc_update_bits(codec, WM8960_DACCTL1,
132afd6d36aSMark Brown 				   0x6, val);
133afd6d36aSMark Brown }
134afd6d36aSMark Brown 
135afd6d36aSMark Brown static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
136afd6d36aSMark Brown 			     struct snd_ctl_elem_value *ucontrol)
137afd6d36aSMark Brown {
138afd6d36aSMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
139afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
140afd6d36aSMark Brown 
141afd6d36aSMark Brown 	return wm8960->deemph;
142afd6d36aSMark Brown }
143afd6d36aSMark Brown 
144afd6d36aSMark Brown static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
145afd6d36aSMark Brown 			     struct snd_ctl_elem_value *ucontrol)
146afd6d36aSMark Brown {
147afd6d36aSMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
148afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
149afd6d36aSMark Brown 	int deemph = ucontrol->value.enumerated.item[0];
150afd6d36aSMark Brown 
151afd6d36aSMark Brown 	if (deemph > 1)
152afd6d36aSMark Brown 		return -EINVAL;
153afd6d36aSMark Brown 
154afd6d36aSMark Brown 	wm8960->deemph = deemph;
155afd6d36aSMark Brown 
156afd6d36aSMark Brown 	return wm8960_set_deemph(codec);
157afd6d36aSMark Brown }
158afd6d36aSMark Brown 
159f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
160f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
161f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
162f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
163f2644a2cSMark Brown 
164f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = {
165f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
166f2644a2cSMark Brown 		 0, 63, 0, adc_tlv),
167f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
168f2644a2cSMark Brown 	6, 1, 0),
169f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
170f2644a2cSMark Brown 	7, 1, 0),
171f2644a2cSMark Brown 
172f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
173f2644a2cSMark Brown 		 0, 255, 0, dac_tlv),
174f2644a2cSMark Brown 
175f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
176f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
177f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
178f2644a2cSMark Brown 	7, 1, 0),
179f2644a2cSMark Brown 
180f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
181f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
182f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
183f2644a2cSMark Brown 	7, 1, 0),
184f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
185f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
186f2644a2cSMark Brown 
187f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
1884faaa8d9SMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[0]),
189f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
190f2644a2cSMark Brown 
191f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]),
192afd6d36aSMark Brown SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
193afd6d36aSMark Brown 		    wm8960_get_deemph, wm8960_put_deemph),
194f2644a2cSMark Brown 
1954faaa8d9SMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]),
1964faaa8d9SMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]),
197f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
198f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
199f2644a2cSMark Brown 
2004faaa8d9SMark Brown SOC_ENUM("ALC Function", wm8960_enum[4]),
201f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
202f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
203f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
204f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
2054faaa8d9SMark Brown SOC_ENUM("ALC Mode", wm8960_enum[5]),
206f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
207f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
208f2644a2cSMark Brown 
209f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
210f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
211f2644a2cSMark Brown 
212f2644a2cSMark Brown SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
213f2644a2cSMark Brown 	0, 127, 0),
214f2644a2cSMark Brown 
215f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
216f2644a2cSMark Brown 	       WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
217f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
218f2644a2cSMark Brown 	       WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
219f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
220f2644a2cSMark Brown 	       WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
221f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
222f2644a2cSMark Brown 	       WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
223f2644a2cSMark Brown };
224f2644a2cSMark Brown 
225f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = {
226f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
227f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
228f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
229f2644a2cSMark Brown };
230f2644a2cSMark Brown 
231f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = {
232f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
233f2644a2cSMark Brown };
234f2644a2cSMark Brown 
235f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = {
236f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
237f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
238f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
239f2644a2cSMark Brown };
240f2644a2cSMark Brown 
241f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = {
242f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
243f2644a2cSMark Brown };
244f2644a2cSMark Brown 
245f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
246f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
247f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
248f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
249f2644a2cSMark Brown };
250f2644a2cSMark Brown 
251f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
252f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
253f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
254f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
255f2644a2cSMark Brown };
256f2644a2cSMark Brown 
257f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = {
258f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
259f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
260f2644a2cSMark Brown };
261f2644a2cSMark Brown 
262f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
263f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"),
264f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"),
265f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"),
266f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"),
267f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"),
268f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"),
269f2644a2cSMark Brown 
270f2644a2cSMark Brown SND_SOC_DAPM_MICBIAS("MICB", WM8960_POWER1, 1, 0),
271f2644a2cSMark Brown 
272f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
273f2644a2cSMark Brown 		   wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
274f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
275f2644a2cSMark Brown 		   wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
276f2644a2cSMark Brown 
277f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
278f2644a2cSMark Brown 		   wm8960_lin, ARRAY_SIZE(wm8960_lin)),
279f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
280f2644a2cSMark Brown 		   wm8960_rin, ARRAY_SIZE(wm8960_rin)),
281f2644a2cSMark Brown 
282f2644a2cSMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0),
283f2644a2cSMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0),
284f2644a2cSMark Brown 
285f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
286f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
287f2644a2cSMark Brown 
288f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
289f2644a2cSMark Brown 	&wm8960_loutput_mixer[0],
290f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_loutput_mixer)),
291f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
292f2644a2cSMark Brown 	&wm8960_routput_mixer[0],
293f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_routput_mixer)),
294f2644a2cSMark Brown 
295f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
296f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
297f2644a2cSMark Brown 
298f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
299f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
300f2644a2cSMark Brown 
301f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
302f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
303f2644a2cSMark Brown 
304f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"),
305f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"),
306f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"),
307f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"),
308f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"),
309f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"),
310f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"),
311f2644a2cSMark Brown };
312f2644a2cSMark Brown 
313913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = {
314913d7b4cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
315913d7b4cSMark Brown 	&wm8960_mono_out[0],
316913d7b4cSMark Brown 	ARRAY_SIZE(wm8960_mono_out)),
317913d7b4cSMark Brown };
318913d7b4cSMark Brown 
319913d7b4cSMark Brown /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */
320913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = {
321913d7b4cSMark Brown SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0),
322913d7b4cSMark Brown };
323913d7b4cSMark Brown 
324f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = {
325f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
326f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
327f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
328f2644a2cSMark Brown 
329f2644a2cSMark Brown 	{ "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
330f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT1", },  /* Really Boost Switch */
331f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT2" },
332f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT3" },
333f2644a2cSMark Brown 
334f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
335f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
336f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
337f2644a2cSMark Brown 
338f2644a2cSMark Brown 	{ "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
339f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT1", },  /* Really Boost Switch */
340f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT2" },
341f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "LINPUT3" },
342f2644a2cSMark Brown 
343f2644a2cSMark Brown 	{ "Left ADC", NULL, "Left Input Mixer" },
344f2644a2cSMark Brown 	{ "Right ADC", NULL, "Right Input Mixer" },
345f2644a2cSMark Brown 
346f2644a2cSMark Brown 	{ "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
347f2644a2cSMark Brown 	{ "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
348f2644a2cSMark Brown 	{ "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
349f2644a2cSMark Brown 
350f2644a2cSMark Brown 	{ "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
351f2644a2cSMark Brown 	{ "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
352f2644a2cSMark Brown 	{ "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
353f2644a2cSMark Brown 
354f2644a2cSMark Brown 	{ "LOUT1 PGA", NULL, "Left Output Mixer" },
355f2644a2cSMark Brown 	{ "ROUT1 PGA", NULL, "Right Output Mixer" },
356f2644a2cSMark Brown 
357f2644a2cSMark Brown 	{ "HP_L", NULL, "LOUT1 PGA" },
358f2644a2cSMark Brown 	{ "HP_R", NULL, "ROUT1 PGA" },
359f2644a2cSMark Brown 
360f2644a2cSMark Brown 	{ "Left Speaker PGA", NULL, "Left Output Mixer" },
361f2644a2cSMark Brown 	{ "Right Speaker PGA", NULL, "Right Output Mixer" },
362f2644a2cSMark Brown 
363f2644a2cSMark Brown 	{ "Left Speaker Output", NULL, "Left Speaker PGA" },
364f2644a2cSMark Brown 	{ "Right Speaker Output", NULL, "Right Speaker PGA" },
365f2644a2cSMark Brown 
366f2644a2cSMark Brown 	{ "SPK_LN", NULL, "Left Speaker Output" },
367f2644a2cSMark Brown 	{ "SPK_LP", NULL, "Left Speaker Output" },
368f2644a2cSMark Brown 	{ "SPK_RN", NULL, "Right Speaker Output" },
369f2644a2cSMark Brown 	{ "SPK_RP", NULL, "Right Speaker Output" },
370913d7b4cSMark Brown };
371913d7b4cSMark Brown 
372913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_out3[] = {
373913d7b4cSMark Brown 	{ "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
374913d7b4cSMark Brown 	{ "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
375f2644a2cSMark Brown 
376f2644a2cSMark Brown 	{ "OUT3", NULL, "Mono Output Mixer", }
377f2644a2cSMark Brown };
378f2644a2cSMark Brown 
379913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_capless[] = {
380913d7b4cSMark Brown 	{ "HP_L", NULL, "OUT3 VMID" },
381913d7b4cSMark Brown 	{ "HP_R", NULL, "OUT3 VMID" },
382913d7b4cSMark Brown 
383913d7b4cSMark Brown 	{ "OUT3 VMID", NULL, "Left Output Mixer" },
384913d7b4cSMark Brown 	{ "OUT3 VMID", NULL, "Right Output Mixer" },
385913d7b4cSMark Brown };
386913d7b4cSMark Brown 
387f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec)
388f2644a2cSMark Brown {
389913d7b4cSMark Brown 	struct wm8960_data *pdata = codec->dev->platform_data;
390b2c812e2SMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
391ce6120ccSLiam Girdwood 	struct snd_soc_dapm_context *dapm = &codec->dapm;
392913d7b4cSMark Brown 	struct snd_soc_dapm_widget *w;
393913d7b4cSMark Brown 
394ce6120ccSLiam Girdwood 	snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
395f2644a2cSMark Brown 				  ARRAY_SIZE(wm8960_dapm_widgets));
396f2644a2cSMark Brown 
397ce6120ccSLiam Girdwood 	snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
398f2644a2cSMark Brown 
399913d7b4cSMark Brown 	/* In capless mode OUT3 is used to provide VMID for the
400913d7b4cSMark Brown 	 * headphone outputs, otherwise it is used as a mono mixer.
401913d7b4cSMark Brown 	 */
402913d7b4cSMark Brown 	if (pdata && pdata->capless) {
403ce6120ccSLiam Girdwood 		snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless,
404913d7b4cSMark Brown 					  ARRAY_SIZE(wm8960_dapm_widgets_capless));
405913d7b4cSMark Brown 
406ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_paths_capless,
407913d7b4cSMark Brown 					ARRAY_SIZE(audio_paths_capless));
408913d7b4cSMark Brown 	} else {
409ce6120ccSLiam Girdwood 		snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3,
410913d7b4cSMark Brown 					  ARRAY_SIZE(wm8960_dapm_widgets_out3));
411913d7b4cSMark Brown 
412ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_paths_out3,
413913d7b4cSMark Brown 					ARRAY_SIZE(audio_paths_out3));
414913d7b4cSMark Brown 	}
415913d7b4cSMark Brown 
416913d7b4cSMark Brown 	/* We need to power up the headphone output stage out of
417913d7b4cSMark Brown 	 * sequence for capless mode.  To save scanning the widget
418913d7b4cSMark Brown 	 * list each time to find the desired power state do so now
419913d7b4cSMark Brown 	 * and save the result.
420913d7b4cSMark Brown 	 */
421ce6120ccSLiam Girdwood 	list_for_each_entry(w, &codec->dapm.widgets, list) {
422913d7b4cSMark Brown 		if (strcmp(w->name, "LOUT1 PGA") == 0)
423913d7b4cSMark Brown 			wm8960->lout1 = w;
424913d7b4cSMark Brown 		if (strcmp(w->name, "ROUT1 PGA") == 0)
425913d7b4cSMark Brown 			wm8960->rout1 = w;
426913d7b4cSMark Brown 		if (strcmp(w->name, "OUT3 VMID") == 0)
427913d7b4cSMark Brown 			wm8960->out3 = w;
428913d7b4cSMark Brown 	}
429913d7b4cSMark Brown 
430f2644a2cSMark Brown 	return 0;
431f2644a2cSMark Brown }
432f2644a2cSMark Brown 
433f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
434f2644a2cSMark Brown 		unsigned int fmt)
435f2644a2cSMark Brown {
436f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
437f2644a2cSMark Brown 	u16 iface = 0;
438f2644a2cSMark Brown 
439f2644a2cSMark Brown 	/* set master/slave audio interface */
440f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
441f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
442f2644a2cSMark Brown 		iface |= 0x0040;
443f2644a2cSMark Brown 		break;
444f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
445f2644a2cSMark Brown 		break;
446f2644a2cSMark Brown 	default:
447f2644a2cSMark Brown 		return -EINVAL;
448f2644a2cSMark Brown 	}
449f2644a2cSMark Brown 
450f2644a2cSMark Brown 	/* interface format */
451f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
452f2644a2cSMark Brown 	case SND_SOC_DAIFMT_I2S:
453f2644a2cSMark Brown 		iface |= 0x0002;
454f2644a2cSMark Brown 		break;
455f2644a2cSMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
456f2644a2cSMark Brown 		break;
457f2644a2cSMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
458f2644a2cSMark Brown 		iface |= 0x0001;
459f2644a2cSMark Brown 		break;
460f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_A:
461f2644a2cSMark Brown 		iface |= 0x0003;
462f2644a2cSMark Brown 		break;
463f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_B:
464f2644a2cSMark Brown 		iface |= 0x0013;
465f2644a2cSMark Brown 		break;
466f2644a2cSMark Brown 	default:
467f2644a2cSMark Brown 		return -EINVAL;
468f2644a2cSMark Brown 	}
469f2644a2cSMark Brown 
470f2644a2cSMark Brown 	/* clock inversion */
471f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
472f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_NF:
473f2644a2cSMark Brown 		break;
474f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_IF:
475f2644a2cSMark Brown 		iface |= 0x0090;
476f2644a2cSMark Brown 		break;
477f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_NF:
478f2644a2cSMark Brown 		iface |= 0x0080;
479f2644a2cSMark Brown 		break;
480f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_IF:
481f2644a2cSMark Brown 		iface |= 0x0010;
482f2644a2cSMark Brown 		break;
483f2644a2cSMark Brown 	default:
484f2644a2cSMark Brown 		return -EINVAL;
485f2644a2cSMark Brown 	}
486f2644a2cSMark Brown 
487f2644a2cSMark Brown 	/* set iface */
48817a52fd6SMark Brown 	snd_soc_write(codec, WM8960_IFACE1, iface);
489f2644a2cSMark Brown 	return 0;
490f2644a2cSMark Brown }
491f2644a2cSMark Brown 
492db059c0fSMark Brown static struct {
493db059c0fSMark Brown 	int rate;
494db059c0fSMark Brown 	unsigned int val;
495db059c0fSMark Brown } alc_rates[] = {
496db059c0fSMark Brown 	{ 48000, 0 },
497db059c0fSMark Brown 	{ 44100, 0 },
498db059c0fSMark Brown 	{ 32000, 1 },
499db059c0fSMark Brown 	{ 22050, 2 },
500db059c0fSMark Brown 	{ 24000, 2 },
501db059c0fSMark Brown 	{ 16000, 3 },
502db059c0fSMark Brown 	{ 11250, 4 },
503db059c0fSMark Brown 	{ 12000, 4 },
504db059c0fSMark Brown 	{  8000, 5 },
505db059c0fSMark Brown };
506db059c0fSMark Brown 
507f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream,
508f2644a2cSMark Brown 			    struct snd_pcm_hw_params *params,
509f2644a2cSMark Brown 			    struct snd_soc_dai *dai)
510f2644a2cSMark Brown {
511f2644a2cSMark Brown 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
512f0fba2adSLiam Girdwood 	struct snd_soc_codec *codec = rtd->codec;
513afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
51417a52fd6SMark Brown 	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
515db059c0fSMark Brown 	int i;
516f2644a2cSMark Brown 
517f2644a2cSMark Brown 	/* bit size */
518f2644a2cSMark Brown 	switch (params_format(params)) {
519f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
520f2644a2cSMark Brown 		break;
521f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
522f2644a2cSMark Brown 		iface |= 0x0004;
523f2644a2cSMark Brown 		break;
524f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
525f2644a2cSMark Brown 		iface |= 0x0008;
526f2644a2cSMark Brown 		break;
527f2644a2cSMark Brown 	}
528f2644a2cSMark Brown 
529afd6d36aSMark Brown 	/* Update filters for the new rate */
530afd6d36aSMark Brown 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
531afd6d36aSMark Brown 		wm8960->playback_fs = params_rate(params);
532afd6d36aSMark Brown 		wm8960_set_deemph(codec);
533db059c0fSMark Brown 	} else {
534db059c0fSMark Brown 		for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
535db059c0fSMark Brown 			if (alc_rates[i].rate == params_rate(params))
536db059c0fSMark Brown 				snd_soc_update_bits(codec,
537db059c0fSMark Brown 						    WM8960_ADDCTL3, 0x7,
538db059c0fSMark Brown 						    alc_rates[i].val);
539afd6d36aSMark Brown 	}
540afd6d36aSMark Brown 
541f2644a2cSMark Brown 	/* set iface */
54217a52fd6SMark Brown 	snd_soc_write(codec, WM8960_IFACE1, iface);
543f2644a2cSMark Brown 	return 0;
544f2644a2cSMark Brown }
545f2644a2cSMark Brown 
546f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute)
547f2644a2cSMark Brown {
548f2644a2cSMark Brown 	struct snd_soc_codec *codec = dai->codec;
54917a52fd6SMark Brown 	u16 mute_reg = snd_soc_read(codec, WM8960_DACCTL1) & 0xfff7;
550f2644a2cSMark Brown 
551f2644a2cSMark Brown 	if (mute)
55217a52fd6SMark Brown 		snd_soc_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
553f2644a2cSMark Brown 	else
55417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_DACCTL1, mute_reg);
555f2644a2cSMark Brown 	return 0;
556f2644a2cSMark Brown }
557f2644a2cSMark Brown 
558913d7b4cSMark Brown static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
559f2644a2cSMark Brown 				      enum snd_soc_bias_level level)
560f2644a2cSMark Brown {
561f2644a2cSMark Brown 	u16 reg;
562f2644a2cSMark Brown 
563f2644a2cSMark Brown 	switch (level) {
564f2644a2cSMark Brown 	case SND_SOC_BIAS_ON:
565f2644a2cSMark Brown 		break;
566f2644a2cSMark Brown 
567f2644a2cSMark Brown 	case SND_SOC_BIAS_PREPARE:
568f2644a2cSMark Brown 		/* Set VMID to 2x50k */
56917a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_POWER1);
570f2644a2cSMark Brown 		reg &= ~0x180;
571f2644a2cSMark Brown 		reg |= 0x80;
57217a52fd6SMark Brown 		snd_soc_write(codec, WM8960_POWER1, reg);
573f2644a2cSMark Brown 		break;
574f2644a2cSMark Brown 
575f2644a2cSMark Brown 	case SND_SOC_BIAS_STANDBY:
576ce6120ccSLiam Girdwood 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
577f2644a2cSMark Brown 			/* Enable anti-pop features */
57817a52fd6SMark Brown 			snd_soc_write(codec, WM8960_APOP1,
579f2644a2cSMark Brown 				      WM8960_POBCTRL | WM8960_SOFT_ST |
580f2644a2cSMark Brown 				      WM8960_BUFDCOPEN | WM8960_BUFIOEN);
581f2644a2cSMark Brown 
582f2644a2cSMark Brown 			/* Enable & ramp VMID at 2x50k */
58317a52fd6SMark Brown 			reg = snd_soc_read(codec, WM8960_POWER1);
584f2644a2cSMark Brown 			reg |= 0x80;
58517a52fd6SMark Brown 			snd_soc_write(codec, WM8960_POWER1, reg);
586f2644a2cSMark Brown 			msleep(100);
587f2644a2cSMark Brown 
588f2644a2cSMark Brown 			/* Enable VREF */
58917a52fd6SMark Brown 			snd_soc_write(codec, WM8960_POWER1, reg | WM8960_VREF);
590f2644a2cSMark Brown 
591f2644a2cSMark Brown 			/* Disable anti-pop features */
59217a52fd6SMark Brown 			snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
593f2644a2cSMark Brown 		}
594f2644a2cSMark Brown 
595f2644a2cSMark Brown 		/* Set VMID to 2x250k */
59617a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_POWER1);
597f2644a2cSMark Brown 		reg &= ~0x180;
598f2644a2cSMark Brown 		reg |= 0x100;
59917a52fd6SMark Brown 		snd_soc_write(codec, WM8960_POWER1, reg);
600f2644a2cSMark Brown 		break;
601f2644a2cSMark Brown 
602f2644a2cSMark Brown 	case SND_SOC_BIAS_OFF:
603f2644a2cSMark Brown 		/* Enable anti-pop features */
60417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_APOP1,
605f2644a2cSMark Brown 			     WM8960_POBCTRL | WM8960_SOFT_ST |
606f2644a2cSMark Brown 			     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
607f2644a2cSMark Brown 
608f2644a2cSMark Brown 		/* Disable VMID and VREF, let them discharge */
60917a52fd6SMark Brown 		snd_soc_write(codec, WM8960_POWER1, 0);
610f2644a2cSMark Brown 		msleep(600);
611913d7b4cSMark Brown 		break;
612913d7b4cSMark Brown 	}
613f2644a2cSMark Brown 
614ce6120ccSLiam Girdwood 	codec->dapm.bias_level = level;
615913d7b4cSMark Brown 
616913d7b4cSMark Brown 	return 0;
617913d7b4cSMark Brown }
618913d7b4cSMark Brown 
619913d7b4cSMark Brown static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
620913d7b4cSMark Brown 					 enum snd_soc_bias_level level)
621913d7b4cSMark Brown {
622b2c812e2SMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
623913d7b4cSMark Brown 	int reg;
624913d7b4cSMark Brown 
625913d7b4cSMark Brown 	switch (level) {
626913d7b4cSMark Brown 	case SND_SOC_BIAS_ON:
627913d7b4cSMark Brown 		break;
628913d7b4cSMark Brown 
629913d7b4cSMark Brown 	case SND_SOC_BIAS_PREPARE:
630ce6120ccSLiam Girdwood 		switch (codec->dapm.bias_level) {
631913d7b4cSMark Brown 		case SND_SOC_BIAS_STANDBY:
632913d7b4cSMark Brown 			/* Enable anti pop mode */
633913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
634913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
635913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
636913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
637913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
638913d7b4cSMark Brown 
639913d7b4cSMark Brown 			/* Enable LOUT1, ROUT1 and OUT3 if they're enabled */
640913d7b4cSMark Brown 			reg = 0;
641913d7b4cSMark Brown 			if (wm8960->lout1 && wm8960->lout1->power)
642913d7b4cSMark Brown 				reg |= WM8960_PWR2_LOUT1;
643913d7b4cSMark Brown 			if (wm8960->rout1 && wm8960->rout1->power)
644913d7b4cSMark Brown 				reg |= WM8960_PWR2_ROUT1;
645913d7b4cSMark Brown 			if (wm8960->out3 && wm8960->out3->power)
646913d7b4cSMark Brown 				reg |= WM8960_PWR2_OUT3;
647913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER2,
648913d7b4cSMark Brown 					    WM8960_PWR2_LOUT1 |
649913d7b4cSMark Brown 					    WM8960_PWR2_ROUT1 |
650913d7b4cSMark Brown 					    WM8960_PWR2_OUT3, reg);
651913d7b4cSMark Brown 
652913d7b4cSMark Brown 			/* Enable VMID at 2*50k */
653913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
654913d7b4cSMark Brown 					    WM8960_VMID_MASK, 0x80);
655913d7b4cSMark Brown 
656913d7b4cSMark Brown 			/* Ramp */
657913d7b4cSMark Brown 			msleep(100);
658913d7b4cSMark Brown 
659913d7b4cSMark Brown 			/* Enable VREF */
660913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
661913d7b4cSMark Brown 					    WM8960_VREF, WM8960_VREF);
662913d7b4cSMark Brown 
663913d7b4cSMark Brown 			msleep(100);
664913d7b4cSMark Brown 			break;
665913d7b4cSMark Brown 
666913d7b4cSMark Brown 		case SND_SOC_BIAS_ON:
667913d7b4cSMark Brown 			/* Enable anti-pop mode */
668913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
669913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
670913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
671913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
672913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
673913d7b4cSMark Brown 
674913d7b4cSMark Brown 			/* Disable VMID and VREF */
675913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
676913d7b4cSMark Brown 					    WM8960_VREF | WM8960_VMID_MASK, 0);
677913d7b4cSMark Brown 			break;
678913d7b4cSMark Brown 
679913d7b4cSMark Brown 		default:
680913d7b4cSMark Brown 			break;
681913d7b4cSMark Brown 		}
682913d7b4cSMark Brown 		break;
683913d7b4cSMark Brown 
684913d7b4cSMark Brown 	case SND_SOC_BIAS_STANDBY:
685ce6120ccSLiam Girdwood 		switch (codec->dapm.bias_level) {
686913d7b4cSMark Brown 		case SND_SOC_BIAS_PREPARE:
687913d7b4cSMark Brown 			/* Disable HP discharge */
688913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP2,
689913d7b4cSMark Brown 					    WM8960_DISOP | WM8960_DRES_MASK,
690913d7b4cSMark Brown 					    0);
691913d7b4cSMark Brown 
692913d7b4cSMark Brown 			/* Disable anti-pop features */
693913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
694913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
695913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
696913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
697913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
698913d7b4cSMark Brown 			break;
699913d7b4cSMark Brown 
700913d7b4cSMark Brown 		default:
701913d7b4cSMark Brown 			break;
702913d7b4cSMark Brown 		}
703913d7b4cSMark Brown 		break;
704913d7b4cSMark Brown 
705913d7b4cSMark Brown 	case SND_SOC_BIAS_OFF:
706f2644a2cSMark Brown 		break;
707f2644a2cSMark Brown 	}
708f2644a2cSMark Brown 
709ce6120ccSLiam Girdwood 	codec->dapm.bias_level = level;
710f2644a2cSMark Brown 
711f2644a2cSMark Brown 	return 0;
712f2644a2cSMark Brown }
713f2644a2cSMark Brown 
714f2644a2cSMark Brown /* PLL divisors */
715f2644a2cSMark Brown struct _pll_div {
716f2644a2cSMark Brown 	u32 pre_div:1;
717f2644a2cSMark Brown 	u32 n:4;
718f2644a2cSMark Brown 	u32 k:24;
719f2644a2cSMark Brown };
720f2644a2cSMark Brown 
721f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10
722f2644a2cSMark Brown  * to allow rounding later */
723f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
724f2644a2cSMark Brown 
725f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target,
726f2644a2cSMark Brown 		       struct _pll_div *pll_div)
727f2644a2cSMark Brown {
728f2644a2cSMark Brown 	unsigned long long Kpart;
729f2644a2cSMark Brown 	unsigned int K, Ndiv, Nmod;
730f2644a2cSMark Brown 
731f2644a2cSMark Brown 	pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
732f2644a2cSMark Brown 
733f2644a2cSMark Brown 	/* Scale up target to PLL operating frequency */
734f2644a2cSMark Brown 	target *= 4;
735f2644a2cSMark Brown 
736f2644a2cSMark Brown 	Ndiv = target / source;
737f2644a2cSMark Brown 	if (Ndiv < 6) {
738f2644a2cSMark Brown 		source >>= 1;
739f2644a2cSMark Brown 		pll_div->pre_div = 1;
740f2644a2cSMark Brown 		Ndiv = target / source;
741f2644a2cSMark Brown 	} else
742f2644a2cSMark Brown 		pll_div->pre_div = 0;
743f2644a2cSMark Brown 
744f2644a2cSMark Brown 	if ((Ndiv < 6) || (Ndiv > 12)) {
745f2644a2cSMark Brown 		pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
746f2644a2cSMark Brown 		return -EINVAL;
747f2644a2cSMark Brown 	}
748f2644a2cSMark Brown 
749f2644a2cSMark Brown 	pll_div->n = Ndiv;
750f2644a2cSMark Brown 	Nmod = target % source;
751f2644a2cSMark Brown 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
752f2644a2cSMark Brown 
753f2644a2cSMark Brown 	do_div(Kpart, source);
754f2644a2cSMark Brown 
755f2644a2cSMark Brown 	K = Kpart & 0xFFFFFFFF;
756f2644a2cSMark Brown 
757f2644a2cSMark Brown 	/* Check if we need to round */
758f2644a2cSMark Brown 	if ((K % 10) >= 5)
759f2644a2cSMark Brown 		K += 5;
760f2644a2cSMark Brown 
761f2644a2cSMark Brown 	/* Move down to proper range now rounding is done */
762f2644a2cSMark Brown 	K /= 10;
763f2644a2cSMark Brown 
764f2644a2cSMark Brown 	pll_div->k = K;
765f2644a2cSMark Brown 
766f2644a2cSMark Brown 	pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
767f2644a2cSMark Brown 		 pll_div->n, pll_div->k, pll_div->pre_div);
768f2644a2cSMark Brown 
769f2644a2cSMark Brown 	return 0;
770f2644a2cSMark Brown }
771f2644a2cSMark Brown 
77285488037SMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
77385488037SMark Brown 		int source, unsigned int freq_in, unsigned int freq_out)
774f2644a2cSMark Brown {
775f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
776f2644a2cSMark Brown 	u16 reg;
777f2644a2cSMark Brown 	static struct _pll_div pll_div;
778f2644a2cSMark Brown 	int ret;
779f2644a2cSMark Brown 
780f2644a2cSMark Brown 	if (freq_in && freq_out) {
781f2644a2cSMark Brown 		ret = pll_factors(freq_in, freq_out, &pll_div);
782f2644a2cSMark Brown 		if (ret != 0)
783f2644a2cSMark Brown 			return ret;
784f2644a2cSMark Brown 	}
785f2644a2cSMark Brown 
786f2644a2cSMark Brown 	/* Disable the PLL: even if we are changing the frequency the
787f2644a2cSMark Brown 	 * PLL needs to be disabled while we do so. */
78817a52fd6SMark Brown 	snd_soc_write(codec, WM8960_CLOCK1,
78917a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_CLOCK1) & ~1);
79017a52fd6SMark Brown 	snd_soc_write(codec, WM8960_POWER2,
79117a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_POWER2) & ~1);
792f2644a2cSMark Brown 
793f2644a2cSMark Brown 	if (!freq_in || !freq_out)
794f2644a2cSMark Brown 		return 0;
795f2644a2cSMark Brown 
79617a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
797f2644a2cSMark Brown 	reg |= pll_div.pre_div << 4;
798f2644a2cSMark Brown 	reg |= pll_div.n;
799f2644a2cSMark Brown 
800f2644a2cSMark Brown 	if (pll_div.k) {
801f2644a2cSMark Brown 		reg |= 0x20;
802f2644a2cSMark Brown 
80317a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
80417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
80517a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
806f2644a2cSMark Brown 	}
80717a52fd6SMark Brown 	snd_soc_write(codec, WM8960_PLL1, reg);
808f2644a2cSMark Brown 
809f2644a2cSMark Brown 	/* Turn it on */
81017a52fd6SMark Brown 	snd_soc_write(codec, WM8960_POWER2,
81117a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_POWER2) | 1);
812f2644a2cSMark Brown 	msleep(250);
81317a52fd6SMark Brown 	snd_soc_write(codec, WM8960_CLOCK1,
81417a52fd6SMark Brown 		     snd_soc_read(codec, WM8960_CLOCK1) | 1);
815f2644a2cSMark Brown 
816f2644a2cSMark Brown 	return 0;
817f2644a2cSMark Brown }
818f2644a2cSMark Brown 
819f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
820f2644a2cSMark Brown 		int div_id, int div)
821f2644a2cSMark Brown {
822f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
823f2644a2cSMark Brown 	u16 reg;
824f2644a2cSMark Brown 
825f2644a2cSMark Brown 	switch (div_id) {
826f2644a2cSMark Brown 	case WM8960_SYSCLKDIV:
82717a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
82817a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
829f2644a2cSMark Brown 		break;
830f2644a2cSMark Brown 	case WM8960_DACDIV:
83117a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
83217a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
833f2644a2cSMark Brown 		break;
834f2644a2cSMark Brown 	case WM8960_OPCLKDIV:
83517a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
83617a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL1, reg | div);
837f2644a2cSMark Brown 		break;
838f2644a2cSMark Brown 	case WM8960_DCLKDIV:
83917a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
84017a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK2, reg | div);
841f2644a2cSMark Brown 		break;
842f2644a2cSMark Brown 	case WM8960_TOCLKSEL:
84317a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
84417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
845f2644a2cSMark Brown 		break;
846f2644a2cSMark Brown 	default:
847f2644a2cSMark Brown 		return -EINVAL;
848f2644a2cSMark Brown 	}
849f2644a2cSMark Brown 
850f2644a2cSMark Brown 	return 0;
851f2644a2cSMark Brown }
852f2644a2cSMark Brown 
853f0fba2adSLiam Girdwood static int wm8960_set_bias_level(struct snd_soc_codec *codec,
854f0fba2adSLiam Girdwood 				 enum snd_soc_bias_level level)
855f0fba2adSLiam Girdwood {
856f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
857f0fba2adSLiam Girdwood 
858f0fba2adSLiam Girdwood 	return wm8960->set_bias_level(codec, level);
859f0fba2adSLiam Girdwood }
860f0fba2adSLiam Girdwood 
861f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
862f2644a2cSMark Brown 
863f2644a2cSMark Brown #define WM8960_FORMATS \
864f2644a2cSMark Brown 	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
865f2644a2cSMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
866f2644a2cSMark Brown 
867f2644a2cSMark Brown static struct snd_soc_dai_ops wm8960_dai_ops = {
868f2644a2cSMark Brown 	.hw_params = wm8960_hw_params,
869f2644a2cSMark Brown 	.digital_mute = wm8960_mute,
870f2644a2cSMark Brown 	.set_fmt = wm8960_set_dai_fmt,
871f2644a2cSMark Brown 	.set_clkdiv = wm8960_set_dai_clkdiv,
872f2644a2cSMark Brown 	.set_pll = wm8960_set_dai_pll,
873f2644a2cSMark Brown };
874f2644a2cSMark Brown 
875f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8960_dai = {
876f0fba2adSLiam Girdwood 	.name = "wm8960-hifi",
877f2644a2cSMark Brown 	.playback = {
878f2644a2cSMark Brown 		.stream_name = "Playback",
879f2644a2cSMark Brown 		.channels_min = 1,
880f2644a2cSMark Brown 		.channels_max = 2,
881f2644a2cSMark Brown 		.rates = WM8960_RATES,
882f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
883f2644a2cSMark Brown 	.capture = {
884f2644a2cSMark Brown 		.stream_name = "Capture",
885f2644a2cSMark Brown 		.channels_min = 1,
886f2644a2cSMark Brown 		.channels_max = 2,
887f2644a2cSMark Brown 		.rates = WM8960_RATES,
888f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
889f2644a2cSMark Brown 	.ops = &wm8960_dai_ops,
890f2644a2cSMark Brown 	.symmetric_rates = 1,
891f2644a2cSMark Brown };
892f2644a2cSMark Brown 
893f0fba2adSLiam Girdwood static int wm8960_suspend(struct snd_soc_codec *codec, pm_message_t state)
894f2644a2cSMark Brown {
895f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
896f2644a2cSMark Brown 
897f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
898f2644a2cSMark Brown 	return 0;
899f2644a2cSMark Brown }
900f2644a2cSMark Brown 
901f0fba2adSLiam Girdwood static int wm8960_resume(struct snd_soc_codec *codec)
902f2644a2cSMark Brown {
903f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
904f2644a2cSMark Brown 	int i;
905f2644a2cSMark Brown 	u8 data[2];
906f2644a2cSMark Brown 	u16 *cache = codec->reg_cache;
907f2644a2cSMark Brown 
908f2644a2cSMark Brown 	/* Sync reg_cache with the hardware */
909f2644a2cSMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
910f2644a2cSMark Brown 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
911f2644a2cSMark Brown 		data[1] = cache[i] & 0x00ff;
912f2644a2cSMark Brown 		codec->hw_write(codec->control_data, data, 2);
913f2644a2cSMark Brown 	}
914f2644a2cSMark Brown 
915f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
916f2644a2cSMark Brown 	return 0;
917f2644a2cSMark Brown }
918f2644a2cSMark Brown 
919f0fba2adSLiam Girdwood static int wm8960_probe(struct snd_soc_codec *codec)
920f2644a2cSMark Brown {
921f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
922f0fba2adSLiam Girdwood 	struct wm8960_data *pdata = dev_get_platdata(codec->dev);
923f2644a2cSMark Brown 	int ret;
924f2644a2cSMark Brown 	u16 reg;
925f2644a2cSMark Brown 
926f0fba2adSLiam Girdwood 	wm8960->set_bias_level = wm8960_set_bias_level_out3;
927f0fba2adSLiam Girdwood 	codec->control_data = wm8960->control_data;
928913d7b4cSMark Brown 
929f2644a2cSMark Brown 	if (!pdata) {
930f2644a2cSMark Brown 		dev_warn(codec->dev, "No platform data supplied\n");
931f2644a2cSMark Brown 	} else {
932f2644a2cSMark Brown 		if (pdata->dres > WM8960_DRES_MAX) {
933f2644a2cSMark Brown 			dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
934f2644a2cSMark Brown 			pdata->dres = 0;
935f2644a2cSMark Brown 		}
936913d7b4cSMark Brown 
937913d7b4cSMark Brown 		if (pdata->capless)
938f0fba2adSLiam Girdwood 			wm8960->set_bias_level = wm8960_set_bias_level_capless;
939f2644a2cSMark Brown 	}
940f2644a2cSMark Brown 
941f0fba2adSLiam Girdwood 	ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8960->control_type);
94217a52fd6SMark Brown 	if (ret < 0) {
94317a52fd6SMark Brown 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
944f0fba2adSLiam Girdwood 		return ret;
94517a52fd6SMark Brown 	}
94617a52fd6SMark Brown 
947f2644a2cSMark Brown 	ret = wm8960_reset(codec);
948f2644a2cSMark Brown 	if (ret < 0) {
949f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to issue reset\n");
950f0fba2adSLiam Girdwood 		return ret;
951f2644a2cSMark Brown 	}
952f2644a2cSMark Brown 
953f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
954f2644a2cSMark Brown 
955f2644a2cSMark Brown 	/* Latch the update bits */
95617a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LINVOL);
95717a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LINVOL, reg | 0x100);
95817a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_RINVOL);
95917a52fd6SMark Brown 	snd_soc_write(codec, WM8960_RINVOL, reg | 0x100);
96017a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LADC);
96117a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LADC, reg | 0x100);
96217a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_RADC);
96317a52fd6SMark Brown 	snd_soc_write(codec, WM8960_RADC, reg | 0x100);
96417a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LDAC);
96517a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LDAC, reg | 0x100);
96617a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_RDAC);
96717a52fd6SMark Brown 	snd_soc_write(codec, WM8960_RDAC, reg | 0x100);
96817a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LOUT1);
96917a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LOUT1, reg | 0x100);
97017a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_ROUT1);
97117a52fd6SMark Brown 	snd_soc_write(codec, WM8960_ROUT1, reg | 0x100);
97217a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_LOUT2);
97317a52fd6SMark Brown 	snd_soc_write(codec, WM8960_LOUT2, reg | 0x100);
97417a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_ROUT2);
97517a52fd6SMark Brown 	snd_soc_write(codec, WM8960_ROUT2, reg | 0x100);
976f2644a2cSMark Brown 
977f0fba2adSLiam Girdwood 	snd_soc_add_controls(codec, wm8960_snd_controls,
978f0fba2adSLiam Girdwood 				     ARRAY_SIZE(wm8960_snd_controls));
979f0fba2adSLiam Girdwood 	wm8960_add_widgets(codec);
980f2644a2cSMark Brown 
981f2644a2cSMark Brown 	return 0;
982f2644a2cSMark Brown }
983f2644a2cSMark Brown 
984f0fba2adSLiam Girdwood /* power down chip */
985f0fba2adSLiam Girdwood static int wm8960_remove(struct snd_soc_codec *codec)
986f2644a2cSMark Brown {
987f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
988f0fba2adSLiam Girdwood 
989f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
990f0fba2adSLiam Girdwood 	return 0;
991f2644a2cSMark Brown }
992f2644a2cSMark Brown 
993f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
994f0fba2adSLiam Girdwood 	.probe =	wm8960_probe,
995f0fba2adSLiam Girdwood 	.remove =	wm8960_remove,
996f0fba2adSLiam Girdwood 	.suspend =	wm8960_suspend,
997f0fba2adSLiam Girdwood 	.resume =	wm8960_resume,
998f0fba2adSLiam Girdwood 	.set_bias_level = wm8960_set_bias_level,
999f0fba2adSLiam Girdwood 	.reg_cache_size = ARRAY_SIZE(wm8960_reg),
1000f0fba2adSLiam Girdwood 	.reg_word_size = sizeof(u16),
1001f0fba2adSLiam Girdwood 	.reg_cache_default = wm8960_reg,
1002f0fba2adSLiam Girdwood };
1003f0fba2adSLiam Girdwood 
1004f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1005f2644a2cSMark Brown static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
1006f2644a2cSMark Brown 				      const struct i2c_device_id *id)
1007f2644a2cSMark Brown {
1008f2644a2cSMark Brown 	struct wm8960_priv *wm8960;
1009f0fba2adSLiam Girdwood 	int ret;
1010f2644a2cSMark Brown 
1011f2644a2cSMark Brown 	wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);
1012f2644a2cSMark Brown 	if (wm8960 == NULL)
1013f2644a2cSMark Brown 		return -ENOMEM;
1014f2644a2cSMark Brown 
1015f2644a2cSMark Brown 	i2c_set_clientdata(i2c, wm8960);
1016f0fba2adSLiam Girdwood 	wm8960->control_data = i2c;
1017f2644a2cSMark Brown 
1018f0fba2adSLiam Girdwood 	ret = snd_soc_register_codec(&i2c->dev,
1019f0fba2adSLiam Girdwood 			&soc_codec_dev_wm8960, &wm8960_dai, 1);
1020f0fba2adSLiam Girdwood 	if (ret < 0)
1021f0fba2adSLiam Girdwood 		kfree(wm8960);
1022f0fba2adSLiam Girdwood 	return ret;
1023f2644a2cSMark Brown }
1024f2644a2cSMark Brown 
1025f2644a2cSMark Brown static __devexit int wm8960_i2c_remove(struct i2c_client *client)
1026f2644a2cSMark Brown {
1027f0fba2adSLiam Girdwood 	snd_soc_unregister_codec(&client->dev);
1028f0fba2adSLiam Girdwood 	kfree(i2c_get_clientdata(client));
1029f2644a2cSMark Brown 	return 0;
1030f2644a2cSMark Brown }
1031f2644a2cSMark Brown 
1032f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = {
1033f2644a2cSMark Brown 	{ "wm8960", 0 },
1034f2644a2cSMark Brown 	{ }
1035f2644a2cSMark Brown };
1036f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
1037f2644a2cSMark Brown 
1038f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = {
1039f2644a2cSMark Brown 	.driver = {
1040f0fba2adSLiam Girdwood 		.name = "wm8960-codec",
1041f2644a2cSMark Brown 		.owner = THIS_MODULE,
1042f2644a2cSMark Brown 	},
1043f2644a2cSMark Brown 	.probe =    wm8960_i2c_probe,
1044f2644a2cSMark Brown 	.remove =   __devexit_p(wm8960_i2c_remove),
1045f2644a2cSMark Brown 	.id_table = wm8960_i2c_id,
1046f2644a2cSMark Brown };
1047f0fba2adSLiam Girdwood #endif
1048f2644a2cSMark Brown 
1049f2644a2cSMark Brown static int __init wm8960_modinit(void)
1050f2644a2cSMark Brown {
1051f0fba2adSLiam Girdwood 	int ret = 0;
1052f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1053f2644a2cSMark Brown 	ret = i2c_add_driver(&wm8960_i2c_driver);
1054f2644a2cSMark Brown 	if (ret != 0) {
1055f2644a2cSMark Brown 		printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
1056f2644a2cSMark Brown 		       ret);
1057f2644a2cSMark Brown 	}
1058f0fba2adSLiam Girdwood #endif
1059f2644a2cSMark Brown 	return ret;
1060f2644a2cSMark Brown }
1061f2644a2cSMark Brown module_init(wm8960_modinit);
1062f2644a2cSMark Brown 
1063f2644a2cSMark Brown static void __exit wm8960_exit(void)
1064f2644a2cSMark Brown {
1065f0fba2adSLiam Girdwood #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
1066f2644a2cSMark Brown 	i2c_del_driver(&wm8960_i2c_driver);
1067f0fba2adSLiam Girdwood #endif
1068f2644a2cSMark Brown }
1069f2644a2cSMark Brown module_exit(wm8960_exit);
1070f2644a2cSMark Brown 
1071f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver");
1072f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood");
1073f2644a2cSMark Brown MODULE_LICENSE("GPL");
1074