xref: /openbmc/linux/sound/soc/codecs/wm8960.c (revision b9791c01)
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>
175a0e3ad6STejun Heo #include <linux/slab.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/initval.h>
23f2644a2cSMark Brown #include <sound/tlv.h>
24b6877a47SMark Brown #include <sound/wm8960.h>
25f2644a2cSMark Brown 
26f2644a2cSMark Brown #include "wm8960.h"
27f2644a2cSMark Brown 
28f2644a2cSMark Brown /* R25 - Power 1 */
29913d7b4cSMark Brown #define WM8960_VMID_MASK 0x180
30f2644a2cSMark Brown #define WM8960_VREF      0x40
31f2644a2cSMark Brown 
32913d7b4cSMark Brown /* R26 - Power 2 */
33913d7b4cSMark Brown #define WM8960_PWR2_LOUT1	0x40
34913d7b4cSMark Brown #define WM8960_PWR2_ROUT1	0x20
35913d7b4cSMark Brown #define WM8960_PWR2_OUT3	0x02
36913d7b4cSMark Brown 
37f2644a2cSMark Brown /* R28 - Anti-pop 1 */
38f2644a2cSMark Brown #define WM8960_POBCTRL   0x80
39f2644a2cSMark Brown #define WM8960_BUFDCOPEN 0x10
40f2644a2cSMark Brown #define WM8960_BUFIOEN   0x08
41f2644a2cSMark Brown #define WM8960_SOFT_ST   0x04
42f2644a2cSMark Brown #define WM8960_HPSTBY    0x01
43f2644a2cSMark Brown 
44f2644a2cSMark Brown /* R29 - Anti-pop 2 */
45f2644a2cSMark Brown #define WM8960_DISOP     0x40
46913d7b4cSMark Brown #define WM8960_DRES_MASK 0x30
47f2644a2cSMark Brown 
48f2644a2cSMark Brown /*
49f2644a2cSMark Brown  * wm8960 register cache
50f2644a2cSMark Brown  * We can't read the WM8960 register space when we are
51f2644a2cSMark Brown  * using 2 wire for device control, so we cache them instead.
52f2644a2cSMark Brown  */
53f2644a2cSMark Brown static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
54f2644a2cSMark Brown 	0x0097, 0x0097, 0x0000, 0x0000,
55f2644a2cSMark Brown 	0x0000, 0x0008, 0x0000, 0x000a,
56f2644a2cSMark Brown 	0x01c0, 0x0000, 0x00ff, 0x00ff,
57f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
58f2644a2cSMark Brown 	0x0000, 0x007b, 0x0100, 0x0032,
59f2644a2cSMark Brown 	0x0000, 0x00c3, 0x00c3, 0x01c0,
60f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
61f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
62f2644a2cSMark Brown 	0x0100, 0x0100, 0x0050, 0x0050,
63f2644a2cSMark Brown 	0x0050, 0x0050, 0x0000, 0x0000,
64f2644a2cSMark Brown 	0x0000, 0x0000, 0x0040, 0x0000,
65f2644a2cSMark Brown 	0x0000, 0x0050, 0x0050, 0x0000,
66f2644a2cSMark Brown 	0x0002, 0x0037, 0x004d, 0x0080,
67f2644a2cSMark Brown 	0x0008, 0x0031, 0x0026, 0x00e9,
68f2644a2cSMark Brown };
69f2644a2cSMark Brown 
70f2644a2cSMark Brown struct wm8960_priv {
71f0fba2adSLiam Girdwood 	enum snd_soc_control_type control_type;
72f0fba2adSLiam Girdwood 	int (*set_bias_level)(struct snd_soc_codec *,
73f0fba2adSLiam Girdwood 			      enum snd_soc_bias_level level);
74913d7b4cSMark Brown 	struct snd_soc_dapm_widget *lout1;
75913d7b4cSMark Brown 	struct snd_soc_dapm_widget *rout1;
76913d7b4cSMark Brown 	struct snd_soc_dapm_widget *out3;
77afd6d36aSMark Brown 	bool deemph;
78afd6d36aSMark Brown 	int playback_fs;
79f2644a2cSMark Brown };
80f2644a2cSMark Brown 
8117a52fd6SMark Brown #define wm8960_reset(c)	snd_soc_write(c, WM8960_RESET, 0)
82f2644a2cSMark Brown 
83f2644a2cSMark Brown /* enumerated controls */
84f2644a2cSMark Brown static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
85f2644a2cSMark Brown 	"Right Inverted", "Stereo Inversion"};
86f2644a2cSMark Brown static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
87f2644a2cSMark Brown static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
88f2644a2cSMark Brown static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
89f2644a2cSMark Brown static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
90f2644a2cSMark Brown 
91f2644a2cSMark Brown static const struct soc_enum wm8960_enum[] = {
92f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
93f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
94f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
95f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
96f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
97f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
98f2644a2cSMark Brown };
99f2644a2cSMark Brown 
100afd6d36aSMark Brown static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
101afd6d36aSMark Brown 
102afd6d36aSMark Brown static int wm8960_set_deemph(struct snd_soc_codec *codec)
103afd6d36aSMark Brown {
104afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
105afd6d36aSMark Brown 	int val, i, best;
106afd6d36aSMark Brown 
107afd6d36aSMark Brown 	/* If we're using deemphasis select the nearest available sample
108afd6d36aSMark Brown 	 * rate.
109afd6d36aSMark Brown 	 */
110afd6d36aSMark Brown 	if (wm8960->deemph) {
111afd6d36aSMark Brown 		best = 1;
112afd6d36aSMark Brown 		for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
113afd6d36aSMark Brown 			if (abs(deemph_settings[i] - wm8960->playback_fs) <
114afd6d36aSMark Brown 			    abs(deemph_settings[best] - wm8960->playback_fs))
115afd6d36aSMark Brown 				best = i;
116afd6d36aSMark Brown 		}
117afd6d36aSMark Brown 
118afd6d36aSMark Brown 		val = best << 1;
119afd6d36aSMark Brown 	} else {
120afd6d36aSMark Brown 		val = 0;
121afd6d36aSMark Brown 	}
122afd6d36aSMark Brown 
123afd6d36aSMark Brown 	dev_dbg(codec->dev, "Set deemphasis %d\n", val);
124afd6d36aSMark Brown 
125afd6d36aSMark Brown 	return snd_soc_update_bits(codec, WM8960_DACCTL1,
126afd6d36aSMark Brown 				   0x6, val);
127afd6d36aSMark Brown }
128afd6d36aSMark Brown 
129afd6d36aSMark Brown static int wm8960_get_deemph(struct snd_kcontrol *kcontrol,
130afd6d36aSMark Brown 			     struct snd_ctl_elem_value *ucontrol)
131afd6d36aSMark Brown {
132afd6d36aSMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
133afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
134afd6d36aSMark Brown 
1353f343f85SDmitry Artamonow 	ucontrol->value.enumerated.item[0] = wm8960->deemph;
1363f343f85SDmitry Artamonow 	return 0;
137afd6d36aSMark Brown }
138afd6d36aSMark Brown 
139afd6d36aSMark Brown static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
140afd6d36aSMark Brown 			     struct snd_ctl_elem_value *ucontrol)
141afd6d36aSMark Brown {
142afd6d36aSMark Brown 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
143afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
144afd6d36aSMark Brown 	int deemph = ucontrol->value.enumerated.item[0];
145afd6d36aSMark Brown 
146afd6d36aSMark Brown 	if (deemph > 1)
147afd6d36aSMark Brown 		return -EINVAL;
148afd6d36aSMark Brown 
149afd6d36aSMark Brown 	wm8960->deemph = deemph;
150afd6d36aSMark Brown 
151afd6d36aSMark Brown 	return wm8960_set_deemph(codec);
152afd6d36aSMark Brown }
153afd6d36aSMark Brown 
154f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
155f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
156f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
157f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
158f2644a2cSMark Brown 
159f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = {
160f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
161f2644a2cSMark Brown 		 0, 63, 0, adc_tlv),
162f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
163f2644a2cSMark Brown 	6, 1, 0),
164f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
165f2644a2cSMark Brown 	7, 1, 0),
166f2644a2cSMark Brown 
167f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
168f2644a2cSMark Brown 		 0, 255, 0, dac_tlv),
169f2644a2cSMark Brown 
170f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
171f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
172f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
173f2644a2cSMark Brown 	7, 1, 0),
174f2644a2cSMark Brown 
175f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
176f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
177f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
178f2644a2cSMark Brown 	7, 1, 0),
179f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
180f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
181f2644a2cSMark Brown 
182f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
1834faaa8d9SMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[0]),
184f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
185f2644a2cSMark Brown 
186f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]),
187afd6d36aSMark Brown SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
188afd6d36aSMark Brown 		    wm8960_get_deemph, wm8960_put_deemph),
189f2644a2cSMark Brown 
1904faaa8d9SMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]),
1914faaa8d9SMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]),
192f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
193f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
194f2644a2cSMark Brown 
1954faaa8d9SMark Brown SOC_ENUM("ALC Function", wm8960_enum[4]),
196f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
197f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
198f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
199f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
2004faaa8d9SMark Brown SOC_ENUM("ALC Mode", wm8960_enum[5]),
201f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
202f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
203f2644a2cSMark Brown 
204f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
205f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
206f2644a2cSMark Brown 
207f2644a2cSMark Brown SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
208f2644a2cSMark Brown 	0, 127, 0),
209f2644a2cSMark Brown 
210f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
211f2644a2cSMark Brown 	       WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
212f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
213f2644a2cSMark Brown 	       WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
214f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
215f2644a2cSMark Brown 	       WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
216f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
217f2644a2cSMark Brown 	       WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
218f2644a2cSMark Brown };
219f2644a2cSMark Brown 
220f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = {
221f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
222f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
223f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
224f2644a2cSMark Brown };
225f2644a2cSMark Brown 
226f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = {
227f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
228f2644a2cSMark Brown };
229f2644a2cSMark Brown 
230f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = {
231f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
232f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
233f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
234f2644a2cSMark Brown };
235f2644a2cSMark Brown 
236f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = {
237f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
238f2644a2cSMark Brown };
239f2644a2cSMark Brown 
240f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
241f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
242f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
243f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
244f2644a2cSMark Brown };
245f2644a2cSMark Brown 
246f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
247f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
248f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
249f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
250f2644a2cSMark Brown };
251f2644a2cSMark Brown 
252f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = {
253f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
254f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
255f2644a2cSMark Brown };
256f2644a2cSMark Brown 
257f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
258f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"),
259f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"),
260f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"),
261f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"),
262f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"),
263f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"),
264f2644a2cSMark Brown 
265187774cbSMark Brown SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0),
266f2644a2cSMark Brown 
267f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
268f2644a2cSMark Brown 		   wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
269f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
270f2644a2cSMark Brown 		   wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
271f2644a2cSMark Brown 
272f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
273f2644a2cSMark Brown 		   wm8960_lin, ARRAY_SIZE(wm8960_lin)),
274f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
275f2644a2cSMark Brown 		   wm8960_rin, ARRAY_SIZE(wm8960_rin)),
276f2644a2cSMark Brown 
277f2644a2cSMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0),
278f2644a2cSMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0),
279f2644a2cSMark Brown 
280f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
281f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
282f2644a2cSMark Brown 
283f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
284f2644a2cSMark Brown 	&wm8960_loutput_mixer[0],
285f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_loutput_mixer)),
286f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
287f2644a2cSMark Brown 	&wm8960_routput_mixer[0],
288f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_routput_mixer)),
289f2644a2cSMark Brown 
290f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
291f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
292f2644a2cSMark Brown 
293f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
294f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
295f2644a2cSMark Brown 
296f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
297f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
298f2644a2cSMark Brown 
299f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"),
300f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"),
301f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"),
302f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"),
303f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"),
304f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"),
305f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"),
306f2644a2cSMark Brown };
307f2644a2cSMark Brown 
308913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = {
309913d7b4cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
310913d7b4cSMark Brown 	&wm8960_mono_out[0],
311913d7b4cSMark Brown 	ARRAY_SIZE(wm8960_mono_out)),
312913d7b4cSMark Brown };
313913d7b4cSMark Brown 
314913d7b4cSMark Brown /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */
315913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = {
316913d7b4cSMark Brown SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0),
317913d7b4cSMark Brown };
318913d7b4cSMark Brown 
319f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = {
320f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
321f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
322f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
323f2644a2cSMark Brown 
324f2644a2cSMark Brown 	{ "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
325f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT1", },  /* Really Boost Switch */
326f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT2" },
327f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT3" },
328f2644a2cSMark Brown 
329f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
330f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
331f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
332f2644a2cSMark Brown 
333f2644a2cSMark Brown 	{ "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
334f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT1", },  /* Really Boost Switch */
335f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT2" },
336f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "LINPUT3" },
337f2644a2cSMark Brown 
338f2644a2cSMark Brown 	{ "Left ADC", NULL, "Left Input Mixer" },
339f2644a2cSMark Brown 	{ "Right ADC", NULL, "Right Input Mixer" },
340f2644a2cSMark Brown 
341f2644a2cSMark Brown 	{ "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
342f2644a2cSMark Brown 	{ "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
343f2644a2cSMark Brown 	{ "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
344f2644a2cSMark Brown 
345f2644a2cSMark Brown 	{ "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
346f2644a2cSMark Brown 	{ "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
347f2644a2cSMark Brown 	{ "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
348f2644a2cSMark Brown 
349f2644a2cSMark Brown 	{ "LOUT1 PGA", NULL, "Left Output Mixer" },
350f2644a2cSMark Brown 	{ "ROUT1 PGA", NULL, "Right Output Mixer" },
351f2644a2cSMark Brown 
352f2644a2cSMark Brown 	{ "HP_L", NULL, "LOUT1 PGA" },
353f2644a2cSMark Brown 	{ "HP_R", NULL, "ROUT1 PGA" },
354f2644a2cSMark Brown 
355f2644a2cSMark Brown 	{ "Left Speaker PGA", NULL, "Left Output Mixer" },
356f2644a2cSMark Brown 	{ "Right Speaker PGA", NULL, "Right Output Mixer" },
357f2644a2cSMark Brown 
358f2644a2cSMark Brown 	{ "Left Speaker Output", NULL, "Left Speaker PGA" },
359f2644a2cSMark Brown 	{ "Right Speaker Output", NULL, "Right Speaker PGA" },
360f2644a2cSMark Brown 
361f2644a2cSMark Brown 	{ "SPK_LN", NULL, "Left Speaker Output" },
362f2644a2cSMark Brown 	{ "SPK_LP", NULL, "Left Speaker Output" },
363f2644a2cSMark Brown 	{ "SPK_RN", NULL, "Right Speaker Output" },
364f2644a2cSMark Brown 	{ "SPK_RP", NULL, "Right Speaker Output" },
365913d7b4cSMark Brown };
366913d7b4cSMark Brown 
367913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_out3[] = {
368913d7b4cSMark Brown 	{ "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
369913d7b4cSMark Brown 	{ "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
370f2644a2cSMark Brown 
371f2644a2cSMark Brown 	{ "OUT3", NULL, "Mono Output Mixer", }
372f2644a2cSMark Brown };
373f2644a2cSMark Brown 
374913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_capless[] = {
375913d7b4cSMark Brown 	{ "HP_L", NULL, "OUT3 VMID" },
376913d7b4cSMark Brown 	{ "HP_R", NULL, "OUT3 VMID" },
377913d7b4cSMark Brown 
378913d7b4cSMark Brown 	{ "OUT3 VMID", NULL, "Left Output Mixer" },
379913d7b4cSMark Brown 	{ "OUT3 VMID", NULL, "Right Output Mixer" },
380913d7b4cSMark Brown };
381913d7b4cSMark Brown 
382f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec)
383f2644a2cSMark Brown {
384913d7b4cSMark Brown 	struct wm8960_data *pdata = codec->dev->platform_data;
385b2c812e2SMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
386ce6120ccSLiam Girdwood 	struct snd_soc_dapm_context *dapm = &codec->dapm;
387913d7b4cSMark Brown 	struct snd_soc_dapm_widget *w;
388913d7b4cSMark Brown 
389ce6120ccSLiam Girdwood 	snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,
390f2644a2cSMark Brown 				  ARRAY_SIZE(wm8960_dapm_widgets));
391f2644a2cSMark Brown 
392ce6120ccSLiam Girdwood 	snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
393f2644a2cSMark Brown 
394913d7b4cSMark Brown 	/* In capless mode OUT3 is used to provide VMID for the
395913d7b4cSMark Brown 	 * headphone outputs, otherwise it is used as a mono mixer.
396913d7b4cSMark Brown 	 */
397913d7b4cSMark Brown 	if (pdata && pdata->capless) {
398ce6120ccSLiam Girdwood 		snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless,
399913d7b4cSMark Brown 					  ARRAY_SIZE(wm8960_dapm_widgets_capless));
400913d7b4cSMark Brown 
401ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_paths_capless,
402913d7b4cSMark Brown 					ARRAY_SIZE(audio_paths_capless));
403913d7b4cSMark Brown 	} else {
404ce6120ccSLiam Girdwood 		snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3,
405913d7b4cSMark Brown 					  ARRAY_SIZE(wm8960_dapm_widgets_out3));
406913d7b4cSMark Brown 
407ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_paths_out3,
408913d7b4cSMark Brown 					ARRAY_SIZE(audio_paths_out3));
409913d7b4cSMark Brown 	}
410913d7b4cSMark Brown 
411913d7b4cSMark Brown 	/* We need to power up the headphone output stage out of
412913d7b4cSMark Brown 	 * sequence for capless mode.  To save scanning the widget
413913d7b4cSMark Brown 	 * list each time to find the desired power state do so now
414913d7b4cSMark Brown 	 * and save the result.
415913d7b4cSMark Brown 	 */
41697c866deSJarkko Nikula 	list_for_each_entry(w, &codec->card->widgets, list) {
41797c866deSJarkko Nikula 		if (w->dapm != &codec->dapm)
41897c866deSJarkko Nikula 			continue;
419913d7b4cSMark Brown 		if (strcmp(w->name, "LOUT1 PGA") == 0)
420913d7b4cSMark Brown 			wm8960->lout1 = w;
421913d7b4cSMark Brown 		if (strcmp(w->name, "ROUT1 PGA") == 0)
422913d7b4cSMark Brown 			wm8960->rout1 = w;
423913d7b4cSMark Brown 		if (strcmp(w->name, "OUT3 VMID") == 0)
424913d7b4cSMark Brown 			wm8960->out3 = w;
425913d7b4cSMark Brown 	}
426913d7b4cSMark Brown 
427f2644a2cSMark Brown 	return 0;
428f2644a2cSMark Brown }
429f2644a2cSMark Brown 
430f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
431f2644a2cSMark Brown 		unsigned int fmt)
432f2644a2cSMark Brown {
433f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
434f2644a2cSMark Brown 	u16 iface = 0;
435f2644a2cSMark Brown 
436f2644a2cSMark Brown 	/* set master/slave audio interface */
437f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
438f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
439f2644a2cSMark Brown 		iface |= 0x0040;
440f2644a2cSMark Brown 		break;
441f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
442f2644a2cSMark Brown 		break;
443f2644a2cSMark Brown 	default:
444f2644a2cSMark Brown 		return -EINVAL;
445f2644a2cSMark Brown 	}
446f2644a2cSMark Brown 
447f2644a2cSMark Brown 	/* interface format */
448f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
449f2644a2cSMark Brown 	case SND_SOC_DAIFMT_I2S:
450f2644a2cSMark Brown 		iface |= 0x0002;
451f2644a2cSMark Brown 		break;
452f2644a2cSMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
453f2644a2cSMark Brown 		break;
454f2644a2cSMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
455f2644a2cSMark Brown 		iface |= 0x0001;
456f2644a2cSMark Brown 		break;
457f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_A:
458f2644a2cSMark Brown 		iface |= 0x0003;
459f2644a2cSMark Brown 		break;
460f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_B:
461f2644a2cSMark Brown 		iface |= 0x0013;
462f2644a2cSMark Brown 		break;
463f2644a2cSMark Brown 	default:
464f2644a2cSMark Brown 		return -EINVAL;
465f2644a2cSMark Brown 	}
466f2644a2cSMark Brown 
467f2644a2cSMark Brown 	/* clock inversion */
468f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
469f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_NF:
470f2644a2cSMark Brown 		break;
471f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_IF:
472f2644a2cSMark Brown 		iface |= 0x0090;
473f2644a2cSMark Brown 		break;
474f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_NF:
475f2644a2cSMark Brown 		iface |= 0x0080;
476f2644a2cSMark Brown 		break;
477f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_IF:
478f2644a2cSMark Brown 		iface |= 0x0010;
479f2644a2cSMark Brown 		break;
480f2644a2cSMark Brown 	default:
481f2644a2cSMark Brown 		return -EINVAL;
482f2644a2cSMark Brown 	}
483f2644a2cSMark Brown 
484f2644a2cSMark Brown 	/* set iface */
48517a52fd6SMark Brown 	snd_soc_write(codec, WM8960_IFACE1, iface);
486f2644a2cSMark Brown 	return 0;
487f2644a2cSMark Brown }
488f2644a2cSMark Brown 
489db059c0fSMark Brown static struct {
490db059c0fSMark Brown 	int rate;
491db059c0fSMark Brown 	unsigned int val;
492db059c0fSMark Brown } alc_rates[] = {
493db059c0fSMark Brown 	{ 48000, 0 },
494db059c0fSMark Brown 	{ 44100, 0 },
495db059c0fSMark Brown 	{ 32000, 1 },
496db059c0fSMark Brown 	{ 22050, 2 },
497db059c0fSMark Brown 	{ 24000, 2 },
498db059c0fSMark Brown 	{ 16000, 3 },
499db059c0fSMark Brown 	{ 11250, 4 },
500db059c0fSMark Brown 	{ 12000, 4 },
501db059c0fSMark Brown 	{  8000, 5 },
502db059c0fSMark Brown };
503db059c0fSMark Brown 
504f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream,
505f2644a2cSMark Brown 			    struct snd_pcm_hw_params *params,
506f2644a2cSMark Brown 			    struct snd_soc_dai *dai)
507f2644a2cSMark Brown {
508f2644a2cSMark Brown 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
509f0fba2adSLiam Girdwood 	struct snd_soc_codec *codec = rtd->codec;
510afd6d36aSMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
51117a52fd6SMark Brown 	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
512db059c0fSMark Brown 	int i;
513f2644a2cSMark Brown 
514f2644a2cSMark Brown 	/* bit size */
515f2644a2cSMark Brown 	switch (params_format(params)) {
516f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
517f2644a2cSMark Brown 		break;
518f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
519f2644a2cSMark Brown 		iface |= 0x0004;
520f2644a2cSMark Brown 		break;
521f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
522f2644a2cSMark Brown 		iface |= 0x0008;
523f2644a2cSMark Brown 		break;
524f2644a2cSMark Brown 	}
525f2644a2cSMark Brown 
526afd6d36aSMark Brown 	/* Update filters for the new rate */
527afd6d36aSMark Brown 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
528afd6d36aSMark Brown 		wm8960->playback_fs = params_rate(params);
529afd6d36aSMark Brown 		wm8960_set_deemph(codec);
530db059c0fSMark Brown 	} else {
531db059c0fSMark Brown 		for (i = 0; i < ARRAY_SIZE(alc_rates); i++)
532db059c0fSMark Brown 			if (alc_rates[i].rate == params_rate(params))
533db059c0fSMark Brown 				snd_soc_update_bits(codec,
534db059c0fSMark Brown 						    WM8960_ADDCTL3, 0x7,
535db059c0fSMark Brown 						    alc_rates[i].val);
536afd6d36aSMark Brown 	}
537afd6d36aSMark Brown 
538f2644a2cSMark Brown 	/* set iface */
53917a52fd6SMark Brown 	snd_soc_write(codec, WM8960_IFACE1, iface);
540f2644a2cSMark Brown 	return 0;
541f2644a2cSMark Brown }
542f2644a2cSMark Brown 
543f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute)
544f2644a2cSMark Brown {
545f2644a2cSMark Brown 	struct snd_soc_codec *codec = dai->codec;
546f2644a2cSMark Brown 
547f2644a2cSMark Brown 	if (mute)
54816b24881SAxel Lin 		snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8);
549f2644a2cSMark Brown 	else
55016b24881SAxel Lin 		snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0);
551f2644a2cSMark Brown 	return 0;
552f2644a2cSMark Brown }
553f2644a2cSMark Brown 
554913d7b4cSMark Brown static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
555f2644a2cSMark Brown 				      enum snd_soc_bias_level level)
556f2644a2cSMark Brown {
557f2644a2cSMark Brown 	switch (level) {
558f2644a2cSMark Brown 	case SND_SOC_BIAS_ON:
559f2644a2cSMark Brown 		break;
560f2644a2cSMark Brown 
561f2644a2cSMark Brown 	case SND_SOC_BIAS_PREPARE:
562f2644a2cSMark Brown 		/* Set VMID to 2x50k */
56316b24881SAxel Lin 		snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80);
564f2644a2cSMark Brown 		break;
565f2644a2cSMark Brown 
566f2644a2cSMark Brown 	case SND_SOC_BIAS_STANDBY:
567ce6120ccSLiam Girdwood 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
568bc45df2dSAxel Lin 			snd_soc_cache_sync(codec);
569bc45df2dSAxel Lin 
570f2644a2cSMark Brown 			/* Enable anti-pop features */
57117a52fd6SMark Brown 			snd_soc_write(codec, WM8960_APOP1,
572f2644a2cSMark Brown 				      WM8960_POBCTRL | WM8960_SOFT_ST |
573f2644a2cSMark Brown 				      WM8960_BUFDCOPEN | WM8960_BUFIOEN);
574f2644a2cSMark Brown 
575f2644a2cSMark Brown 			/* Enable & ramp VMID at 2x50k */
57616b24881SAxel Lin 			snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80);
577f2644a2cSMark Brown 			msleep(100);
578f2644a2cSMark Brown 
579f2644a2cSMark Brown 			/* Enable VREF */
58016b24881SAxel Lin 			snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF,
58116b24881SAxel Lin 					    WM8960_VREF);
582f2644a2cSMark Brown 
583f2644a2cSMark Brown 			/* Disable anti-pop features */
58417a52fd6SMark Brown 			snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
585f2644a2cSMark Brown 		}
586f2644a2cSMark Brown 
587f2644a2cSMark Brown 		/* Set VMID to 2x250k */
58816b24881SAxel Lin 		snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100);
589f2644a2cSMark Brown 		break;
590f2644a2cSMark Brown 
591f2644a2cSMark Brown 	case SND_SOC_BIAS_OFF:
592f2644a2cSMark Brown 		/* Enable anti-pop features */
59317a52fd6SMark Brown 		snd_soc_write(codec, WM8960_APOP1,
594f2644a2cSMark Brown 			     WM8960_POBCTRL | WM8960_SOFT_ST |
595f2644a2cSMark Brown 			     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
596f2644a2cSMark Brown 
597f2644a2cSMark Brown 		/* Disable VMID and VREF, let them discharge */
59817a52fd6SMark Brown 		snd_soc_write(codec, WM8960_POWER1, 0);
599f2644a2cSMark Brown 		msleep(600);
600913d7b4cSMark Brown 		break;
601913d7b4cSMark Brown 	}
602f2644a2cSMark Brown 
603ce6120ccSLiam Girdwood 	codec->dapm.bias_level = level;
604913d7b4cSMark Brown 
605913d7b4cSMark Brown 	return 0;
606913d7b4cSMark Brown }
607913d7b4cSMark Brown 
608913d7b4cSMark Brown static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
609913d7b4cSMark Brown 					 enum snd_soc_bias_level level)
610913d7b4cSMark Brown {
611b2c812e2SMark Brown 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
612913d7b4cSMark Brown 	int reg;
613913d7b4cSMark Brown 
614913d7b4cSMark Brown 	switch (level) {
615913d7b4cSMark Brown 	case SND_SOC_BIAS_ON:
616913d7b4cSMark Brown 		break;
617913d7b4cSMark Brown 
618913d7b4cSMark Brown 	case SND_SOC_BIAS_PREPARE:
619ce6120ccSLiam Girdwood 		switch (codec->dapm.bias_level) {
620913d7b4cSMark Brown 		case SND_SOC_BIAS_STANDBY:
621913d7b4cSMark Brown 			/* Enable anti pop mode */
622913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
623913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
624913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
625913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
626913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
627913d7b4cSMark Brown 
628913d7b4cSMark Brown 			/* Enable LOUT1, ROUT1 and OUT3 if they're enabled */
629913d7b4cSMark Brown 			reg = 0;
630913d7b4cSMark Brown 			if (wm8960->lout1 && wm8960->lout1->power)
631913d7b4cSMark Brown 				reg |= WM8960_PWR2_LOUT1;
632913d7b4cSMark Brown 			if (wm8960->rout1 && wm8960->rout1->power)
633913d7b4cSMark Brown 				reg |= WM8960_PWR2_ROUT1;
634913d7b4cSMark Brown 			if (wm8960->out3 && wm8960->out3->power)
635913d7b4cSMark Brown 				reg |= WM8960_PWR2_OUT3;
636913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER2,
637913d7b4cSMark Brown 					    WM8960_PWR2_LOUT1 |
638913d7b4cSMark Brown 					    WM8960_PWR2_ROUT1 |
639913d7b4cSMark Brown 					    WM8960_PWR2_OUT3, reg);
640913d7b4cSMark Brown 
641913d7b4cSMark Brown 			/* Enable VMID at 2*50k */
642913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
643913d7b4cSMark Brown 					    WM8960_VMID_MASK, 0x80);
644913d7b4cSMark Brown 
645913d7b4cSMark Brown 			/* Ramp */
646913d7b4cSMark Brown 			msleep(100);
647913d7b4cSMark Brown 
648913d7b4cSMark Brown 			/* Enable VREF */
649913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
650913d7b4cSMark Brown 					    WM8960_VREF, WM8960_VREF);
651913d7b4cSMark Brown 
652913d7b4cSMark Brown 			msleep(100);
653913d7b4cSMark Brown 			break;
654913d7b4cSMark Brown 
655913d7b4cSMark Brown 		case SND_SOC_BIAS_ON:
656913d7b4cSMark Brown 			/* Enable anti-pop mode */
657913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
658913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
659913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
660913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
661913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
662913d7b4cSMark Brown 
663913d7b4cSMark Brown 			/* Disable VMID and VREF */
664913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_POWER1,
665913d7b4cSMark Brown 					    WM8960_VREF | WM8960_VMID_MASK, 0);
666913d7b4cSMark Brown 			break;
667913d7b4cSMark Brown 
668bc45df2dSAxel Lin 		case SND_SOC_BIAS_OFF:
669bc45df2dSAxel Lin 			snd_soc_cache_sync(codec);
670bc45df2dSAxel Lin 			break;
671913d7b4cSMark Brown 		default:
672913d7b4cSMark Brown 			break;
673913d7b4cSMark Brown 		}
674913d7b4cSMark Brown 		break;
675913d7b4cSMark Brown 
676913d7b4cSMark Brown 	case SND_SOC_BIAS_STANDBY:
677ce6120ccSLiam Girdwood 		switch (codec->dapm.bias_level) {
678913d7b4cSMark Brown 		case SND_SOC_BIAS_PREPARE:
679913d7b4cSMark Brown 			/* Disable HP discharge */
680913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP2,
681913d7b4cSMark Brown 					    WM8960_DISOP | WM8960_DRES_MASK,
682913d7b4cSMark Brown 					    0);
683913d7b4cSMark Brown 
684913d7b4cSMark Brown 			/* Disable anti-pop features */
685913d7b4cSMark Brown 			snd_soc_update_bits(codec, WM8960_APOP1,
686913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
687913d7b4cSMark Brown 					    WM8960_BUFDCOPEN,
688913d7b4cSMark Brown 					    WM8960_POBCTRL | WM8960_SOFT_ST |
689913d7b4cSMark Brown 					    WM8960_BUFDCOPEN);
690913d7b4cSMark Brown 			break;
691913d7b4cSMark Brown 
692913d7b4cSMark Brown 		default:
693913d7b4cSMark Brown 			break;
694913d7b4cSMark Brown 		}
695913d7b4cSMark Brown 		break;
696913d7b4cSMark Brown 
697913d7b4cSMark Brown 	case SND_SOC_BIAS_OFF:
698f2644a2cSMark Brown 		break;
699f2644a2cSMark Brown 	}
700f2644a2cSMark Brown 
701ce6120ccSLiam Girdwood 	codec->dapm.bias_level = level;
702f2644a2cSMark Brown 
703f2644a2cSMark Brown 	return 0;
704f2644a2cSMark Brown }
705f2644a2cSMark Brown 
706f2644a2cSMark Brown /* PLL divisors */
707f2644a2cSMark Brown struct _pll_div {
708f2644a2cSMark Brown 	u32 pre_div:1;
709f2644a2cSMark Brown 	u32 n:4;
710f2644a2cSMark Brown 	u32 k:24;
711f2644a2cSMark Brown };
712f2644a2cSMark Brown 
713f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10
714f2644a2cSMark Brown  * to allow rounding later */
715f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
716f2644a2cSMark Brown 
717f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target,
718f2644a2cSMark Brown 		       struct _pll_div *pll_div)
719f2644a2cSMark Brown {
720f2644a2cSMark Brown 	unsigned long long Kpart;
721f2644a2cSMark Brown 	unsigned int K, Ndiv, Nmod;
722f2644a2cSMark Brown 
723f2644a2cSMark Brown 	pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
724f2644a2cSMark Brown 
725f2644a2cSMark Brown 	/* Scale up target to PLL operating frequency */
726f2644a2cSMark Brown 	target *= 4;
727f2644a2cSMark Brown 
728f2644a2cSMark Brown 	Ndiv = target / source;
729f2644a2cSMark Brown 	if (Ndiv < 6) {
730f2644a2cSMark Brown 		source >>= 1;
731f2644a2cSMark Brown 		pll_div->pre_div = 1;
732f2644a2cSMark Brown 		Ndiv = target / source;
733f2644a2cSMark Brown 	} else
734f2644a2cSMark Brown 		pll_div->pre_div = 0;
735f2644a2cSMark Brown 
736f2644a2cSMark Brown 	if ((Ndiv < 6) || (Ndiv > 12)) {
737f2644a2cSMark Brown 		pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
738f2644a2cSMark Brown 		return -EINVAL;
739f2644a2cSMark Brown 	}
740f2644a2cSMark Brown 
741f2644a2cSMark Brown 	pll_div->n = Ndiv;
742f2644a2cSMark Brown 	Nmod = target % source;
743f2644a2cSMark Brown 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
744f2644a2cSMark Brown 
745f2644a2cSMark Brown 	do_div(Kpart, source);
746f2644a2cSMark Brown 
747f2644a2cSMark Brown 	K = Kpart & 0xFFFFFFFF;
748f2644a2cSMark Brown 
749f2644a2cSMark Brown 	/* Check if we need to round */
750f2644a2cSMark Brown 	if ((K % 10) >= 5)
751f2644a2cSMark Brown 		K += 5;
752f2644a2cSMark Brown 
753f2644a2cSMark Brown 	/* Move down to proper range now rounding is done */
754f2644a2cSMark Brown 	K /= 10;
755f2644a2cSMark Brown 
756f2644a2cSMark Brown 	pll_div->k = K;
757f2644a2cSMark Brown 
758f2644a2cSMark Brown 	pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
759f2644a2cSMark Brown 		 pll_div->n, pll_div->k, pll_div->pre_div);
760f2644a2cSMark Brown 
761f2644a2cSMark Brown 	return 0;
762f2644a2cSMark Brown }
763f2644a2cSMark Brown 
76485488037SMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
76585488037SMark Brown 		int source, unsigned int freq_in, unsigned int freq_out)
766f2644a2cSMark Brown {
767f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
768f2644a2cSMark Brown 	u16 reg;
769f2644a2cSMark Brown 	static struct _pll_div pll_div;
770f2644a2cSMark Brown 	int ret;
771f2644a2cSMark Brown 
772f2644a2cSMark Brown 	if (freq_in && freq_out) {
773f2644a2cSMark Brown 		ret = pll_factors(freq_in, freq_out, &pll_div);
774f2644a2cSMark Brown 		if (ret != 0)
775f2644a2cSMark Brown 			return ret;
776f2644a2cSMark Brown 	}
777f2644a2cSMark Brown 
778f2644a2cSMark Brown 	/* Disable the PLL: even if we are changing the frequency the
779f2644a2cSMark Brown 	 * PLL needs to be disabled while we do so. */
78016b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0);
78116b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0);
782f2644a2cSMark Brown 
783f2644a2cSMark Brown 	if (!freq_in || !freq_out)
784f2644a2cSMark Brown 		return 0;
785f2644a2cSMark Brown 
78617a52fd6SMark Brown 	reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
787f2644a2cSMark Brown 	reg |= pll_div.pre_div << 4;
788f2644a2cSMark Brown 	reg |= pll_div.n;
789f2644a2cSMark Brown 
790f2644a2cSMark Brown 	if (pll_div.k) {
791f2644a2cSMark Brown 		reg |= 0x20;
792f2644a2cSMark Brown 
79317a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
79417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
79517a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
796f2644a2cSMark Brown 	}
79717a52fd6SMark Brown 	snd_soc_write(codec, WM8960_PLL1, reg);
798f2644a2cSMark Brown 
799f2644a2cSMark Brown 	/* Turn it on */
80016b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1);
801f2644a2cSMark Brown 	msleep(250);
80216b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1);
803f2644a2cSMark Brown 
804f2644a2cSMark Brown 	return 0;
805f2644a2cSMark Brown }
806f2644a2cSMark Brown 
807f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
808f2644a2cSMark Brown 		int div_id, int div)
809f2644a2cSMark Brown {
810f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
811f2644a2cSMark Brown 	u16 reg;
812f2644a2cSMark Brown 
813f2644a2cSMark Brown 	switch (div_id) {
814f2644a2cSMark Brown 	case WM8960_SYSCLKDIV:
81517a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
81617a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
817f2644a2cSMark Brown 		break;
818f2644a2cSMark Brown 	case WM8960_DACDIV:
81917a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
82017a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
821f2644a2cSMark Brown 		break;
822f2644a2cSMark Brown 	case WM8960_OPCLKDIV:
82317a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
82417a52fd6SMark Brown 		snd_soc_write(codec, WM8960_PLL1, reg | div);
825f2644a2cSMark Brown 		break;
826f2644a2cSMark Brown 	case WM8960_DCLKDIV:
82717a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
82817a52fd6SMark Brown 		snd_soc_write(codec, WM8960_CLOCK2, reg | div);
829f2644a2cSMark Brown 		break;
830f2644a2cSMark Brown 	case WM8960_TOCLKSEL:
83117a52fd6SMark Brown 		reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
83217a52fd6SMark Brown 		snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
833f2644a2cSMark Brown 		break;
834f2644a2cSMark Brown 	default:
835f2644a2cSMark Brown 		return -EINVAL;
836f2644a2cSMark Brown 	}
837f2644a2cSMark Brown 
838f2644a2cSMark Brown 	return 0;
839f2644a2cSMark Brown }
840f2644a2cSMark Brown 
841f0fba2adSLiam Girdwood static int wm8960_set_bias_level(struct snd_soc_codec *codec,
842f0fba2adSLiam Girdwood 				 enum snd_soc_bias_level level)
843f0fba2adSLiam Girdwood {
844f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
845f0fba2adSLiam Girdwood 
846f0fba2adSLiam Girdwood 	return wm8960->set_bias_level(codec, level);
847f0fba2adSLiam Girdwood }
848f0fba2adSLiam Girdwood 
849f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
850f2644a2cSMark Brown 
851f2644a2cSMark Brown #define WM8960_FORMATS \
852f2644a2cSMark Brown 	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
853f2644a2cSMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
854f2644a2cSMark Brown 
85585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8960_dai_ops = {
856f2644a2cSMark Brown 	.hw_params = wm8960_hw_params,
857f2644a2cSMark Brown 	.digital_mute = wm8960_mute,
858f2644a2cSMark Brown 	.set_fmt = wm8960_set_dai_fmt,
859f2644a2cSMark Brown 	.set_clkdiv = wm8960_set_dai_clkdiv,
860f2644a2cSMark Brown 	.set_pll = wm8960_set_dai_pll,
861f2644a2cSMark Brown };
862f2644a2cSMark Brown 
863f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8960_dai = {
864f0fba2adSLiam Girdwood 	.name = "wm8960-hifi",
865f2644a2cSMark Brown 	.playback = {
866f2644a2cSMark Brown 		.stream_name = "Playback",
867f2644a2cSMark Brown 		.channels_min = 1,
868f2644a2cSMark Brown 		.channels_max = 2,
869f2644a2cSMark Brown 		.rates = WM8960_RATES,
870f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
871f2644a2cSMark Brown 	.capture = {
872f2644a2cSMark Brown 		.stream_name = "Capture",
873f2644a2cSMark Brown 		.channels_min = 1,
874f2644a2cSMark Brown 		.channels_max = 2,
875f2644a2cSMark Brown 		.rates = WM8960_RATES,
876f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
877f2644a2cSMark Brown 	.ops = &wm8960_dai_ops,
878f2644a2cSMark Brown 	.symmetric_rates = 1,
879f2644a2cSMark Brown };
880f2644a2cSMark Brown 
88184b315eeSLars-Peter Clausen static int wm8960_suspend(struct snd_soc_codec *codec)
882f2644a2cSMark Brown {
883f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
884f2644a2cSMark Brown 
885f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
886f2644a2cSMark Brown 	return 0;
887f2644a2cSMark Brown }
888f2644a2cSMark Brown 
889f0fba2adSLiam Girdwood static int wm8960_resume(struct snd_soc_codec *codec)
890f2644a2cSMark Brown {
891f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
892f2644a2cSMark Brown 
893f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
894f2644a2cSMark Brown 	return 0;
895f2644a2cSMark Brown }
896f2644a2cSMark Brown 
897f0fba2adSLiam Girdwood static int wm8960_probe(struct snd_soc_codec *codec)
898f2644a2cSMark Brown {
899f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
900f0fba2adSLiam Girdwood 	struct wm8960_data *pdata = dev_get_platdata(codec->dev);
901f2644a2cSMark Brown 	int ret;
902f2644a2cSMark Brown 
903f0fba2adSLiam Girdwood 	wm8960->set_bias_level = wm8960_set_bias_level_out3;
904913d7b4cSMark Brown 
905f2644a2cSMark Brown 	if (!pdata) {
906f2644a2cSMark Brown 		dev_warn(codec->dev, "No platform data supplied\n");
907f2644a2cSMark Brown 	} else {
908f2644a2cSMark Brown 		if (pdata->dres > WM8960_DRES_MAX) {
909f2644a2cSMark Brown 			dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
910f2644a2cSMark Brown 			pdata->dres = 0;
911f2644a2cSMark Brown 		}
912913d7b4cSMark Brown 
913913d7b4cSMark Brown 		if (pdata->capless)
914f0fba2adSLiam Girdwood 			wm8960->set_bias_level = wm8960_set_bias_level_capless;
915f2644a2cSMark Brown 	}
916f2644a2cSMark Brown 
917f0fba2adSLiam Girdwood 	ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8960->control_type);
91817a52fd6SMark Brown 	if (ret < 0) {
91917a52fd6SMark Brown 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
920f0fba2adSLiam Girdwood 		return ret;
92117a52fd6SMark Brown 	}
92217a52fd6SMark Brown 
923f2644a2cSMark Brown 	ret = wm8960_reset(codec);
924f2644a2cSMark Brown 	if (ret < 0) {
925f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to issue reset\n");
926f0fba2adSLiam Girdwood 		return ret;
927f2644a2cSMark Brown 	}
928f2644a2cSMark Brown 
929f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
930f2644a2cSMark Brown 
931f2644a2cSMark Brown 	/* Latch the update bits */
93216b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100);
93316b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100);
93416b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100);
93516b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100);
93616b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100);
93716b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100);
93816b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100);
93916b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100);
94016b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100);
94116b24881SAxel Lin 	snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100);
942f2644a2cSMark Brown 
943f0fba2adSLiam Girdwood 	snd_soc_add_controls(codec, wm8960_snd_controls,
944f0fba2adSLiam Girdwood 				     ARRAY_SIZE(wm8960_snd_controls));
945f0fba2adSLiam Girdwood 	wm8960_add_widgets(codec);
946f2644a2cSMark Brown 
947f2644a2cSMark Brown 	return 0;
948f2644a2cSMark Brown }
949f2644a2cSMark Brown 
950f0fba2adSLiam Girdwood /* power down chip */
951f0fba2adSLiam Girdwood static int wm8960_remove(struct snd_soc_codec *codec)
952f2644a2cSMark Brown {
953f0fba2adSLiam Girdwood 	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
954f0fba2adSLiam Girdwood 
955f0fba2adSLiam Girdwood 	wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
956f0fba2adSLiam Girdwood 	return 0;
957f2644a2cSMark Brown }
958f2644a2cSMark Brown 
959f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
960f0fba2adSLiam Girdwood 	.probe =	wm8960_probe,
961f0fba2adSLiam Girdwood 	.remove =	wm8960_remove,
962f0fba2adSLiam Girdwood 	.suspend =	wm8960_suspend,
963f0fba2adSLiam Girdwood 	.resume =	wm8960_resume,
964f0fba2adSLiam Girdwood 	.set_bias_level = wm8960_set_bias_level,
965f0fba2adSLiam Girdwood 	.reg_cache_size = ARRAY_SIZE(wm8960_reg),
966f0fba2adSLiam Girdwood 	.reg_word_size = sizeof(u16),
967f0fba2adSLiam Girdwood 	.reg_cache_default = wm8960_reg,
968f0fba2adSLiam Girdwood };
969f0fba2adSLiam Girdwood 
970f2644a2cSMark Brown static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
971f2644a2cSMark Brown 				      const struct i2c_device_id *id)
972f2644a2cSMark Brown {
973f2644a2cSMark Brown 	struct wm8960_priv *wm8960;
974f0fba2adSLiam Girdwood 	int ret;
975f2644a2cSMark Brown 
976b9791c01SMark Brown 	wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv),
977b9791c01SMark Brown 			      GFP_KERNEL);
978f2644a2cSMark Brown 	if (wm8960 == NULL)
979f2644a2cSMark Brown 		return -ENOMEM;
980f2644a2cSMark Brown 
981f2644a2cSMark Brown 	i2c_set_clientdata(i2c, wm8960);
9827f984b55SLars-Peter Clausen 	wm8960->control_type = SND_SOC_I2C;
983f2644a2cSMark Brown 
984f0fba2adSLiam Girdwood 	ret = snd_soc_register_codec(&i2c->dev,
985f0fba2adSLiam Girdwood 			&soc_codec_dev_wm8960, &wm8960_dai, 1);
986b9791c01SMark Brown 
987f0fba2adSLiam Girdwood 	return ret;
988f2644a2cSMark Brown }
989f2644a2cSMark Brown 
990f2644a2cSMark Brown static __devexit int wm8960_i2c_remove(struct i2c_client *client)
991f2644a2cSMark Brown {
992f0fba2adSLiam Girdwood 	snd_soc_unregister_codec(&client->dev);
993f2644a2cSMark Brown 	return 0;
994f2644a2cSMark Brown }
995f2644a2cSMark Brown 
996f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = {
997f2644a2cSMark Brown 	{ "wm8960", 0 },
998f2644a2cSMark Brown 	{ }
999f2644a2cSMark Brown };
1000f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
1001f2644a2cSMark Brown 
1002f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = {
1003f2644a2cSMark Brown 	.driver = {
1004091edccfSMark Brown 		.name = "wm8960",
1005f2644a2cSMark Brown 		.owner = THIS_MODULE,
1006f2644a2cSMark Brown 	},
1007f2644a2cSMark Brown 	.probe =    wm8960_i2c_probe,
1008f2644a2cSMark Brown 	.remove =   __devexit_p(wm8960_i2c_remove),
1009f2644a2cSMark Brown 	.id_table = wm8960_i2c_id,
1010f2644a2cSMark Brown };
1011f2644a2cSMark Brown 
1012f2644a2cSMark Brown static int __init wm8960_modinit(void)
1013f2644a2cSMark Brown {
1014f0fba2adSLiam Girdwood 	int ret = 0;
1015f2644a2cSMark Brown 	ret = i2c_add_driver(&wm8960_i2c_driver);
1016f2644a2cSMark Brown 	if (ret != 0) {
1017f2644a2cSMark Brown 		printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
1018f2644a2cSMark Brown 		       ret);
1019f2644a2cSMark Brown 	}
1020f2644a2cSMark Brown 	return ret;
1021f2644a2cSMark Brown }
1022f2644a2cSMark Brown module_init(wm8960_modinit);
1023f2644a2cSMark Brown 
1024f2644a2cSMark Brown static void __exit wm8960_exit(void)
1025f2644a2cSMark Brown {
1026f2644a2cSMark Brown 	i2c_del_driver(&wm8960_i2c_driver);
1027f2644a2cSMark Brown }
1028f2644a2cSMark Brown module_exit(wm8960_exit);
1029f2644a2cSMark Brown 
1030f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver");
1031f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood");
1032f2644a2cSMark Brown MODULE_LICENSE("GPL");
1033