1/*
2 * wm9712.c  --  ALSA Soc WM9712 codec support
3 *
4 * Copyright 2006-12 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood <lrg@slimlogic.co.uk>
6 *
7 *  This program is free software; you can redistribute  it and/or modify it
8 *  under  the terms of  the GNU General  Public License as published by the
9 *  Free Software Foundation;  either version 2 of the  License, or (at your
10 *  option) any later version.
11 */
12
13#include <linux/init.h>
14#include <linux/slab.h>
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/device.h>
18#include <sound/core.h>
19#include <sound/pcm.h>
20#include <sound/ac97_codec.h>
21#include <sound/initval.h>
22#include <sound/soc.h>
23#include <sound/tlv.h>
24#include "wm9712.h"
25
26struct wm9712_priv {
27	struct snd_ac97 *ac97;
28	unsigned int hp_mixer[2];
29	struct mutex lock;
30};
31
32static unsigned int ac97_read(struct snd_soc_codec *codec,
33	unsigned int reg);
34static int ac97_write(struct snd_soc_codec *codec,
35	unsigned int reg, unsigned int val);
36
37/*
38 * WM9712 register cache
39 */
40static const u16 wm9712_reg[] = {
41	0x6174, 0x8000, 0x8000, 0x8000, /*  6 */
42	0x0f0f, 0xaaa0, 0xc008, 0x6808, /*  e */
43	0xe808, 0xaaa0, 0xad00, 0x8000, /* 16 */
44	0xe808, 0x3000, 0x8000, 0x0000, /* 1e */
45	0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
46	0x0405, 0x0410, 0xbb80, 0xbb80, /* 2e */
47	0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
48	0x0000, 0x2000, 0x0000, 0x0000, /* 3e */
49	0x0000, 0x0000, 0x0000, 0x0000, /* 46 */
50	0x0000, 0x0000, 0xf83e, 0xffff, /* 4e */
51	0x0000, 0x0000, 0x0000, 0xf83e, /* 56 */
52	0x0008, 0x0000, 0x0000, 0x0000, /* 5e */
53	0xb032, 0x3e00, 0x0000, 0x0000, /* 66 */
54	0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
55	0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
56	0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
57};
58
59#define HPL_MIXER	0x0
60#define HPR_MIXER	0x1
61
62static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
63static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
64static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right",
65	"Mono"};
66static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"};
67static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
68static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"};
69static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
70static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2",
71	"Stereo"};
72static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer",
73	"Line", "Headphone Mixer", "Phone Mixer", "Phone"};
74static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"};
75static const char *wm9712_diff_sel[] = {"Mic", "Line"};
76
77static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
78static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 2000, 0);
79
80static const struct soc_enum wm9712_enum[] = {
81SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select),
82SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux),
83SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src),
84SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src),
85SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc),
86SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base),
87SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain),
88SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic),
89SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel),
90SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel),
91SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type),
92SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel),
93};
94
95static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
96SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
97SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
98SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
99SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
100SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
101
102SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
103SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
104SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
105SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
106SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
107SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
108
109SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
110SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
111SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
112SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
113SOC_ENUM("ALC Function", wm9712_enum[0]),
114SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
115SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
116SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
117SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
118SOC_ENUM("ALC NG Type", wm9712_enum[10]),
119SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
120
121SOC_SINGLE("Mic Headphone  Volume", AC97_VIDEO, 12, 7, 1),
122SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
123
124SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
125SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1),
126SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
127
128SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
129SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1),
130SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1),
131
132SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
133SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
134SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
135
136SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1),
137SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
138
139SOC_SINGLE_TLV("Capture Boost Switch", AC97_REC_SEL, 14, 1, 0, boost_tlv),
140SOC_SINGLE_TLV("Capture to Phone Boost Switch", AC97_REC_SEL, 11, 1, 1,
141	       boost_tlv),
142
143SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
144SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
145SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0),
146
147SOC_ENUM("Bass Control", wm9712_enum[5]),
148SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
149SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
150SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
151SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1),
152SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
153
154SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
155SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
156SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0),
157SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
158
159SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
160SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
161SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
162};
163
164static const unsigned int wm9712_mixer_mute_regs[] = {
165	AC97_VIDEO,
166	AC97_PCM,
167	AC97_LINE,
168	AC97_PHONE,
169	AC97_CD,
170	AC97_PC_BEEP,
171};
172
173/* We have to create a fake left and right HP mixers because
174 * the codec only has a single control that is shared by both channels.
175 * This makes it impossible to determine the audio path.
176 */
177static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
178	struct snd_ctl_elem_value *ucontrol)
179{
180	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
181	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
182	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
183	unsigned int val = ucontrol->value.integer.value[0];
184	struct soc_mixer_control *mc =
185		(struct soc_mixer_control *)kcontrol->private_value;
186	unsigned int mixer, mask, shift, old;
187	struct snd_soc_dapm_update update;
188	bool change;
189
190	mixer = mc->shift >> 8;
191	shift = mc->shift & 0xff;
192	mask = 1 << shift;
193
194	mutex_lock(&wm9712->lock);
195	old = wm9712->hp_mixer[mixer];
196	if (ucontrol->value.integer.value[0])
197		wm9712->hp_mixer[mixer] |= mask;
198	else
199		wm9712->hp_mixer[mixer] &= ~mask;
200
201	change = old != wm9712->hp_mixer[mixer];
202	if (change) {
203		update.kcontrol = kcontrol;
204		update.reg = wm9712_mixer_mute_regs[shift];
205		update.mask = 0x8000;
206		if ((wm9712->hp_mixer[0] & mask) ||
207		    (wm9712->hp_mixer[1] & mask))
208			update.val = 0x0;
209		else
210			update.val = 0x8000;
211
212		snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
213			&update);
214	}
215
216	mutex_unlock(&wm9712->lock);
217
218	return change;
219}
220
221static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
222	struct snd_ctl_elem_value *ucontrol)
223{
224	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
225	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
226	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
227	struct soc_mixer_control *mc =
228		(struct soc_mixer_control *)kcontrol->private_value;
229	unsigned int shift, mixer;
230
231	mixer = mc->shift >> 8;
232	shift = mc->shift & 0xff;
233
234	ucontrol->value.integer.value[0] =
235		(wm9712->hp_mixer[mixer] >> shift) & 1;
236
237	return 0;
238}
239
240#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
241	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
242	.info = snd_soc_info_volsw, \
243	.get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
244	.private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
245		(xmixer << 8) | xshift, 1, 0, 0) \
246}
247
248/* Left Headphone Mixers */
249static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
250	WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
251	WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
252	WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
253	WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
254	WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
255	WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
256};
257
258/* Right Headphone Mixers */
259static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
260	WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
261	WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
262	WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
263	WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
264	WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
265	WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
266};
267
268/* Speaker Mixer */
269static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = {
270	SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1),
271	SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1),
272	SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1),
273	SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1),
274	SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1),
275};
276
277/* Phone Mixer */
278static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = {
279	SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1),
280	SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1),
281	SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1),
282	SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1),
283	SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1),
284	SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1),
285};
286
287/* ALC headphone mux */
288static const struct snd_kcontrol_new wm9712_alc_mux_controls =
289SOC_DAPM_ENUM("Route", wm9712_enum[1]);
290
291/* out 3 mux */
292static const struct snd_kcontrol_new wm9712_out3_mux_controls =
293SOC_DAPM_ENUM("Route", wm9712_enum[2]);
294
295/* spk mux */
296static const struct snd_kcontrol_new wm9712_spk_mux_controls =
297SOC_DAPM_ENUM("Route", wm9712_enum[3]);
298
299/* Capture to Phone mux */
300static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls =
301SOC_DAPM_ENUM("Route", wm9712_enum[4]);
302
303/* Capture left select */
304static const struct snd_kcontrol_new wm9712_capture_selectl_controls =
305SOC_DAPM_ENUM("Route", wm9712_enum[8]);
306
307/* Capture right select */
308static const struct snd_kcontrol_new wm9712_capture_selectr_controls =
309SOC_DAPM_ENUM("Route", wm9712_enum[9]);
310
311/* Mic select */
312static const struct snd_kcontrol_new wm9712_mic_src_controls =
313SOC_DAPM_ENUM("Mic Source Select", wm9712_enum[7]);
314
315/* diff select */
316static const struct snd_kcontrol_new wm9712_diff_sel_controls =
317SOC_DAPM_ENUM("Route", wm9712_enum[11]);
318
319static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = {
320SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0,
321	&wm9712_alc_mux_controls),
322SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0,
323	&wm9712_out3_mux_controls),
324SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
325	&wm9712_spk_mux_controls),
326SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0,
327	&wm9712_capture_phone_mux_controls),
328SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
329	&wm9712_capture_selectl_controls),
330SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
331	&wm9712_capture_selectr_controls),
332SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0,
333	&wm9712_mic_src_controls),
334SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
335	&wm9712_mic_src_controls),
336SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
337	&wm9712_diff_sel_controls),
338SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
339SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
340	&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
341SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
342	&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
343SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
344	&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
345SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
346	&wm9712_speaker_mixer_controls[0],
347	ARRAY_SIZE(wm9712_speaker_mixer_controls)),
348SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
349SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1),
350SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1),
351SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0),
352SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1),
353SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1),
354SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0),
355SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0),
356SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
357SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
358SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
359SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
360SND_SOC_DAPM_PGA("Differential Mic", SND_SOC_NOPM, 0, 0, NULL, 0),
361SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
362SND_SOC_DAPM_OUTPUT("MONOOUT"),
363SND_SOC_DAPM_OUTPUT("HPOUTL"),
364SND_SOC_DAPM_OUTPUT("HPOUTR"),
365SND_SOC_DAPM_OUTPUT("LOUT2"),
366SND_SOC_DAPM_OUTPUT("ROUT2"),
367SND_SOC_DAPM_OUTPUT("OUT3"),
368SND_SOC_DAPM_INPUT("LINEINL"),
369SND_SOC_DAPM_INPUT("LINEINR"),
370SND_SOC_DAPM_INPUT("PHONE"),
371SND_SOC_DAPM_INPUT("PCBEEP"),
372SND_SOC_DAPM_INPUT("MIC1"),
373SND_SOC_DAPM_INPUT("MIC2"),
374};
375
376static const struct snd_soc_dapm_route wm9712_audio_map[] = {
377	/* virtual mixer - mixes left & right channels for spk and mono */
378	{"AC97 Mixer", NULL, "Left DAC"},
379	{"AC97 Mixer", NULL, "Right DAC"},
380
381	/* Left HP mixer */
382	{"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
383	{"Left HP Mixer", "Aux Playback Switch",  "Aux DAC"},
384	{"Left HP Mixer", "Phone Bypass Switch",  "Phone PGA"},
385	{"Left HP Mixer", "Line Bypass Switch",   "Line PGA"},
386	{"Left HP Mixer", "PCM Playback Switch",  "Left DAC"},
387	{"Left HP Mixer", "Mic Sidetone Switch",  "Mic PGA"},
388	{"Left HP Mixer", NULL,  "ALC Sidetone Mux"},
389
390	/* Right HP mixer */
391	{"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
392	{"Right HP Mixer", "Aux Playback Switch",  "Aux DAC"},
393	{"Right HP Mixer", "Phone Bypass Switch",  "Phone PGA"},
394	{"Right HP Mixer", "Line Bypass Switch",   "Line PGA"},
395	{"Right HP Mixer", "PCM Playback Switch",  "Right DAC"},
396	{"Right HP Mixer", "Mic Sidetone Switch",  "Mic PGA"},
397	{"Right HP Mixer", NULL,  "ALC Sidetone Mux"},
398
399	/* speaker mixer */
400	{"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"},
401	{"Speaker Mixer", "Line Bypass Switch",   "Line PGA"},
402	{"Speaker Mixer", "PCM Playback Switch",  "AC97 Mixer"},
403	{"Speaker Mixer", "Phone Bypass Switch",  "Phone PGA"},
404	{"Speaker Mixer", "Aux Playback Switch",  "Aux DAC"},
405
406	/* Phone mixer */
407	{"Phone Mixer", "PCBeep Bypass Switch",  "PCBEEP"},
408	{"Phone Mixer", "Line Bypass Switch",    "Line PGA"},
409	{"Phone Mixer", "Aux Playback Switch",   "Aux DAC"},
410	{"Phone Mixer", "PCM Playback Switch",   "AC97 Mixer"},
411	{"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"},
412	{"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"},
413
414	/* inputs */
415	{"Line PGA", NULL, "LINEINL"},
416	{"Line PGA", NULL, "LINEINR"},
417	{"Phone PGA", NULL, "PHONE"},
418	{"Mic PGA", NULL, "MIC1"},
419	{"Mic PGA", NULL, "MIC2"},
420
421	/* microphones */
422	{"Differential Mic", NULL, "MIC1"},
423	{"Differential Mic", NULL, "MIC2"},
424	{"Left Mic Select Source", "Mic 1", "MIC1"},
425	{"Left Mic Select Source", "Mic 2", "MIC2"},
426	{"Left Mic Select Source", "Stereo", "MIC1"},
427	{"Left Mic Select Source", "Differential", "Differential Mic"},
428	{"Right Mic Select Source", "Mic 1", "MIC1"},
429	{"Right Mic Select Source", "Mic 2", "MIC2"},
430	{"Right Mic Select Source", "Stereo", "MIC2"},
431	{"Right Mic Select Source", "Differential", "Differential Mic"},
432
433	/* left capture selector */
434	{"Left Capture Select", "Mic", "MIC1"},
435	{"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
436	{"Left Capture Select", "Line", "LINEINL"},
437	{"Left Capture Select", "Headphone Mixer", "Left HP Mixer"},
438	{"Left Capture Select", "Phone Mixer", "Phone Mixer"},
439	{"Left Capture Select", "Phone", "PHONE"},
440
441	/* right capture selector */
442	{"Right Capture Select", "Mic", "MIC2"},
443	{"Right Capture Select", "Speaker Mixer", "Speaker Mixer"},
444	{"Right Capture Select", "Line", "LINEINR"},
445	{"Right Capture Select", "Headphone Mixer", "Right HP Mixer"},
446	{"Right Capture Select", "Phone Mixer", "Phone Mixer"},
447	{"Right Capture Select", "Phone", "PHONE"},
448
449	/* ALC Sidetone */
450	{"ALC Sidetone Mux", "Stereo", "Left Capture Select"},
451	{"ALC Sidetone Mux", "Stereo", "Right Capture Select"},
452	{"ALC Sidetone Mux", "Left", "Left Capture Select"},
453	{"ALC Sidetone Mux", "Right", "Right Capture Select"},
454
455	/* ADC's */
456	{"Left ADC", NULL, "Left Capture Select"},
457	{"Right ADC", NULL, "Right Capture Select"},
458
459	/* outputs */
460	{"MONOOUT", NULL, "Phone Mixer"},
461	{"HPOUTL", NULL, "Headphone PGA"},
462	{"Headphone PGA", NULL, "Left HP Mixer"},
463	{"HPOUTR", NULL, "Headphone PGA"},
464	{"Headphone PGA", NULL, "Right HP Mixer"},
465
466	/* mono mixer */
467	{"Mono Mixer", NULL, "Left HP Mixer"},
468	{"Mono Mixer", NULL, "Right HP Mixer"},
469
470	/* Out3 Mux */
471	{"Out3 Mux", "Left", "Left HP Mixer"},
472	{"Out3 Mux", "Mono", "Phone Mixer"},
473	{"Out3 Mux", "Left + Right", "Mono Mixer"},
474	{"Out 3 PGA", NULL, "Out3 Mux"},
475	{"OUT3", NULL, "Out 3 PGA"},
476
477	/* speaker Mux */
478	{"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
479	{"Speaker Mux", "Headphone Mix", "Mono Mixer"},
480	{"Speaker PGA", NULL, "Speaker Mux"},
481	{"LOUT2", NULL, "Speaker PGA"},
482	{"ROUT2", NULL, "Speaker PGA"},
483};
484
485static unsigned int ac97_read(struct snd_soc_codec *codec,
486	unsigned int reg)
487{
488	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
489	u16 *cache = codec->reg_cache;
490
491	if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
492		reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
493		reg == AC97_REC_GAIN)
494		return soc_ac97_ops->read(wm9712->ac97, reg);
495	else {
496		reg = reg >> 1;
497
498		if (reg >= (ARRAY_SIZE(wm9712_reg)))
499			return -EIO;
500
501		return cache[reg];
502	}
503}
504
505static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
506	unsigned int val)
507{
508	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
509	u16 *cache = codec->reg_cache;
510
511	soc_ac97_ops->write(wm9712->ac97, reg, val);
512	reg = reg >> 1;
513	if (reg < (ARRAY_SIZE(wm9712_reg)))
514		cache[reg] = val;
515
516	return 0;
517}
518
519static int ac97_prepare(struct snd_pcm_substream *substream,
520			struct snd_soc_dai *dai)
521{
522	struct snd_soc_codec *codec = dai->codec;
523	int reg;
524	u16 vra;
525	struct snd_pcm_runtime *runtime = substream->runtime;
526
527	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
528	ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
529
530	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
531		reg = AC97_PCM_FRONT_DAC_RATE;
532	else
533		reg = AC97_PCM_LR_ADC_RATE;
534
535	return ac97_write(codec, reg, runtime->rate);
536}
537
538static int ac97_aux_prepare(struct snd_pcm_substream *substream,
539			    struct snd_soc_dai *dai)
540{
541	struct snd_soc_codec *codec = dai->codec;
542	u16 vra, xsle;
543	struct snd_pcm_runtime *runtime = substream->runtime;
544
545	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
546	ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
547	xsle = ac97_read(codec, AC97_PCI_SID);
548	ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
549
550	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
551		return -ENODEV;
552
553	return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
554}
555
556#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
557		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
558		SNDRV_PCM_RATE_48000)
559
560static const struct snd_soc_dai_ops wm9712_dai_ops_hifi = {
561	.prepare	= ac97_prepare,
562};
563
564static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
565	.prepare	= ac97_aux_prepare,
566};
567
568static struct snd_soc_dai_driver wm9712_dai[] = {
569{
570	.name = "wm9712-hifi",
571	.playback = {
572		.stream_name = "HiFi Playback",
573		.channels_min = 1,
574		.channels_max = 2,
575		.rates = WM9712_AC97_RATES,
576		.formats = SND_SOC_STD_AC97_FMTS,},
577	.capture = {
578		.stream_name = "HiFi Capture",
579		.channels_min = 1,
580		.channels_max = 2,
581		.rates = WM9712_AC97_RATES,
582		.formats = SND_SOC_STD_AC97_FMTS,},
583	.ops = &wm9712_dai_ops_hifi,
584},
585{
586	.name = "wm9712-aux",
587	.playback = {
588		.stream_name = "Aux Playback",
589		.channels_min = 1,
590		.channels_max = 1,
591		.rates = WM9712_AC97_RATES,
592		.formats = SND_SOC_STD_AC97_FMTS,},
593	.ops = &wm9712_dai_ops_aux,
594}
595};
596
597static int wm9712_set_bias_level(struct snd_soc_codec *codec,
598				 enum snd_soc_bias_level level)
599{
600	switch (level) {
601	case SND_SOC_BIAS_ON:
602	case SND_SOC_BIAS_PREPARE:
603		break;
604	case SND_SOC_BIAS_STANDBY:
605		ac97_write(codec, AC97_POWERDOWN, 0x0000);
606		break;
607	case SND_SOC_BIAS_OFF:
608		/* disable everything including AC link */
609		ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
610		ac97_write(codec, AC97_POWERDOWN, 0xffff);
611		break;
612	}
613	codec->dapm.bias_level = level;
614	return 0;
615}
616
617static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
618{
619	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
620
621	if (try_warm && soc_ac97_ops->warm_reset) {
622		soc_ac97_ops->warm_reset(wm9712->ac97);
623		if (ac97_read(codec, 0) == wm9712_reg[0])
624			return 1;
625	}
626
627	soc_ac97_ops->reset(wm9712->ac97);
628	if (soc_ac97_ops->warm_reset)
629		soc_ac97_ops->warm_reset(wm9712->ac97);
630	if (ac97_read(codec, 0) != wm9712_reg[0])
631		goto err;
632	return 0;
633
634err:
635	dev_err(codec->dev, "Failed to reset: AC97 link error\n");
636	return -EIO;
637}
638
639static int wm9712_soc_resume(struct snd_soc_codec *codec)
640{
641	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
642	int i, ret;
643	u16 *cache = codec->reg_cache;
644
645	ret = wm9712_reset(codec, 1);
646	if (ret < 0)
647		return ret;
648
649	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
650
651	if (ret == 0) {
652		/* Sync reg_cache with the hardware after cold reset */
653		for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i += 2) {
654			if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
655			    (i > 0x58 && i != 0x5c))
656				continue;
657			soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
658		}
659	}
660
661	return ret;
662}
663
664static int wm9712_soc_probe(struct snd_soc_codec *codec)
665{
666	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
667	int ret = 0;
668
669	wm9712->ac97 = snd_soc_alloc_ac97_codec(codec);
670	if (IS_ERR(wm9712->ac97)) {
671		ret = PTR_ERR(wm9712->ac97);
672		dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
673		return ret;
674	}
675
676	ret = wm9712_reset(codec, 0);
677	if (ret < 0)
678		goto err_put_device;
679
680	ret = device_add(&wm9712->ac97->dev);
681	if (ret)
682		goto err_put_device;
683
684	/* set alc mux to none */
685	ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
686
687	return 0;
688
689err_put_device:
690	put_device(&wm9712->ac97->dev);
691	return ret;
692}
693
694static int wm9712_soc_remove(struct snd_soc_codec *codec)
695{
696	struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
697
698	snd_soc_free_ac97_codec(wm9712->ac97);
699	return 0;
700}
701
702static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
703	.probe = 	wm9712_soc_probe,
704	.remove = 	wm9712_soc_remove,
705	.resume =	wm9712_soc_resume,
706	.read = ac97_read,
707	.write = ac97_write,
708	.set_bias_level = wm9712_set_bias_level,
709	.suspend_bias_off = true,
710	.reg_cache_size = ARRAY_SIZE(wm9712_reg),
711	.reg_word_size = sizeof(u16),
712	.reg_cache_step = 2,
713	.reg_cache_default = wm9712_reg,
714
715	.controls = wm9712_snd_ac97_controls,
716	.num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
717	.dapm_widgets = wm9712_dapm_widgets,
718	.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
719	.dapm_routes = wm9712_audio_map,
720	.num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
721};
722
723static int wm9712_probe(struct platform_device *pdev)
724{
725	struct wm9712_priv *wm9712;
726
727	wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
728	if (wm9712 == NULL)
729		return -ENOMEM;
730
731	mutex_init(&wm9712->lock);
732
733	platform_set_drvdata(pdev, wm9712);
734
735	return snd_soc_register_codec(&pdev->dev,
736			&soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
737}
738
739static int wm9712_remove(struct platform_device *pdev)
740{
741	snd_soc_unregister_codec(&pdev->dev);
742	return 0;
743}
744
745static struct platform_driver wm9712_codec_driver = {
746	.driver = {
747		.name = "wm9712-codec",
748	},
749
750	.probe = wm9712_probe,
751	.remove = wm9712_remove,
752};
753
754module_platform_driver(wm9712_codec_driver);
755
756MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
757MODULE_AUTHOR("Liam Girdwood");
758MODULE_LICENSE("GPL");
759