xref: /openbmc/linux/sound/soc/codecs/wm8960.c (revision f2644a2c)
1f2644a2cSMark Brown /*
2f2644a2cSMark Brown  * wm8960.c  --  WM8960 ALSA SoC Audio driver
3f2644a2cSMark Brown  *
4f2644a2cSMark Brown  * Author: Liam Girdwood
5f2644a2cSMark Brown  *
6f2644a2cSMark Brown  * This program is free software; you can redistribute it and/or modify
7f2644a2cSMark Brown  * it under the terms of the GNU General Public License version 2 as
8f2644a2cSMark Brown  * published by the Free Software Foundation.
9f2644a2cSMark Brown  */
10f2644a2cSMark Brown 
11f2644a2cSMark Brown #include <linux/module.h>
12f2644a2cSMark Brown #include <linux/moduleparam.h>
13f2644a2cSMark Brown #include <linux/init.h>
14f2644a2cSMark Brown #include <linux/delay.h>
15f2644a2cSMark Brown #include <linux/pm.h>
16f2644a2cSMark Brown #include <linux/i2c.h>
17f2644a2cSMark Brown #include <linux/platform_device.h>
18f2644a2cSMark Brown #include <sound/core.h>
19f2644a2cSMark Brown #include <sound/pcm.h>
20f2644a2cSMark Brown #include <sound/pcm_params.h>
21f2644a2cSMark Brown #include <sound/soc.h>
22f2644a2cSMark Brown #include <sound/soc-dapm.h>
23f2644a2cSMark Brown #include <sound/initval.h>
24f2644a2cSMark Brown #include <sound/tlv.h>
25f2644a2cSMark Brown 
26f2644a2cSMark Brown #include "wm8960.h"
27f2644a2cSMark Brown 
28f2644a2cSMark Brown #define AUDIO_NAME "wm8960"
29f2644a2cSMark Brown 
30f2644a2cSMark Brown struct snd_soc_codec_device soc_codec_dev_wm8960;
31f2644a2cSMark Brown 
32f2644a2cSMark Brown /* R25 - Power 1 */
33f2644a2cSMark Brown #define WM8960_VREF      0x40
34f2644a2cSMark Brown 
35f2644a2cSMark Brown /* R28 - Anti-pop 1 */
36f2644a2cSMark Brown #define WM8960_POBCTRL   0x80
37f2644a2cSMark Brown #define WM8960_BUFDCOPEN 0x10
38f2644a2cSMark Brown #define WM8960_BUFIOEN   0x08
39f2644a2cSMark Brown #define WM8960_SOFT_ST   0x04
40f2644a2cSMark Brown #define WM8960_HPSTBY    0x01
41f2644a2cSMark Brown 
42f2644a2cSMark Brown /* R29 - Anti-pop 2 */
43f2644a2cSMark Brown #define WM8960_DISOP     0x40
44f2644a2cSMark Brown 
45f2644a2cSMark Brown /*
46f2644a2cSMark Brown  * wm8960 register cache
47f2644a2cSMark Brown  * We can't read the WM8960 register space when we are
48f2644a2cSMark Brown  * using 2 wire for device control, so we cache them instead.
49f2644a2cSMark Brown  */
50f2644a2cSMark Brown static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
51f2644a2cSMark Brown 	0x0097, 0x0097, 0x0000, 0x0000,
52f2644a2cSMark Brown 	0x0000, 0x0008, 0x0000, 0x000a,
53f2644a2cSMark Brown 	0x01c0, 0x0000, 0x00ff, 0x00ff,
54f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
55f2644a2cSMark Brown 	0x0000, 0x007b, 0x0100, 0x0032,
56f2644a2cSMark Brown 	0x0000, 0x00c3, 0x00c3, 0x01c0,
57f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
58f2644a2cSMark Brown 	0x0000, 0x0000, 0x0000, 0x0000,
59f2644a2cSMark Brown 	0x0100, 0x0100, 0x0050, 0x0050,
60f2644a2cSMark Brown 	0x0050, 0x0050, 0x0000, 0x0000,
61f2644a2cSMark Brown 	0x0000, 0x0000, 0x0040, 0x0000,
62f2644a2cSMark Brown 	0x0000, 0x0050, 0x0050, 0x0000,
63f2644a2cSMark Brown 	0x0002, 0x0037, 0x004d, 0x0080,
64f2644a2cSMark Brown 	0x0008, 0x0031, 0x0026, 0x00e9,
65f2644a2cSMark Brown };
66f2644a2cSMark Brown 
67f2644a2cSMark Brown struct wm8960_priv {
68f2644a2cSMark Brown 	u16 reg_cache[WM8960_CACHEREGNUM];
69f2644a2cSMark Brown 	struct snd_soc_codec codec;
70f2644a2cSMark Brown };
71f2644a2cSMark Brown 
72f2644a2cSMark Brown /*
73f2644a2cSMark Brown  * read wm8960 register cache
74f2644a2cSMark Brown  */
75f2644a2cSMark Brown static inline unsigned int wm8960_read_reg_cache(struct snd_soc_codec *codec,
76f2644a2cSMark Brown 	unsigned int reg)
77f2644a2cSMark Brown {
78f2644a2cSMark Brown 	u16 *cache = codec->reg_cache;
79f2644a2cSMark Brown 	if (reg == WM8960_RESET)
80f2644a2cSMark Brown 		return 0;
81f2644a2cSMark Brown 	if (reg >= WM8960_CACHEREGNUM)
82f2644a2cSMark Brown 		return -1;
83f2644a2cSMark Brown 	return cache[reg];
84f2644a2cSMark Brown }
85f2644a2cSMark Brown 
86f2644a2cSMark Brown /*
87f2644a2cSMark Brown  * write wm8960 register cache
88f2644a2cSMark Brown  */
89f2644a2cSMark Brown static inline void wm8960_write_reg_cache(struct snd_soc_codec *codec,
90f2644a2cSMark Brown 	u16 reg, unsigned int value)
91f2644a2cSMark Brown {
92f2644a2cSMark Brown 	u16 *cache = codec->reg_cache;
93f2644a2cSMark Brown 	if (reg >= WM8960_CACHEREGNUM)
94f2644a2cSMark Brown 		return;
95f2644a2cSMark Brown 	cache[reg] = value;
96f2644a2cSMark Brown }
97f2644a2cSMark Brown 
98f2644a2cSMark Brown static inline unsigned int wm8960_read(struct snd_soc_codec *codec,
99f2644a2cSMark Brown 	unsigned int reg)
100f2644a2cSMark Brown {
101f2644a2cSMark Brown 	return wm8960_read_reg_cache(codec, reg);
102f2644a2cSMark Brown }
103f2644a2cSMark Brown 
104f2644a2cSMark Brown /*
105f2644a2cSMark Brown  * write to the WM8960 register space
106f2644a2cSMark Brown  */
107f2644a2cSMark Brown static int wm8960_write(struct snd_soc_codec *codec, unsigned int reg,
108f2644a2cSMark Brown 	unsigned int value)
109f2644a2cSMark Brown {
110f2644a2cSMark Brown 	u8 data[2];
111f2644a2cSMark Brown 
112f2644a2cSMark Brown 	/* data is
113f2644a2cSMark Brown 	 *   D15..D9 WM8960 register offset
114f2644a2cSMark Brown 	 *   D8...D0 register data
115f2644a2cSMark Brown 	 */
116f2644a2cSMark Brown 	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
117f2644a2cSMark Brown 	data[1] = value & 0x00ff;
118f2644a2cSMark Brown 
119f2644a2cSMark Brown 	wm8960_write_reg_cache(codec, reg, value);
120f2644a2cSMark Brown 	if (codec->hw_write(codec->control_data, data, 2) == 2)
121f2644a2cSMark Brown 		return 0;
122f2644a2cSMark Brown 	else
123f2644a2cSMark Brown 		return -EIO;
124f2644a2cSMark Brown }
125f2644a2cSMark Brown 
126f2644a2cSMark Brown #define wm8960_reset(c)	wm8960_write(c, WM8960_RESET, 0)
127f2644a2cSMark Brown 
128f2644a2cSMark Brown /* enumerated controls */
129f2644a2cSMark Brown static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
130f2644a2cSMark Brown static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
131f2644a2cSMark Brown 	"Right Inverted", "Stereo Inversion"};
132f2644a2cSMark Brown static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
133f2644a2cSMark Brown static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
134f2644a2cSMark Brown static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
135f2644a2cSMark Brown static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
136f2644a2cSMark Brown 
137f2644a2cSMark Brown static const struct soc_enum wm8960_enum[] = {
138f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
139f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
140f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
141f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
142f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
143f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
144f2644a2cSMark Brown 	SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
145f2644a2cSMark Brown };
146f2644a2cSMark Brown 
147f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
148f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
149f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
150f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
151f2644a2cSMark Brown 
152f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = {
153f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
154f2644a2cSMark Brown 		 0, 63, 0, adc_tlv),
155f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
156f2644a2cSMark Brown 	6, 1, 0),
157f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
158f2644a2cSMark Brown 	7, 1, 0),
159f2644a2cSMark Brown 
160f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
161f2644a2cSMark Brown 		 0, 255, 0, dac_tlv),
162f2644a2cSMark Brown 
163f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
164f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
165f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
166f2644a2cSMark Brown 	7, 1, 0),
167f2644a2cSMark Brown 
168f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
169f2644a2cSMark Brown 		 0, 127, 0, out_tlv),
170f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
171f2644a2cSMark Brown 	7, 1, 0),
172f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
173f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
174f2644a2cSMark Brown 
175f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
176f2644a2cSMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[1]),
177f2644a2cSMark Brown SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
178f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
179f2644a2cSMark Brown 
180f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]),
181f2644a2cSMark Brown 
182f2644a2cSMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
183f2644a2cSMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
184f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
185f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
186f2644a2cSMark Brown 
187f2644a2cSMark Brown SOC_ENUM("ALC Function", wm8960_enum[5]),
188f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
189f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
190f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
191f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
192f2644a2cSMark Brown SOC_ENUM("ALC Mode", wm8960_enum[6]),
193f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
194f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
195f2644a2cSMark Brown 
196f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
197f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
198f2644a2cSMark Brown 
199f2644a2cSMark Brown SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
200f2644a2cSMark Brown 	0, 127, 0),
201f2644a2cSMark Brown 
202f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
203f2644a2cSMark Brown 	       WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
204f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
205f2644a2cSMark Brown 	       WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
206f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
207f2644a2cSMark Brown 	       WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
208f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
209f2644a2cSMark Brown 	       WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
210f2644a2cSMark Brown };
211f2644a2cSMark Brown 
212f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = {
213f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
214f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
215f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
216f2644a2cSMark Brown };
217f2644a2cSMark Brown 
218f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = {
219f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
220f2644a2cSMark Brown };
221f2644a2cSMark Brown 
222f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = {
223f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
224f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
225f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
226f2644a2cSMark Brown };
227f2644a2cSMark Brown 
228f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = {
229f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
230f2644a2cSMark Brown };
231f2644a2cSMark Brown 
232f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
233f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
234f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
235f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
236f2644a2cSMark Brown };
237f2644a2cSMark Brown 
238f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
239f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
240f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
241f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
242f2644a2cSMark Brown };
243f2644a2cSMark Brown 
244f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = {
245f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
246f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
247f2644a2cSMark Brown };
248f2644a2cSMark Brown 
249f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
250f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"),
251f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"),
252f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"),
253f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"),
254f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"),
255f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"),
256f2644a2cSMark Brown 
257f2644a2cSMark Brown SND_SOC_DAPM_MICBIAS("MICB", WM8960_POWER1, 1, 0),
258f2644a2cSMark Brown 
259f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
260f2644a2cSMark Brown 		   wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
261f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
262f2644a2cSMark Brown 		   wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
263f2644a2cSMark Brown 
264f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
265f2644a2cSMark Brown 		   wm8960_lin, ARRAY_SIZE(wm8960_lin)),
266f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
267f2644a2cSMark Brown 		   wm8960_rin, ARRAY_SIZE(wm8960_rin)),
268f2644a2cSMark Brown 
269f2644a2cSMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0),
270f2644a2cSMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0),
271f2644a2cSMark Brown 
272f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
273f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
274f2644a2cSMark Brown 
275f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
276f2644a2cSMark Brown 	&wm8960_loutput_mixer[0],
277f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_loutput_mixer)),
278f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
279f2644a2cSMark Brown 	&wm8960_routput_mixer[0],
280f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_routput_mixer)),
281f2644a2cSMark Brown 
282f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
283f2644a2cSMark Brown 	&wm8960_mono_out[0],
284f2644a2cSMark Brown 	ARRAY_SIZE(wm8960_mono_out)),
285f2644a2cSMark Brown 
286f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
287f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
288f2644a2cSMark Brown 
289f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
290f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
291f2644a2cSMark Brown 
292f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
293f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
294f2644a2cSMark Brown 
295f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"),
296f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"),
297f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"),
298f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"),
299f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"),
300f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"),
301f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"),
302f2644a2cSMark Brown };
303f2644a2cSMark Brown 
304f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = {
305f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
306f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
307f2644a2cSMark Brown 	{ "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
308f2644a2cSMark Brown 
309f2644a2cSMark Brown 	{ "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
310f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT1", },  /* Really Boost Switch */
311f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT2" },
312f2644a2cSMark Brown 	{ "Left Input Mixer", NULL, "LINPUT3" },
313f2644a2cSMark Brown 
314f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
315f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
316f2644a2cSMark Brown 	{ "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
317f2644a2cSMark Brown 
318f2644a2cSMark Brown 	{ "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
319f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT1", },  /* Really Boost Switch */
320f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "RINPUT2" },
321f2644a2cSMark Brown 	{ "Right Input Mixer", NULL, "LINPUT3" },
322f2644a2cSMark Brown 
323f2644a2cSMark Brown 	{ "Left ADC", NULL, "Left Input Mixer" },
324f2644a2cSMark Brown 	{ "Right ADC", NULL, "Right Input Mixer" },
325f2644a2cSMark Brown 
326f2644a2cSMark Brown 	{ "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
327f2644a2cSMark Brown 	{ "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
328f2644a2cSMark Brown 	{ "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
329f2644a2cSMark Brown 
330f2644a2cSMark Brown 	{ "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
331f2644a2cSMark Brown 	{ "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
332f2644a2cSMark Brown 	{ "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
333f2644a2cSMark Brown 
334f2644a2cSMark Brown 	{ "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
335f2644a2cSMark Brown 	{ "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
336f2644a2cSMark Brown 
337f2644a2cSMark Brown 	{ "LOUT1 PGA", NULL, "Left Output Mixer" },
338f2644a2cSMark Brown 	{ "ROUT1 PGA", NULL, "Right Output Mixer" },
339f2644a2cSMark Brown 
340f2644a2cSMark Brown 	{ "HP_L", NULL, "LOUT1 PGA" },
341f2644a2cSMark Brown 	{ "HP_R", NULL, "ROUT1 PGA" },
342f2644a2cSMark Brown 
343f2644a2cSMark Brown 	{ "Left Speaker PGA", NULL, "Left Output Mixer" },
344f2644a2cSMark Brown 	{ "Right Speaker PGA", NULL, "Right Output Mixer" },
345f2644a2cSMark Brown 
346f2644a2cSMark Brown 	{ "Left Speaker Output", NULL, "Left Speaker PGA" },
347f2644a2cSMark Brown 	{ "Right Speaker Output", NULL, "Right Speaker PGA" },
348f2644a2cSMark Brown 
349f2644a2cSMark Brown 	{ "SPK_LN", NULL, "Left Speaker Output" },
350f2644a2cSMark Brown 	{ "SPK_LP", NULL, "Left Speaker Output" },
351f2644a2cSMark Brown 	{ "SPK_RN", NULL, "Right Speaker Output" },
352f2644a2cSMark Brown 	{ "SPK_RP", NULL, "Right Speaker Output" },
353f2644a2cSMark Brown 
354f2644a2cSMark Brown 	{ "OUT3", NULL, "Mono Output Mixer", }
355f2644a2cSMark Brown };
356f2644a2cSMark Brown 
357f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec)
358f2644a2cSMark Brown {
359f2644a2cSMark Brown 	snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
360f2644a2cSMark Brown 				  ARRAY_SIZE(wm8960_dapm_widgets));
361f2644a2cSMark Brown 
362f2644a2cSMark Brown 	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
363f2644a2cSMark Brown 
364f2644a2cSMark Brown 	snd_soc_dapm_new_widgets(codec);
365f2644a2cSMark Brown 	return 0;
366f2644a2cSMark Brown }
367f2644a2cSMark Brown 
368f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
369f2644a2cSMark Brown 		unsigned int fmt)
370f2644a2cSMark Brown {
371f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
372f2644a2cSMark Brown 	u16 iface = 0;
373f2644a2cSMark Brown 
374f2644a2cSMark Brown 	/* set master/slave audio interface */
375f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
376f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBM_CFM:
377f2644a2cSMark Brown 		iface |= 0x0040;
378f2644a2cSMark Brown 		break;
379f2644a2cSMark Brown 	case SND_SOC_DAIFMT_CBS_CFS:
380f2644a2cSMark Brown 		break;
381f2644a2cSMark Brown 	default:
382f2644a2cSMark Brown 		return -EINVAL;
383f2644a2cSMark Brown 	}
384f2644a2cSMark Brown 
385f2644a2cSMark Brown 	/* interface format */
386f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
387f2644a2cSMark Brown 	case SND_SOC_DAIFMT_I2S:
388f2644a2cSMark Brown 		iface |= 0x0002;
389f2644a2cSMark Brown 		break;
390f2644a2cSMark Brown 	case SND_SOC_DAIFMT_RIGHT_J:
391f2644a2cSMark Brown 		break;
392f2644a2cSMark Brown 	case SND_SOC_DAIFMT_LEFT_J:
393f2644a2cSMark Brown 		iface |= 0x0001;
394f2644a2cSMark Brown 		break;
395f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_A:
396f2644a2cSMark Brown 		iface |= 0x0003;
397f2644a2cSMark Brown 		break;
398f2644a2cSMark Brown 	case SND_SOC_DAIFMT_DSP_B:
399f2644a2cSMark Brown 		iface |= 0x0013;
400f2644a2cSMark Brown 		break;
401f2644a2cSMark Brown 	default:
402f2644a2cSMark Brown 		return -EINVAL;
403f2644a2cSMark Brown 	}
404f2644a2cSMark Brown 
405f2644a2cSMark Brown 	/* clock inversion */
406f2644a2cSMark Brown 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
407f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_NF:
408f2644a2cSMark Brown 		break;
409f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_IF:
410f2644a2cSMark Brown 		iface |= 0x0090;
411f2644a2cSMark Brown 		break;
412f2644a2cSMark Brown 	case SND_SOC_DAIFMT_IB_NF:
413f2644a2cSMark Brown 		iface |= 0x0080;
414f2644a2cSMark Brown 		break;
415f2644a2cSMark Brown 	case SND_SOC_DAIFMT_NB_IF:
416f2644a2cSMark Brown 		iface |= 0x0010;
417f2644a2cSMark Brown 		break;
418f2644a2cSMark Brown 	default:
419f2644a2cSMark Brown 		return -EINVAL;
420f2644a2cSMark Brown 	}
421f2644a2cSMark Brown 
422f2644a2cSMark Brown 	/* set iface */
423f2644a2cSMark Brown 	wm8960_write(codec, WM8960_IFACE1, iface);
424f2644a2cSMark Brown 	return 0;
425f2644a2cSMark Brown }
426f2644a2cSMark Brown 
427f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream,
428f2644a2cSMark Brown 			    struct snd_pcm_hw_params *params,
429f2644a2cSMark Brown 			    struct snd_soc_dai *dai)
430f2644a2cSMark Brown {
431f2644a2cSMark Brown 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
432f2644a2cSMark Brown 	struct snd_soc_device *socdev = rtd->socdev;
433f2644a2cSMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
434f2644a2cSMark Brown 	u16 iface = wm8960_read(codec, WM8960_IFACE1) & 0xfff3;
435f2644a2cSMark Brown 
436f2644a2cSMark Brown 	/* bit size */
437f2644a2cSMark Brown 	switch (params_format(params)) {
438f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S16_LE:
439f2644a2cSMark Brown 		break;
440f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S20_3LE:
441f2644a2cSMark Brown 		iface |= 0x0004;
442f2644a2cSMark Brown 		break;
443f2644a2cSMark Brown 	case SNDRV_PCM_FORMAT_S24_LE:
444f2644a2cSMark Brown 		iface |= 0x0008;
445f2644a2cSMark Brown 		break;
446f2644a2cSMark Brown 	}
447f2644a2cSMark Brown 
448f2644a2cSMark Brown 	/* set iface */
449f2644a2cSMark Brown 	wm8960_write(codec, WM8960_IFACE1, iface);
450f2644a2cSMark Brown 	return 0;
451f2644a2cSMark Brown }
452f2644a2cSMark Brown 
453f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute)
454f2644a2cSMark Brown {
455f2644a2cSMark Brown 	struct snd_soc_codec *codec = dai->codec;
456f2644a2cSMark Brown 	u16 mute_reg = wm8960_read(codec, WM8960_DACCTL1) & 0xfff7;
457f2644a2cSMark Brown 
458f2644a2cSMark Brown 	if (mute)
459f2644a2cSMark Brown 		wm8960_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
460f2644a2cSMark Brown 	else
461f2644a2cSMark Brown 		wm8960_write(codec, WM8960_DACCTL1, mute_reg);
462f2644a2cSMark Brown 	return 0;
463f2644a2cSMark Brown }
464f2644a2cSMark Brown 
465f2644a2cSMark Brown static int wm8960_set_bias_level(struct snd_soc_codec *codec,
466f2644a2cSMark Brown 				 enum snd_soc_bias_level level)
467f2644a2cSMark Brown {
468f2644a2cSMark Brown 	struct wm8960_data *pdata = codec->dev->platform_data;
469f2644a2cSMark Brown 	u16 reg;
470f2644a2cSMark Brown 
471f2644a2cSMark Brown 	switch (level) {
472f2644a2cSMark Brown 	case SND_SOC_BIAS_ON:
473f2644a2cSMark Brown 		break;
474f2644a2cSMark Brown 
475f2644a2cSMark Brown 	case SND_SOC_BIAS_PREPARE:
476f2644a2cSMark Brown 		/* Set VMID to 2x50k */
477f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_POWER1);
478f2644a2cSMark Brown 		reg &= ~0x180;
479f2644a2cSMark Brown 		reg |= 0x80;
480f2644a2cSMark Brown 		wm8960_write(codec, WM8960_POWER1, reg);
481f2644a2cSMark Brown 		break;
482f2644a2cSMark Brown 
483f2644a2cSMark Brown 	case SND_SOC_BIAS_STANDBY:
484f2644a2cSMark Brown 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
485f2644a2cSMark Brown 			/* Enable anti-pop features */
486f2644a2cSMark Brown 			wm8960_write(codec, WM8960_APOP1,
487f2644a2cSMark Brown 				     WM8960_POBCTRL | WM8960_SOFT_ST |
488f2644a2cSMark Brown 				     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
489f2644a2cSMark Brown 
490f2644a2cSMark Brown 			/* Discharge HP output */
491f2644a2cSMark Brown 			reg = WM8960_DISOP;
492f2644a2cSMark Brown 			if (pdata)
493f2644a2cSMark Brown 				reg |= pdata->dres << 4;
494f2644a2cSMark Brown 			wm8960_write(codec, WM8960_APOP2, reg);
495f2644a2cSMark Brown 
496f2644a2cSMark Brown 			msleep(400);
497f2644a2cSMark Brown 
498f2644a2cSMark Brown 			wm8960_write(codec, WM8960_APOP2, 0);
499f2644a2cSMark Brown 
500f2644a2cSMark Brown 			/* Enable & ramp VMID at 2x50k */
501f2644a2cSMark Brown 			reg = wm8960_read(codec, WM8960_POWER1);
502f2644a2cSMark Brown 			reg |= 0x80;
503f2644a2cSMark Brown 			wm8960_write(codec, WM8960_POWER1, reg);
504f2644a2cSMark Brown 			msleep(100);
505f2644a2cSMark Brown 
506f2644a2cSMark Brown 			/* Enable VREF */
507f2644a2cSMark Brown 			wm8960_write(codec, WM8960_POWER1, reg | WM8960_VREF);
508f2644a2cSMark Brown 
509f2644a2cSMark Brown 			/* Disable anti-pop features */
510f2644a2cSMark Brown 			wm8960_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
511f2644a2cSMark Brown 		}
512f2644a2cSMark Brown 
513f2644a2cSMark Brown 		/* Set VMID to 2x250k */
514f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_POWER1);
515f2644a2cSMark Brown 		reg &= ~0x180;
516f2644a2cSMark Brown 		reg |= 0x100;
517f2644a2cSMark Brown 		wm8960_write(codec, WM8960_POWER1, reg);
518f2644a2cSMark Brown 		break;
519f2644a2cSMark Brown 
520f2644a2cSMark Brown 	case SND_SOC_BIAS_OFF:
521f2644a2cSMark Brown 		/* Enable anti-pop features */
522f2644a2cSMark Brown 		wm8960_write(codec, WM8960_APOP1,
523f2644a2cSMark Brown 			     WM8960_POBCTRL | WM8960_SOFT_ST |
524f2644a2cSMark Brown 			     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
525f2644a2cSMark Brown 
526f2644a2cSMark Brown 		/* Disable VMID and VREF, let them discharge */
527f2644a2cSMark Brown 		wm8960_write(codec, WM8960_POWER1, 0);
528f2644a2cSMark Brown 		msleep(600);
529f2644a2cSMark Brown 
530f2644a2cSMark Brown 		wm8960_write(codec, WM8960_APOP1, 0);
531f2644a2cSMark Brown 		break;
532f2644a2cSMark Brown 	}
533f2644a2cSMark Brown 
534f2644a2cSMark Brown 	codec->bias_level = level;
535f2644a2cSMark Brown 
536f2644a2cSMark Brown 	return 0;
537f2644a2cSMark Brown }
538f2644a2cSMark Brown 
539f2644a2cSMark Brown /* PLL divisors */
540f2644a2cSMark Brown struct _pll_div {
541f2644a2cSMark Brown 	u32 pre_div:1;
542f2644a2cSMark Brown 	u32 n:4;
543f2644a2cSMark Brown 	u32 k:24;
544f2644a2cSMark Brown };
545f2644a2cSMark Brown 
546f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10
547f2644a2cSMark Brown  * to allow rounding later */
548f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
549f2644a2cSMark Brown 
550f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target,
551f2644a2cSMark Brown 		       struct _pll_div *pll_div)
552f2644a2cSMark Brown {
553f2644a2cSMark Brown 	unsigned long long Kpart;
554f2644a2cSMark Brown 	unsigned int K, Ndiv, Nmod;
555f2644a2cSMark Brown 
556f2644a2cSMark Brown 	pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
557f2644a2cSMark Brown 
558f2644a2cSMark Brown 	/* Scale up target to PLL operating frequency */
559f2644a2cSMark Brown 	target *= 4;
560f2644a2cSMark Brown 
561f2644a2cSMark Brown 	Ndiv = target / source;
562f2644a2cSMark Brown 	if (Ndiv < 6) {
563f2644a2cSMark Brown 		source >>= 1;
564f2644a2cSMark Brown 		pll_div->pre_div = 1;
565f2644a2cSMark Brown 		Ndiv = target / source;
566f2644a2cSMark Brown 	} else
567f2644a2cSMark Brown 		pll_div->pre_div = 0;
568f2644a2cSMark Brown 
569f2644a2cSMark Brown 	if ((Ndiv < 6) || (Ndiv > 12)) {
570f2644a2cSMark Brown 		pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
571f2644a2cSMark Brown 		return -EINVAL;
572f2644a2cSMark Brown 	}
573f2644a2cSMark Brown 
574f2644a2cSMark Brown 	pll_div->n = Ndiv;
575f2644a2cSMark Brown 	Nmod = target % source;
576f2644a2cSMark Brown 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
577f2644a2cSMark Brown 
578f2644a2cSMark Brown 	do_div(Kpart, source);
579f2644a2cSMark Brown 
580f2644a2cSMark Brown 	K = Kpart & 0xFFFFFFFF;
581f2644a2cSMark Brown 
582f2644a2cSMark Brown 	/* Check if we need to round */
583f2644a2cSMark Brown 	if ((K % 10) >= 5)
584f2644a2cSMark Brown 		K += 5;
585f2644a2cSMark Brown 
586f2644a2cSMark Brown 	/* Move down to proper range now rounding is done */
587f2644a2cSMark Brown 	K /= 10;
588f2644a2cSMark Brown 
589f2644a2cSMark Brown 	pll_div->k = K;
590f2644a2cSMark Brown 
591f2644a2cSMark Brown 	pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
592f2644a2cSMark Brown 		 pll_div->n, pll_div->k, pll_div->pre_div);
593f2644a2cSMark Brown 
594f2644a2cSMark Brown 	return 0;
595f2644a2cSMark Brown }
596f2644a2cSMark Brown 
597f2644a2cSMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
598f2644a2cSMark Brown 		int pll_id, unsigned int freq_in, unsigned int freq_out)
599f2644a2cSMark Brown {
600f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
601f2644a2cSMark Brown 	u16 reg;
602f2644a2cSMark Brown 	static struct _pll_div pll_div;
603f2644a2cSMark Brown 	int ret;
604f2644a2cSMark Brown 
605f2644a2cSMark Brown 	if (freq_in && freq_out) {
606f2644a2cSMark Brown 		ret = pll_factors(freq_in, freq_out, &pll_div);
607f2644a2cSMark Brown 		if (ret != 0)
608f2644a2cSMark Brown 			return ret;
609f2644a2cSMark Brown 	}
610f2644a2cSMark Brown 
611f2644a2cSMark Brown 	/* Disable the PLL: even if we are changing the frequency the
612f2644a2cSMark Brown 	 * PLL needs to be disabled while we do so. */
613f2644a2cSMark Brown 	wm8960_write(codec, WM8960_CLOCK1,
614f2644a2cSMark Brown 		     wm8960_read(codec, WM8960_CLOCK1) & ~1);
615f2644a2cSMark Brown 	wm8960_write(codec, WM8960_POWER2,
616f2644a2cSMark Brown 		     wm8960_read(codec, WM8960_POWER2) & ~1);
617f2644a2cSMark Brown 
618f2644a2cSMark Brown 	if (!freq_in || !freq_out)
619f2644a2cSMark Brown 		return 0;
620f2644a2cSMark Brown 
621f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_PLL1) & ~0x3f;
622f2644a2cSMark Brown 	reg |= pll_div.pre_div << 4;
623f2644a2cSMark Brown 	reg |= pll_div.n;
624f2644a2cSMark Brown 
625f2644a2cSMark Brown 	if (pll_div.k) {
626f2644a2cSMark Brown 		reg |= 0x20;
627f2644a2cSMark Brown 
628f2644a2cSMark Brown 		wm8960_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
629f2644a2cSMark Brown 		wm8960_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
630f2644a2cSMark Brown 		wm8960_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
631f2644a2cSMark Brown 	}
632f2644a2cSMark Brown 	wm8960_write(codec, WM8960_PLL1, reg);
633f2644a2cSMark Brown 
634f2644a2cSMark Brown 	/* Turn it on */
635f2644a2cSMark Brown 	wm8960_write(codec, WM8960_POWER2,
636f2644a2cSMark Brown 		     wm8960_read(codec, WM8960_POWER2) | 1);
637f2644a2cSMark Brown 	msleep(250);
638f2644a2cSMark Brown 	wm8960_write(codec, WM8960_CLOCK1,
639f2644a2cSMark Brown 		     wm8960_read(codec, WM8960_CLOCK1) | 1);
640f2644a2cSMark Brown 
641f2644a2cSMark Brown 	return 0;
642f2644a2cSMark Brown }
643f2644a2cSMark Brown 
644f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
645f2644a2cSMark Brown 		int div_id, int div)
646f2644a2cSMark Brown {
647f2644a2cSMark Brown 	struct snd_soc_codec *codec = codec_dai->codec;
648f2644a2cSMark Brown 	u16 reg;
649f2644a2cSMark Brown 
650f2644a2cSMark Brown 	switch (div_id) {
651f2644a2cSMark Brown 	case WM8960_SYSCLKSEL:
652f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1fe;
653f2644a2cSMark Brown 		wm8960_write(codec, WM8960_CLOCK1, reg | div);
654f2644a2cSMark Brown 		break;
655f2644a2cSMark Brown 	case WM8960_SYSCLKDIV:
656f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1f9;
657f2644a2cSMark Brown 		wm8960_write(codec, WM8960_CLOCK1, reg | div);
658f2644a2cSMark Brown 		break;
659f2644a2cSMark Brown 	case WM8960_DACDIV:
660f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_CLOCK1) & 0x1c7;
661f2644a2cSMark Brown 		wm8960_write(codec, WM8960_CLOCK1, reg | div);
662f2644a2cSMark Brown 		break;
663f2644a2cSMark Brown 	case WM8960_OPCLKDIV:
664f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_PLL1) & 0x03f;
665f2644a2cSMark Brown 		wm8960_write(codec, WM8960_PLL1, reg | div);
666f2644a2cSMark Brown 		break;
667f2644a2cSMark Brown 	case WM8960_DCLKDIV:
668f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_CLOCK2) & 0x03f;
669f2644a2cSMark Brown 		wm8960_write(codec, WM8960_CLOCK2, reg | div);
670f2644a2cSMark Brown 		break;
671f2644a2cSMark Brown 	case WM8960_TOCLKSEL:
672f2644a2cSMark Brown 		reg = wm8960_read(codec, WM8960_ADDCTL1) & 0x1fd;
673f2644a2cSMark Brown 		wm8960_write(codec, WM8960_ADDCTL1, reg | div);
674f2644a2cSMark Brown 		break;
675f2644a2cSMark Brown 	default:
676f2644a2cSMark Brown 		return -EINVAL;
677f2644a2cSMark Brown 	}
678f2644a2cSMark Brown 
679f2644a2cSMark Brown 	return 0;
680f2644a2cSMark Brown }
681f2644a2cSMark Brown 
682f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
683f2644a2cSMark Brown 
684f2644a2cSMark Brown #define WM8960_FORMATS \
685f2644a2cSMark Brown 	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
686f2644a2cSMark Brown 	SNDRV_PCM_FMTBIT_S24_LE)
687f2644a2cSMark Brown 
688f2644a2cSMark Brown static struct snd_soc_dai_ops wm8960_dai_ops = {
689f2644a2cSMark Brown 	.hw_params = wm8960_hw_params,
690f2644a2cSMark Brown 	.digital_mute = wm8960_mute,
691f2644a2cSMark Brown 	.set_fmt = wm8960_set_dai_fmt,
692f2644a2cSMark Brown 	.set_clkdiv = wm8960_set_dai_clkdiv,
693f2644a2cSMark Brown 	.set_pll = wm8960_set_dai_pll,
694f2644a2cSMark Brown };
695f2644a2cSMark Brown 
696f2644a2cSMark Brown struct snd_soc_dai wm8960_dai = {
697f2644a2cSMark Brown 	.name = "WM8960",
698f2644a2cSMark Brown 	.playback = {
699f2644a2cSMark Brown 		.stream_name = "Playback",
700f2644a2cSMark Brown 		.channels_min = 1,
701f2644a2cSMark Brown 		.channels_max = 2,
702f2644a2cSMark Brown 		.rates = WM8960_RATES,
703f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
704f2644a2cSMark Brown 	.capture = {
705f2644a2cSMark Brown 		.stream_name = "Capture",
706f2644a2cSMark Brown 		.channels_min = 1,
707f2644a2cSMark Brown 		.channels_max = 2,
708f2644a2cSMark Brown 		.rates = WM8960_RATES,
709f2644a2cSMark Brown 		.formats = WM8960_FORMATS,},
710f2644a2cSMark Brown 	.ops = &wm8960_dai_ops,
711f2644a2cSMark Brown 	.symmetric_rates = 1,
712f2644a2cSMark Brown };
713f2644a2cSMark Brown EXPORT_SYMBOL_GPL(wm8960_dai);
714f2644a2cSMark Brown 
715f2644a2cSMark Brown static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
716f2644a2cSMark Brown {
717f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
718f2644a2cSMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
719f2644a2cSMark Brown 
720f2644a2cSMark Brown 	wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF);
721f2644a2cSMark Brown 	return 0;
722f2644a2cSMark Brown }
723f2644a2cSMark Brown 
724f2644a2cSMark Brown static int wm8960_resume(struct platform_device *pdev)
725f2644a2cSMark Brown {
726f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
727f2644a2cSMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
728f2644a2cSMark Brown 	int i;
729f2644a2cSMark Brown 	u8 data[2];
730f2644a2cSMark Brown 	u16 *cache = codec->reg_cache;
731f2644a2cSMark Brown 
732f2644a2cSMark Brown 	/* Sync reg_cache with the hardware */
733f2644a2cSMark Brown 	for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
734f2644a2cSMark Brown 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
735f2644a2cSMark Brown 		data[1] = cache[i] & 0x00ff;
736f2644a2cSMark Brown 		codec->hw_write(codec->control_data, data, 2);
737f2644a2cSMark Brown 	}
738f2644a2cSMark Brown 
739f2644a2cSMark Brown 	wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
740f2644a2cSMark Brown 	wm8960_set_bias_level(codec, codec->suspend_bias_level);
741f2644a2cSMark Brown 	return 0;
742f2644a2cSMark Brown }
743f2644a2cSMark Brown 
744f2644a2cSMark Brown static struct snd_soc_codec *wm8960_codec;
745f2644a2cSMark Brown 
746f2644a2cSMark Brown static int wm8960_probe(struct platform_device *pdev)
747f2644a2cSMark Brown {
748f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
749f2644a2cSMark Brown 	struct snd_soc_codec *codec;
750f2644a2cSMark Brown 	int ret = 0;
751f2644a2cSMark Brown 
752f2644a2cSMark Brown 	if (wm8960_codec == NULL) {
753f2644a2cSMark Brown 		dev_err(&pdev->dev, "Codec device not registered\n");
754f2644a2cSMark Brown 		return -ENODEV;
755f2644a2cSMark Brown 	}
756f2644a2cSMark Brown 
757f2644a2cSMark Brown 	socdev->card->codec = wm8960_codec;
758f2644a2cSMark Brown 	codec = wm8960_codec;
759f2644a2cSMark Brown 
760f2644a2cSMark Brown 	/* register pcms */
761f2644a2cSMark Brown 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
762f2644a2cSMark Brown 	if (ret < 0) {
763f2644a2cSMark Brown 		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
764f2644a2cSMark Brown 		goto pcm_err;
765f2644a2cSMark Brown 	}
766f2644a2cSMark Brown 
767f2644a2cSMark Brown 	snd_soc_add_controls(codec, wm8960_snd_controls,
768f2644a2cSMark Brown 			     ARRAY_SIZE(wm8960_snd_controls));
769f2644a2cSMark Brown 	wm8960_add_widgets(codec);
770f2644a2cSMark Brown 	ret = snd_soc_init_card(socdev);
771f2644a2cSMark Brown 	if (ret < 0) {
772f2644a2cSMark Brown 		dev_err(codec->dev, "failed to register card: %d\n", ret);
773f2644a2cSMark Brown 		goto card_err;
774f2644a2cSMark Brown 	}
775f2644a2cSMark Brown 
776f2644a2cSMark Brown 	return ret;
777f2644a2cSMark Brown 
778f2644a2cSMark Brown card_err:
779f2644a2cSMark Brown 	snd_soc_free_pcms(socdev);
780f2644a2cSMark Brown 	snd_soc_dapm_free(socdev);
781f2644a2cSMark Brown pcm_err:
782f2644a2cSMark Brown 	return ret;
783f2644a2cSMark Brown }
784f2644a2cSMark Brown 
785f2644a2cSMark Brown /* power down chip */
786f2644a2cSMark Brown static int wm8960_remove(struct platform_device *pdev)
787f2644a2cSMark Brown {
788f2644a2cSMark Brown 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
789f2644a2cSMark Brown 
790f2644a2cSMark Brown 	snd_soc_free_pcms(socdev);
791f2644a2cSMark Brown 	snd_soc_dapm_free(socdev);
792f2644a2cSMark Brown 
793f2644a2cSMark Brown 	return 0;
794f2644a2cSMark Brown }
795f2644a2cSMark Brown 
796f2644a2cSMark Brown struct snd_soc_codec_device soc_codec_dev_wm8960 = {
797f2644a2cSMark Brown 	.probe = 	wm8960_probe,
798f2644a2cSMark Brown 	.remove = 	wm8960_remove,
799f2644a2cSMark Brown 	.suspend = 	wm8960_suspend,
800f2644a2cSMark Brown 	.resume =	wm8960_resume,
801f2644a2cSMark Brown };
802f2644a2cSMark Brown EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
803f2644a2cSMark Brown 
804f2644a2cSMark Brown static int wm8960_register(struct wm8960_priv *wm8960)
805f2644a2cSMark Brown {
806f2644a2cSMark Brown 	struct wm8960_data *pdata = wm8960->codec.dev->platform_data;
807f2644a2cSMark Brown 	struct snd_soc_codec *codec = &wm8960->codec;
808f2644a2cSMark Brown 	int ret;
809f2644a2cSMark Brown 	u16 reg;
810f2644a2cSMark Brown 
811f2644a2cSMark Brown 	if (wm8960_codec) {
812f2644a2cSMark Brown 		dev_err(codec->dev, "Another WM8960 is registered\n");
813f2644a2cSMark Brown 		return -EINVAL;
814f2644a2cSMark Brown 	}
815f2644a2cSMark Brown 
816f2644a2cSMark Brown 	if (!pdata) {
817f2644a2cSMark Brown 		dev_warn(codec->dev, "No platform data supplied\n");
818f2644a2cSMark Brown 	} else {
819f2644a2cSMark Brown 		if (pdata->dres > WM8960_DRES_MAX) {
820f2644a2cSMark Brown 			dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
821f2644a2cSMark Brown 			pdata->dres = 0;
822f2644a2cSMark Brown 		}
823f2644a2cSMark Brown 	}
824f2644a2cSMark Brown 
825f2644a2cSMark Brown 	mutex_init(&codec->mutex);
826f2644a2cSMark Brown 	INIT_LIST_HEAD(&codec->dapm_widgets);
827f2644a2cSMark Brown 	INIT_LIST_HEAD(&codec->dapm_paths);
828f2644a2cSMark Brown 
829f2644a2cSMark Brown 	codec->private_data = wm8960;
830f2644a2cSMark Brown 	codec->name = "WM8960";
831f2644a2cSMark Brown 	codec->owner = THIS_MODULE;
832f2644a2cSMark Brown 	codec->read = wm8960_read_reg_cache;
833f2644a2cSMark Brown 	codec->write = wm8960_write;
834f2644a2cSMark Brown 	codec->bias_level = SND_SOC_BIAS_OFF;
835f2644a2cSMark Brown 	codec->set_bias_level = wm8960_set_bias_level;
836f2644a2cSMark Brown 	codec->dai = &wm8960_dai;
837f2644a2cSMark Brown 	codec->num_dai = 1;
838f2644a2cSMark Brown 	codec->reg_cache_size = WM8960_CACHEREGNUM;
839f2644a2cSMark Brown 	codec->reg_cache = &wm8960->reg_cache;
840f2644a2cSMark Brown 
841f2644a2cSMark Brown 	memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
842f2644a2cSMark Brown 
843f2644a2cSMark Brown 	ret = wm8960_reset(codec);
844f2644a2cSMark Brown 	if (ret < 0) {
845f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to issue reset\n");
846f2644a2cSMark Brown 		return ret;
847f2644a2cSMark Brown 	}
848f2644a2cSMark Brown 
849f2644a2cSMark Brown 	wm8960_dai.dev = codec->dev;
850f2644a2cSMark Brown 
851f2644a2cSMark Brown 	wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
852f2644a2cSMark Brown 
853f2644a2cSMark Brown 	/* Latch the update bits */
854f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_LINVOL);
855f2644a2cSMark Brown 	wm8960_write(codec, WM8960_LINVOL, reg | 0x100);
856f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_RINVOL);
857f2644a2cSMark Brown 	wm8960_write(codec, WM8960_RINVOL, reg | 0x100);
858f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_LADC);
859f2644a2cSMark Brown 	wm8960_write(codec, WM8960_LADC, reg | 0x100);
860f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_RADC);
861f2644a2cSMark Brown 	wm8960_write(codec, WM8960_RADC, reg | 0x100);
862f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_LDAC);
863f2644a2cSMark Brown 	wm8960_write(codec, WM8960_LDAC, reg | 0x100);
864f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_RDAC);
865f2644a2cSMark Brown 	wm8960_write(codec, WM8960_RDAC, reg | 0x100);
866f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_LOUT1);
867f2644a2cSMark Brown 	wm8960_write(codec, WM8960_LOUT1, reg | 0x100);
868f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_ROUT1);
869f2644a2cSMark Brown 	wm8960_write(codec, WM8960_ROUT1, reg | 0x100);
870f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_LOUT2);
871f2644a2cSMark Brown 	wm8960_write(codec, WM8960_LOUT2, reg | 0x100);
872f2644a2cSMark Brown 	reg = wm8960_read(codec, WM8960_ROUT2);
873f2644a2cSMark Brown 	wm8960_write(codec, WM8960_ROUT2, reg | 0x100);
874f2644a2cSMark Brown 
875f2644a2cSMark Brown 	wm8960_codec = codec;
876f2644a2cSMark Brown 
877f2644a2cSMark Brown 	ret = snd_soc_register_codec(codec);
878f2644a2cSMark Brown 	if (ret != 0) {
879f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
880f2644a2cSMark Brown 		return ret;
881f2644a2cSMark Brown 	}
882f2644a2cSMark Brown 
883f2644a2cSMark Brown 	ret = snd_soc_register_dai(&wm8960_dai);
884f2644a2cSMark Brown 	if (ret != 0) {
885f2644a2cSMark Brown 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
886f2644a2cSMark Brown 		snd_soc_unregister_codec(codec);
887f2644a2cSMark Brown 		return ret;
888f2644a2cSMark Brown 	}
889f2644a2cSMark Brown 
890f2644a2cSMark Brown 	return 0;
891f2644a2cSMark Brown }
892f2644a2cSMark Brown 
893f2644a2cSMark Brown static void wm8960_unregister(struct wm8960_priv *wm8960)
894f2644a2cSMark Brown {
895f2644a2cSMark Brown 	wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
896f2644a2cSMark Brown 	snd_soc_unregister_dai(&wm8960_dai);
897f2644a2cSMark Brown 	snd_soc_unregister_codec(&wm8960->codec);
898f2644a2cSMark Brown 	kfree(wm8960);
899f2644a2cSMark Brown 	wm8960_codec = NULL;
900f2644a2cSMark Brown }
901f2644a2cSMark Brown 
902f2644a2cSMark Brown static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
903f2644a2cSMark Brown 				      const struct i2c_device_id *id)
904f2644a2cSMark Brown {
905f2644a2cSMark Brown 	struct wm8960_priv *wm8960;
906f2644a2cSMark Brown 	struct snd_soc_codec *codec;
907f2644a2cSMark Brown 
908f2644a2cSMark Brown 	wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);
909f2644a2cSMark Brown 	if (wm8960 == NULL)
910f2644a2cSMark Brown 		return -ENOMEM;
911f2644a2cSMark Brown 
912f2644a2cSMark Brown 	codec = &wm8960->codec;
913f2644a2cSMark Brown 	codec->hw_write = (hw_write_t)i2c_master_send;
914f2644a2cSMark Brown 
915f2644a2cSMark Brown 	i2c_set_clientdata(i2c, wm8960);
916f2644a2cSMark Brown 	codec->control_data = i2c;
917f2644a2cSMark Brown 
918f2644a2cSMark Brown 	codec->dev = &i2c->dev;
919f2644a2cSMark Brown 
920f2644a2cSMark Brown 	return wm8960_register(wm8960);
921f2644a2cSMark Brown }
922f2644a2cSMark Brown 
923f2644a2cSMark Brown static __devexit int wm8960_i2c_remove(struct i2c_client *client)
924f2644a2cSMark Brown {
925f2644a2cSMark Brown 	struct wm8960_priv *wm8960 = i2c_get_clientdata(client);
926f2644a2cSMark Brown 	wm8960_unregister(wm8960);
927f2644a2cSMark Brown 	return 0;
928f2644a2cSMark Brown }
929f2644a2cSMark Brown 
930f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = {
931f2644a2cSMark Brown 	{ "wm8960", 0 },
932f2644a2cSMark Brown 	{ }
933f2644a2cSMark Brown };
934f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
935f2644a2cSMark Brown 
936f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = {
937f2644a2cSMark Brown 	.driver = {
938f2644a2cSMark Brown 		.name = "WM8960 I2C Codec",
939f2644a2cSMark Brown 		.owner = THIS_MODULE,
940f2644a2cSMark Brown 	},
941f2644a2cSMark Brown 	.probe =    wm8960_i2c_probe,
942f2644a2cSMark Brown 	.remove =   __devexit_p(wm8960_i2c_remove),
943f2644a2cSMark Brown 	.id_table = wm8960_i2c_id,
944f2644a2cSMark Brown };
945f2644a2cSMark Brown 
946f2644a2cSMark Brown static int __init wm8960_modinit(void)
947f2644a2cSMark Brown {
948f2644a2cSMark Brown 	int ret;
949f2644a2cSMark Brown 
950f2644a2cSMark Brown 	ret = i2c_add_driver(&wm8960_i2c_driver);
951f2644a2cSMark Brown 	if (ret != 0) {
952f2644a2cSMark Brown 		printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
953f2644a2cSMark Brown 		       ret);
954f2644a2cSMark Brown 	}
955f2644a2cSMark Brown 
956f2644a2cSMark Brown 	return ret;
957f2644a2cSMark Brown }
958f2644a2cSMark Brown module_init(wm8960_modinit);
959f2644a2cSMark Brown 
960f2644a2cSMark Brown static void __exit wm8960_exit(void)
961f2644a2cSMark Brown {
962f2644a2cSMark Brown 	i2c_del_driver(&wm8960_i2c_driver);
963f2644a2cSMark Brown }
964f2644a2cSMark Brown module_exit(wm8960_exit);
965f2644a2cSMark Brown 
966f2644a2cSMark Brown 
967f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver");
968f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood");
969f2644a2cSMark Brown MODULE_LICENSE("GPL");
970