xref: /openbmc/linux/sound/soc/codecs/wm9712.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
210c5cf30SRichard Purdie /*
310c5cf30SRichard Purdie  * wm9712.c  --  ALSA Soc WM9712 codec support
410c5cf30SRichard Purdie  *
5656baaebSMark Brown  * Copyright 2006-12 Wolfson Microelectronics PLC.
6d331124dSLiam Girdwood  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
710c5cf30SRichard Purdie  */
810c5cf30SRichard Purdie 
910c5cf30SRichard Purdie #include <linux/init.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
112ed1a8e0SRobert Jarzmik #include <linux/mfd/wm97xx.h>
1210c5cf30SRichard Purdie #include <linux/module.h>
1310c5cf30SRichard Purdie #include <linux/kernel.h>
1410c5cf30SRichard Purdie #include <linux/device.h>
15b4f4f2ebSLars-Peter Clausen #include <linux/regmap.h>
1610c5cf30SRichard Purdie #include <sound/core.h>
1710c5cf30SRichard Purdie #include <sound/pcm.h>
1810c5cf30SRichard Purdie #include <sound/ac97_codec.h>
192ed1a8e0SRobert Jarzmik #include <sound/ac97/codec.h>
202ed1a8e0SRobert Jarzmik #include <sound/ac97/compat.h>
2110c5cf30SRichard Purdie #include <sound/initval.h>
2210c5cf30SRichard Purdie #include <sound/soc.h>
23471280b7SMark Brown #include <sound/tlv.h>
2410c5cf30SRichard Purdie 
25a575be4cSLars-Peter Clausen #define WM9712_VENDOR_ID 0x574d4c12
26a575be4cSLars-Peter Clausen #define WM9712_VENDOR_ID_MASK 0xffffffff
27a575be4cSLars-Peter Clausen 
28cf1f2ebeSLars-Peter Clausen struct wm9712_priv {
29358a8bb5SLars-Peter Clausen 	struct snd_ac97 *ac97;
30cf1f2ebeSLars-Peter Clausen 	unsigned int hp_mixer[2];
31cf1f2ebeSLars-Peter Clausen 	struct mutex lock;
322ed1a8e0SRobert Jarzmik 	struct wm97xx_platform_data *mfd_pdata;
33cf1f2ebeSLars-Peter Clausen };
34cf1f2ebeSLars-Peter Clausen 
35b4f4f2ebSLars-Peter Clausen static const struct reg_default wm9712_reg_defaults[] = {
36b4f4f2ebSLars-Peter Clausen 	{ 0x02, 0x8000 },
37b4f4f2ebSLars-Peter Clausen 	{ 0x04, 0x8000 },
38b4f4f2ebSLars-Peter Clausen 	{ 0x06, 0x8000 },
39b4f4f2ebSLars-Peter Clausen 	{ 0x08, 0x0f0f },
40b4f4f2ebSLars-Peter Clausen 	{ 0x0a, 0xaaa0 },
41b4f4f2ebSLars-Peter Clausen 	{ 0x0c, 0xc008 },
42b4f4f2ebSLars-Peter Clausen 	{ 0x0e, 0x6808 },
43b4f4f2ebSLars-Peter Clausen 	{ 0x10, 0xe808 },
44b4f4f2ebSLars-Peter Clausen 	{ 0x12, 0xaaa0 },
45b4f4f2ebSLars-Peter Clausen 	{ 0x14, 0xad00 },
46b4f4f2ebSLars-Peter Clausen 	{ 0x16, 0x8000 },
47b4f4f2ebSLars-Peter Clausen 	{ 0x18, 0xe808 },
48b4f4f2ebSLars-Peter Clausen 	{ 0x1a, 0x3000 },
49b4f4f2ebSLars-Peter Clausen 	{ 0x1c, 0x8000 },
50b4f4f2ebSLars-Peter Clausen 	{ 0x20, 0x0000 },
51b4f4f2ebSLars-Peter Clausen 	{ 0x22, 0x0000 },
52b4f4f2ebSLars-Peter Clausen 	{ 0x26, 0x000f },
53b4f4f2ebSLars-Peter Clausen 	{ 0x28, 0x0605 },
54b4f4f2ebSLars-Peter Clausen 	{ 0x2a, 0x0410 },
55b4f4f2ebSLars-Peter Clausen 	{ 0x2c, 0xbb80 },
56b4f4f2ebSLars-Peter Clausen 	{ 0x2e, 0xbb80 },
57b4f4f2ebSLars-Peter Clausen 	{ 0x32, 0xbb80 },
58b4f4f2ebSLars-Peter Clausen 	{ 0x34, 0x2000 },
59b4f4f2ebSLars-Peter Clausen 	{ 0x4c, 0xf83e },
60b4f4f2ebSLars-Peter Clausen 	{ 0x4e, 0xffff },
61b4f4f2ebSLars-Peter Clausen 	{ 0x50, 0x0000 },
62b4f4f2ebSLars-Peter Clausen 	{ 0x52, 0x0000 },
63b4f4f2ebSLars-Peter Clausen 	{ 0x56, 0xf83e },
64b4f4f2ebSLars-Peter Clausen 	{ 0x58, 0x0008 },
65b4f4f2ebSLars-Peter Clausen 	{ 0x5c, 0x0000 },
66b4f4f2ebSLars-Peter Clausen 	{ 0x60, 0xb032 },
67b4f4f2ebSLars-Peter Clausen 	{ 0x62, 0x3e00 },
68b4f4f2ebSLars-Peter Clausen 	{ 0x64, 0x0000 },
69b4f4f2ebSLars-Peter Clausen 	{ 0x76, 0x0006 },
70b4f4f2ebSLars-Peter Clausen 	{ 0x78, 0x0001 },
71b4f4f2ebSLars-Peter Clausen 	{ 0x7a, 0x0000 },
72b4f4f2ebSLars-Peter Clausen };
7310c5cf30SRichard Purdie 
wm9712_volatile_reg(struct device * dev,unsigned int reg)74b4f4f2ebSLars-Peter Clausen static bool wm9712_volatile_reg(struct device *dev, unsigned int reg)
75b4f4f2ebSLars-Peter Clausen {
76b4f4f2ebSLars-Peter Clausen 	switch (reg) {
77b4f4f2ebSLars-Peter Clausen 	case AC97_REC_GAIN:
78b4f4f2ebSLars-Peter Clausen 		return true;
79b4f4f2ebSLars-Peter Clausen 	default:
80b4f4f2ebSLars-Peter Clausen 		return regmap_ac97_default_volatile(dev, reg);
81b4f4f2ebSLars-Peter Clausen 	}
82b4f4f2ebSLars-Peter Clausen }
83b4f4f2ebSLars-Peter Clausen 
84b4f4f2ebSLars-Peter Clausen static const struct regmap_config wm9712_regmap_config = {
85b4f4f2ebSLars-Peter Clausen 	.reg_bits = 16,
86b4f4f2ebSLars-Peter Clausen 	.reg_stride = 2,
87b4f4f2ebSLars-Peter Clausen 	.val_bits = 16,
88b4f4f2ebSLars-Peter Clausen 	.max_register = 0x7e,
89*2e3a4ee0SMark Brown 	.cache_type = REGCACHE_MAPLE,
90b4f4f2ebSLars-Peter Clausen 
91b4f4f2ebSLars-Peter Clausen 	.volatile_reg = wm9712_volatile_reg,
92b4f4f2ebSLars-Peter Clausen 
93b4f4f2ebSLars-Peter Clausen 	.reg_defaults = wm9712_reg_defaults,
94b4f4f2ebSLars-Peter Clausen 	.num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults),
9510c5cf30SRichard Purdie };
9610c5cf30SRichard Purdie 
97cf1f2ebeSLars-Peter Clausen #define HPL_MIXER	0x0
98cf1f2ebeSLars-Peter Clausen #define HPR_MIXER	0x1
9910c5cf30SRichard Purdie 
10010c5cf30SRichard Purdie static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
10110c5cf30SRichard Purdie static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
10210c5cf30SRichard Purdie static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right",
10310c5cf30SRichard Purdie 	"Mono"};
10410c5cf30SRichard Purdie static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"};
10510c5cf30SRichard Purdie static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
10610c5cf30SRichard Purdie static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"};
10710c5cf30SRichard Purdie static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
10810c5cf30SRichard Purdie static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2",
10910c5cf30SRichard Purdie 	"Stereo"};
11010c5cf30SRichard Purdie static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer",
11110c5cf30SRichard Purdie 	"Line", "Headphone Mixer", "Phone Mixer", "Phone"};
11210c5cf30SRichard Purdie static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"};
11310c5cf30SRichard Purdie static const char *wm9712_diff_sel[] = {"Mic", "Line"};
11410c5cf30SRichard Purdie 
115471280b7SMark Brown static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
116471280b7SMark Brown static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 2000, 0);
117471280b7SMark Brown 
11810c5cf30SRichard Purdie static const struct soc_enum wm9712_enum[] = {
11910c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select),
12010c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux),
12110c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src),
12210c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src),
12310c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc),
12410c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base),
12510c5cf30SRichard Purdie SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain),
12610c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic),
12710c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel),
12810c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel),
12910c5cf30SRichard Purdie SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type),
13010c5cf30SRichard Purdie SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel),
13110c5cf30SRichard Purdie };
13210c5cf30SRichard Purdie 
13310c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
13410c5cf30SRichard Purdie SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
13510c5cf30SRichard Purdie SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
13610c5cf30SRichard Purdie SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
13710c5cf30SRichard Purdie SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
13853ae5194SLiam Girdwood SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
13910c5cf30SRichard Purdie 
14010c5cf30SRichard Purdie SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
14110c5cf30SRichard Purdie SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
14210c5cf30SRichard Purdie SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
14310c5cf30SRichard Purdie SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
14418fe4ac2SMike Rapoport SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
14518fe4ac2SMike Rapoport SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
14610c5cf30SRichard Purdie 
14710c5cf30SRichard Purdie SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
14810c5cf30SRichard Purdie SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
14910c5cf30SRichard Purdie SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
15010c5cf30SRichard Purdie SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
15110c5cf30SRichard Purdie SOC_ENUM("ALC Function", wm9712_enum[0]),
15210c5cf30SRichard Purdie SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
15310c5cf30SRichard Purdie SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
15410c5cf30SRichard Purdie SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
15510c5cf30SRichard Purdie SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
15610c5cf30SRichard Purdie SOC_ENUM("ALC NG Type", wm9712_enum[10]),
15710c5cf30SRichard Purdie SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
15810c5cf30SRichard Purdie 
15910c5cf30SRichard Purdie SOC_SINGLE("Mic Headphone  Volume", AC97_VIDEO, 12, 7, 1),
16010c5cf30SRichard Purdie SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
16110c5cf30SRichard Purdie 
16210c5cf30SRichard Purdie SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
16310c5cf30SRichard Purdie SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1),
16410c5cf30SRichard Purdie SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
16510c5cf30SRichard Purdie 
16610c5cf30SRichard Purdie SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
16710c5cf30SRichard Purdie SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1),
16810c5cf30SRichard Purdie SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1),
16910c5cf30SRichard Purdie 
17010c5cf30SRichard Purdie SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
17110c5cf30SRichard Purdie SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
17210c5cf30SRichard Purdie SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
17310c5cf30SRichard Purdie 
1747570f29aSJoe Sauer SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1),
17510c5cf30SRichard Purdie SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
17610c5cf30SRichard Purdie 
1773eadd88aSMark Brown SOC_SINGLE_TLV("Capture Boost Switch", AC97_REC_SEL, 14, 1, 0, boost_tlv),
1783eadd88aSMark Brown SOC_SINGLE_TLV("Capture to Phone Boost Switch", AC97_REC_SEL, 11, 1, 1,
1793eadd88aSMark Brown 	       boost_tlv),
18010c5cf30SRichard Purdie 
18110c5cf30SRichard Purdie SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
18210c5cf30SRichard Purdie SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
18310c5cf30SRichard Purdie SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0),
18410c5cf30SRichard Purdie 
18510c5cf30SRichard Purdie SOC_ENUM("Bass Control", wm9712_enum[5]),
18610c5cf30SRichard Purdie SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
18710c5cf30SRichard Purdie SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
18810c5cf30SRichard Purdie SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
18918fe4ac2SMike Rapoport SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1),
19018fe4ac2SMike Rapoport SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
19110c5cf30SRichard Purdie 
192689185b7SMark Brown SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
19310c5cf30SRichard Purdie SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
19428c42c28SMark Brown SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0),
19510c5cf30SRichard Purdie SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
19610c5cf30SRichard Purdie 
197471280b7SMark Brown SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
198471280b7SMark Brown SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
199471280b7SMark Brown SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
20010c5cf30SRichard Purdie };
20110c5cf30SRichard Purdie 
202cf1f2ebeSLars-Peter Clausen static const unsigned int wm9712_mixer_mute_regs[] = {
203cf1f2ebeSLars-Peter Clausen 	AC97_VIDEO,
204cf1f2ebeSLars-Peter Clausen 	AC97_PCM,
205cf1f2ebeSLars-Peter Clausen 	AC97_LINE,
206cf1f2ebeSLars-Peter Clausen 	AC97_PHONE,
207cf1f2ebeSLars-Peter Clausen 	AC97_CD,
208cf1f2ebeSLars-Peter Clausen 	AC97_PC_BEEP,
209cf1f2ebeSLars-Peter Clausen };
210cf1f2ebeSLars-Peter Clausen 
21110c5cf30SRichard Purdie /* We have to create a fake left and right HP mixers because
21210c5cf30SRichard Purdie  * the codec only has a single control that is shared by both channels.
21310c5cf30SRichard Purdie  * This makes it impossible to determine the audio path.
21410c5cf30SRichard Purdie  */
wm9712_hp_mixer_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)215cf1f2ebeSLars-Peter Clausen static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
216cf1f2ebeSLars-Peter Clausen 	struct snd_ctl_elem_value *ucontrol)
21710c5cf30SRichard Purdie {
218cf1f2ebeSLars-Peter Clausen 	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
219143b4484SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
220143b4484SKuninori Morimoto 	struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
2214b0b669bSTakashi Iwai 	unsigned int val = ucontrol->value.integer.value[0];
222cf1f2ebeSLars-Peter Clausen 	struct soc_mixer_control *mc =
223cf1f2ebeSLars-Peter Clausen 		(struct soc_mixer_control *)kcontrol->private_value;
224cf1f2ebeSLars-Peter Clausen 	unsigned int mixer, mask, shift, old;
22560edb200SFabio Estevam 	struct snd_soc_dapm_update update = {};
226cf1f2ebeSLars-Peter Clausen 	bool change;
22710c5cf30SRichard Purdie 
228cf1f2ebeSLars-Peter Clausen 	mixer = mc->shift >> 8;
229cf1f2ebeSLars-Peter Clausen 	shift = mc->shift & 0xff;
230cf1f2ebeSLars-Peter Clausen 	mask = 1 << shift;
23110c5cf30SRichard Purdie 
232cf1f2ebeSLars-Peter Clausen 	mutex_lock(&wm9712->lock);
233cf1f2ebeSLars-Peter Clausen 	old = wm9712->hp_mixer[mixer];
2344b0b669bSTakashi Iwai 	if (ucontrol->value.integer.value[0])
235cf1f2ebeSLars-Peter Clausen 		wm9712->hp_mixer[mixer] |= mask;
23610c5cf30SRichard Purdie 	else
237cf1f2ebeSLars-Peter Clausen 		wm9712->hp_mixer[mixer] &= ~mask;
23810c5cf30SRichard Purdie 
239cf1f2ebeSLars-Peter Clausen 	change = old != wm9712->hp_mixer[mixer];
240cf1f2ebeSLars-Peter Clausen 	if (change) {
241cf1f2ebeSLars-Peter Clausen 		update.kcontrol = kcontrol;
242cf1f2ebeSLars-Peter Clausen 		update.reg = wm9712_mixer_mute_regs[shift];
243cf1f2ebeSLars-Peter Clausen 		update.mask = 0x8000;
244cf1f2ebeSLars-Peter Clausen 		if ((wm9712->hp_mixer[0] & mask) ||
245cf1f2ebeSLars-Peter Clausen 		    (wm9712->hp_mixer[1] & mask))
246cf1f2ebeSLars-Peter Clausen 			update.val = 0x0;
24710c5cf30SRichard Purdie 		else
248cf1f2ebeSLars-Peter Clausen 			update.val = 0x8000;
24910c5cf30SRichard Purdie 
250cf1f2ebeSLars-Peter Clausen 		snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
251cf1f2ebeSLars-Peter Clausen 			&update);
252cf1f2ebeSLars-Peter Clausen 	}
25310c5cf30SRichard Purdie 
254cf1f2ebeSLars-Peter Clausen 	mutex_unlock(&wm9712->lock);
25510c5cf30SRichard Purdie 
256cf1f2ebeSLars-Peter Clausen 	return change;
257cf1f2ebeSLars-Peter Clausen }
25810c5cf30SRichard Purdie 
wm9712_hp_mixer_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)259cf1f2ebeSLars-Peter Clausen static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
260cf1f2ebeSLars-Peter Clausen 	struct snd_ctl_elem_value *ucontrol)
261cf1f2ebeSLars-Peter Clausen {
262cf1f2ebeSLars-Peter Clausen 	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
263143b4484SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
264143b4484SKuninori Morimoto 	struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
265cf1f2ebeSLars-Peter Clausen 	struct soc_mixer_control *mc =
266cf1f2ebeSLars-Peter Clausen 		(struct soc_mixer_control *)kcontrol->private_value;
267cf1f2ebeSLars-Peter Clausen 	unsigned int shift, mixer;
268cf1f2ebeSLars-Peter Clausen 
269cf1f2ebeSLars-Peter Clausen 	mixer = mc->shift >> 8;
270cf1f2ebeSLars-Peter Clausen 	shift = mc->shift & 0xff;
271cf1f2ebeSLars-Peter Clausen 
2724b0b669bSTakashi Iwai 	ucontrol->value.integer.value[0] =
273cf1f2ebeSLars-Peter Clausen 		(wm9712->hp_mixer[mixer] >> shift) & 1;
27410c5cf30SRichard Purdie 
27510c5cf30SRichard Purdie 	return 0;
27610c5cf30SRichard Purdie }
27710c5cf30SRichard Purdie 
278cf1f2ebeSLars-Peter Clausen #define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
279cf1f2ebeSLars-Peter Clausen 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
280cf1f2ebeSLars-Peter Clausen 	.info = snd_soc_info_volsw, \
281cf1f2ebeSLars-Peter Clausen 	.get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
282cf1f2ebeSLars-Peter Clausen 	.private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
283cf1f2ebeSLars-Peter Clausen 		(xmixer << 8) | xshift, 1, 0, 0) \
284cf1f2ebeSLars-Peter Clausen }
285cf1f2ebeSLars-Peter Clausen 
28610c5cf30SRichard Purdie /* Left Headphone Mixers */
28710c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
288cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
289cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
290cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
291cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
292cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
293cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
29410c5cf30SRichard Purdie };
29510c5cf30SRichard Purdie 
29610c5cf30SRichard Purdie /* Right Headphone Mixers */
29710c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
298cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
299cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
300cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
301cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
302cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
303cf1f2ebeSLars-Peter Clausen 	WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
30410c5cf30SRichard Purdie };
30510c5cf30SRichard Purdie 
30610c5cf30SRichard Purdie /* Speaker Mixer */
30710c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = {
30810c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1),
30910c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1),
31010c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1),
31110c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1),
31210c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1),
31310c5cf30SRichard Purdie };
31410c5cf30SRichard Purdie 
31510c5cf30SRichard Purdie /* Phone Mixer */
31610c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = {
31710c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1),
31810c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1),
31910c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1),
32010c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1),
32110c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1),
32210c5cf30SRichard Purdie 	SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1),
32310c5cf30SRichard Purdie };
32410c5cf30SRichard Purdie 
32510c5cf30SRichard Purdie /* ALC headphone mux */
32610c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_alc_mux_controls =
32710c5cf30SRichard Purdie SOC_DAPM_ENUM("Route", wm9712_enum[1]);
32810c5cf30SRichard Purdie 
32910c5cf30SRichard Purdie /* out 3 mux */
33010c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_out3_mux_controls =
33110c5cf30SRichard Purdie SOC_DAPM_ENUM("Route", wm9712_enum[2]);
33210c5cf30SRichard Purdie 
33310c5cf30SRichard Purdie /* spk mux */
33410c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_spk_mux_controls =
33510c5cf30SRichard Purdie SOC_DAPM_ENUM("Route", wm9712_enum[3]);
33610c5cf30SRichard Purdie 
33710c5cf30SRichard Purdie /* Capture to Phone mux */
33810c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls =
33910c5cf30SRichard Purdie SOC_DAPM_ENUM("Route", wm9712_enum[4]);
34010c5cf30SRichard Purdie 
34110c5cf30SRichard Purdie /* Capture left select */
34210c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_capture_selectl_controls =
34310c5cf30SRichard Purdie SOC_DAPM_ENUM("Route", wm9712_enum[8]);
34410c5cf30SRichard Purdie 
34510c5cf30SRichard Purdie /* Capture right select */
34610c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_capture_selectr_controls =
34710c5cf30SRichard Purdie SOC_DAPM_ENUM("Route", wm9712_enum[9]);
34810c5cf30SRichard Purdie 
34910c5cf30SRichard Purdie /* Mic select */
35010c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_mic_src_controls =
351ccf79584SMark Brown SOC_DAPM_ENUM("Mic Source Select", wm9712_enum[7]);
35210c5cf30SRichard Purdie 
35310c5cf30SRichard Purdie /* diff select */
35410c5cf30SRichard Purdie static const struct snd_kcontrol_new wm9712_diff_sel_controls =
35510c5cf30SRichard Purdie SOC_DAPM_ENUM("Route", wm9712_enum[11]);
35610c5cf30SRichard Purdie 
35710c5cf30SRichard Purdie static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = {
35810c5cf30SRichard Purdie SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0,
35910c5cf30SRichard Purdie 	&wm9712_alc_mux_controls),
36010c5cf30SRichard Purdie SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0,
36110c5cf30SRichard Purdie 	&wm9712_out3_mux_controls),
36210c5cf30SRichard Purdie SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
36310c5cf30SRichard Purdie 	&wm9712_spk_mux_controls),
36410c5cf30SRichard Purdie SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0,
36510c5cf30SRichard Purdie 	&wm9712_capture_phone_mux_controls),
36610c5cf30SRichard Purdie SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
36710c5cf30SRichard Purdie 	&wm9712_capture_selectl_controls),
36810c5cf30SRichard Purdie SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
36910c5cf30SRichard Purdie 	&wm9712_capture_selectr_controls),
370ccf79584SMark Brown SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0,
371ccf79584SMark Brown 	&wm9712_mic_src_controls),
372ccf79584SMark Brown SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
37310c5cf30SRichard Purdie 	&wm9712_mic_src_controls),
37410c5cf30SRichard Purdie SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
37510c5cf30SRichard Purdie 	&wm9712_diff_sel_controls),
37610c5cf30SRichard Purdie SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
377cf1f2ebeSLars-Peter Clausen SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
378cf1f2ebeSLars-Peter Clausen 	&wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
379cf1f2ebeSLars-Peter Clausen SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
380cf1f2ebeSLars-Peter Clausen 	&wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
38110c5cf30SRichard Purdie SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
38210c5cf30SRichard Purdie 	&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
38310c5cf30SRichard Purdie SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
38410c5cf30SRichard Purdie 	&wm9712_speaker_mixer_controls[0],
38510c5cf30SRichard Purdie 	ARRAY_SIZE(wm9712_speaker_mixer_controls)),
38610c5cf30SRichard Purdie SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
38710c5cf30SRichard Purdie SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1),
38810c5cf30SRichard Purdie SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1),
38910c5cf30SRichard Purdie SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0),
39010c5cf30SRichard Purdie SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1),
39110c5cf30SRichard Purdie SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1),
39210c5cf30SRichard Purdie SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0),
39310c5cf30SRichard Purdie SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0),
39410c5cf30SRichard Purdie SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
39510c5cf30SRichard Purdie SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
39610c5cf30SRichard Purdie SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
39710c5cf30SRichard Purdie SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
398ccf79584SMark Brown SND_SOC_DAPM_PGA("Differential Mic", SND_SOC_NOPM, 0, 0, NULL, 0),
39910c5cf30SRichard Purdie SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
40010c5cf30SRichard Purdie SND_SOC_DAPM_OUTPUT("MONOOUT"),
40110c5cf30SRichard Purdie SND_SOC_DAPM_OUTPUT("HPOUTL"),
40210c5cf30SRichard Purdie SND_SOC_DAPM_OUTPUT("HPOUTR"),
40310c5cf30SRichard Purdie SND_SOC_DAPM_OUTPUT("LOUT2"),
40410c5cf30SRichard Purdie SND_SOC_DAPM_OUTPUT("ROUT2"),
40510c5cf30SRichard Purdie SND_SOC_DAPM_OUTPUT("OUT3"),
40610c5cf30SRichard Purdie SND_SOC_DAPM_INPUT("LINEINL"),
40710c5cf30SRichard Purdie SND_SOC_DAPM_INPUT("LINEINR"),
40810c5cf30SRichard Purdie SND_SOC_DAPM_INPUT("PHONE"),
40910c5cf30SRichard Purdie SND_SOC_DAPM_INPUT("PCBEEP"),
41010c5cf30SRichard Purdie SND_SOC_DAPM_INPUT("MIC1"),
41110c5cf30SRichard Purdie SND_SOC_DAPM_INPUT("MIC2"),
41210c5cf30SRichard Purdie };
41310c5cf30SRichard Purdie 
41498334778SLu Guanqun static const struct snd_soc_dapm_route wm9712_audio_map[] = {
41510c5cf30SRichard Purdie 	/* virtual mixer - mixes left & right channels for spk and mono */
41610c5cf30SRichard Purdie 	{"AC97 Mixer", NULL, "Left DAC"},
41710c5cf30SRichard Purdie 	{"AC97 Mixer", NULL, "Right DAC"},
41810c5cf30SRichard Purdie 
41910c5cf30SRichard Purdie 	/* Left HP mixer */
42010c5cf30SRichard Purdie 	{"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
42110c5cf30SRichard Purdie 	{"Left HP Mixer", "Aux Playback Switch",  "Aux DAC"},
42210c5cf30SRichard Purdie 	{"Left HP Mixer", "Phone Bypass Switch",  "Phone PGA"},
42310c5cf30SRichard Purdie 	{"Left HP Mixer", "Line Bypass Switch",   "Line PGA"},
42410c5cf30SRichard Purdie 	{"Left HP Mixer", "PCM Playback Switch",  "Left DAC"},
42510c5cf30SRichard Purdie 	{"Left HP Mixer", "Mic Sidetone Switch",  "Mic PGA"},
42610c5cf30SRichard Purdie 	{"Left HP Mixer", NULL,  "ALC Sidetone Mux"},
42710c5cf30SRichard Purdie 
42810c5cf30SRichard Purdie 	/* Right HP mixer */
42910c5cf30SRichard Purdie 	{"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
43010c5cf30SRichard Purdie 	{"Right HP Mixer", "Aux Playback Switch",  "Aux DAC"},
43110c5cf30SRichard Purdie 	{"Right HP Mixer", "Phone Bypass Switch",  "Phone PGA"},
43210c5cf30SRichard Purdie 	{"Right HP Mixer", "Line Bypass Switch",   "Line PGA"},
43310c5cf30SRichard Purdie 	{"Right HP Mixer", "PCM Playback Switch",  "Right DAC"},
43410c5cf30SRichard Purdie 	{"Right HP Mixer", "Mic Sidetone Switch",  "Mic PGA"},
43510c5cf30SRichard Purdie 	{"Right HP Mixer", NULL,  "ALC Sidetone Mux"},
43610c5cf30SRichard Purdie 
43710c5cf30SRichard Purdie 	/* speaker mixer */
43810c5cf30SRichard Purdie 	{"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"},
43910c5cf30SRichard Purdie 	{"Speaker Mixer", "Line Bypass Switch",   "Line PGA"},
44010c5cf30SRichard Purdie 	{"Speaker Mixer", "PCM Playback Switch",  "AC97 Mixer"},
44110c5cf30SRichard Purdie 	{"Speaker Mixer", "Phone Bypass Switch",  "Phone PGA"},
44210c5cf30SRichard Purdie 	{"Speaker Mixer", "Aux Playback Switch",  "Aux DAC"},
44310c5cf30SRichard Purdie 
44410c5cf30SRichard Purdie 	/* Phone mixer */
44510c5cf30SRichard Purdie 	{"Phone Mixer", "PCBeep Bypass Switch",  "PCBEEP"},
44610c5cf30SRichard Purdie 	{"Phone Mixer", "Line Bypass Switch",    "Line PGA"},
44710c5cf30SRichard Purdie 	{"Phone Mixer", "Aux Playback Switch",   "Aux DAC"},
44810c5cf30SRichard Purdie 	{"Phone Mixer", "PCM Playback Switch",   "AC97 Mixer"},
44910c5cf30SRichard Purdie 	{"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"},
45010c5cf30SRichard Purdie 	{"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"},
45110c5cf30SRichard Purdie 
45210c5cf30SRichard Purdie 	/* inputs */
45310c5cf30SRichard Purdie 	{"Line PGA", NULL, "LINEINL"},
45410c5cf30SRichard Purdie 	{"Line PGA", NULL, "LINEINR"},
45510c5cf30SRichard Purdie 	{"Phone PGA", NULL, "PHONE"},
45610c5cf30SRichard Purdie 	{"Mic PGA", NULL, "MIC1"},
45710c5cf30SRichard Purdie 	{"Mic PGA", NULL, "MIC2"},
45810c5cf30SRichard Purdie 
459ccf79584SMark Brown 	/* microphones */
460ccf79584SMark Brown 	{"Differential Mic", NULL, "MIC1"},
461ccf79584SMark Brown 	{"Differential Mic", NULL, "MIC2"},
462ccf79584SMark Brown 	{"Left Mic Select Source", "Mic 1", "MIC1"},
463ccf79584SMark Brown 	{"Left Mic Select Source", "Mic 2", "MIC2"},
464ccf79584SMark Brown 	{"Left Mic Select Source", "Stereo", "MIC1"},
465ccf79584SMark Brown 	{"Left Mic Select Source", "Differential", "Differential Mic"},
466ccf79584SMark Brown 	{"Right Mic Select Source", "Mic 1", "MIC1"},
467ccf79584SMark Brown 	{"Right Mic Select Source", "Mic 2", "MIC2"},
468ccf79584SMark Brown 	{"Right Mic Select Source", "Stereo", "MIC2"},
469ccf79584SMark Brown 	{"Right Mic Select Source", "Differential", "Differential Mic"},
470ccf79584SMark Brown 
47110c5cf30SRichard Purdie 	/* left capture selector */
47210c5cf30SRichard Purdie 	{"Left Capture Select", "Mic", "MIC1"},
47310c5cf30SRichard Purdie 	{"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
47410c5cf30SRichard Purdie 	{"Left Capture Select", "Line", "LINEINL"},
47510c5cf30SRichard Purdie 	{"Left Capture Select", "Headphone Mixer", "Left HP Mixer"},
47610c5cf30SRichard Purdie 	{"Left Capture Select", "Phone Mixer", "Phone Mixer"},
47710c5cf30SRichard Purdie 	{"Left Capture Select", "Phone", "PHONE"},
47810c5cf30SRichard Purdie 
47910c5cf30SRichard Purdie 	/* right capture selector */
48010c5cf30SRichard Purdie 	{"Right Capture Select", "Mic", "MIC2"},
48110c5cf30SRichard Purdie 	{"Right Capture Select", "Speaker Mixer", "Speaker Mixer"},
48210c5cf30SRichard Purdie 	{"Right Capture Select", "Line", "LINEINR"},
48310c5cf30SRichard Purdie 	{"Right Capture Select", "Headphone Mixer", "Right HP Mixer"},
48410c5cf30SRichard Purdie 	{"Right Capture Select", "Phone Mixer", "Phone Mixer"},
48510c5cf30SRichard Purdie 	{"Right Capture Select", "Phone", "PHONE"},
48610c5cf30SRichard Purdie 
48710c5cf30SRichard Purdie 	/* ALC Sidetone */
48810c5cf30SRichard Purdie 	{"ALC Sidetone Mux", "Stereo", "Left Capture Select"},
48910c5cf30SRichard Purdie 	{"ALC Sidetone Mux", "Stereo", "Right Capture Select"},
49010c5cf30SRichard Purdie 	{"ALC Sidetone Mux", "Left", "Left Capture Select"},
49110c5cf30SRichard Purdie 	{"ALC Sidetone Mux", "Right", "Right Capture Select"},
49210c5cf30SRichard Purdie 
49310c5cf30SRichard Purdie 	/* ADC's */
49410c5cf30SRichard Purdie 	{"Left ADC", NULL, "Left Capture Select"},
49510c5cf30SRichard Purdie 	{"Right ADC", NULL, "Right Capture Select"},
49610c5cf30SRichard Purdie 
49710c5cf30SRichard Purdie 	/* outputs */
49810c5cf30SRichard Purdie 	{"MONOOUT", NULL, "Phone Mixer"},
49910c5cf30SRichard Purdie 	{"HPOUTL", NULL, "Headphone PGA"},
50010c5cf30SRichard Purdie 	{"Headphone PGA", NULL, "Left HP Mixer"},
50110c5cf30SRichard Purdie 	{"HPOUTR", NULL, "Headphone PGA"},
50210c5cf30SRichard Purdie 	{"Headphone PGA", NULL, "Right HP Mixer"},
50310c5cf30SRichard Purdie 
50494324841SMarek Vasut 	/* mono mixer */
50594324841SMarek Vasut 	{"Mono Mixer", NULL, "Left HP Mixer"},
50694324841SMarek Vasut 	{"Mono Mixer", NULL, "Right HP Mixer"},
50710c5cf30SRichard Purdie 
50810c5cf30SRichard Purdie 	/* Out3 Mux */
50910c5cf30SRichard Purdie 	{"Out3 Mux", "Left", "Left HP Mixer"},
51010c5cf30SRichard Purdie 	{"Out3 Mux", "Mono", "Phone Mixer"},
51194324841SMarek Vasut 	{"Out3 Mux", "Left + Right", "Mono Mixer"},
51210c5cf30SRichard Purdie 	{"Out 3 PGA", NULL, "Out3 Mux"},
51310c5cf30SRichard Purdie 	{"OUT3", NULL, "Out 3 PGA"},
51410c5cf30SRichard Purdie 
51510c5cf30SRichard Purdie 	/* speaker Mux */
51610c5cf30SRichard Purdie 	{"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
51794324841SMarek Vasut 	{"Speaker Mux", "Headphone Mix", "Mono Mixer"},
51810c5cf30SRichard Purdie 	{"Speaker PGA", NULL, "Speaker Mux"},
51910c5cf30SRichard Purdie 	{"LOUT2", NULL, "Speaker PGA"},
52010c5cf30SRichard Purdie 	{"ROUT2", NULL, "Speaker PGA"},
52110c5cf30SRichard Purdie };
52210c5cf30SRichard Purdie 
ac97_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)523dee89c4dSMark Brown static int ac97_prepare(struct snd_pcm_substream *substream,
524dee89c4dSMark Brown 			struct snd_soc_dai *dai)
52510c5cf30SRichard Purdie {
526143b4484SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
52710c5cf30SRichard Purdie 	int reg;
528b46ac308SFabio Estevam 	struct snd_pcm_runtime *runtime = substream->runtime;
52910c5cf30SRichard Purdie 
530143b4484SKuninori Morimoto 	snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1);
53110c5cf30SRichard Purdie 
53210c5cf30SRichard Purdie 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
53310c5cf30SRichard Purdie 		reg = AC97_PCM_FRONT_DAC_RATE;
53410c5cf30SRichard Purdie 	else
53510c5cf30SRichard Purdie 		reg = AC97_PCM_LR_ADC_RATE;
53610c5cf30SRichard Purdie 
537143b4484SKuninori Morimoto 	return snd_soc_component_write(component, reg, runtime->rate);
53810c5cf30SRichard Purdie }
53910c5cf30SRichard Purdie 
ac97_aux_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)540dee89c4dSMark Brown static int ac97_aux_prepare(struct snd_pcm_substream *substream,
541dee89c4dSMark Brown 			    struct snd_soc_dai *dai)
54210c5cf30SRichard Purdie {
543143b4484SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
544b46ac308SFabio Estevam 	struct snd_pcm_runtime *runtime = substream->runtime;
54510c5cf30SRichard Purdie 
546143b4484SKuninori Morimoto 	snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1);
547143b4484SKuninori Morimoto 	snd_soc_component_update_bits(component, AC97_PCI_SID, 0x8000, 0x8000);
54810c5cf30SRichard Purdie 
54910c5cf30SRichard Purdie 	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
55010c5cf30SRichard Purdie 		return -ENODEV;
55110c5cf30SRichard Purdie 
552143b4484SKuninori Morimoto 	return snd_soc_component_write(component, AC97_PCM_SURR_DAC_RATE, runtime->rate);
55310c5cf30SRichard Purdie }
55410c5cf30SRichard Purdie 
555cbe83b17SLiam Girdwood #define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
5567e48bf65SMark Brown 		SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
5577e48bf65SMark Brown 		SNDRV_PCM_RATE_48000)
558cbe83b17SLiam Girdwood 
55985e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm9712_dai_ops_hifi = {
5606335d055SEric Miao 	.prepare	= ac97_prepare,
5616335d055SEric Miao };
5626335d055SEric Miao 
56385e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
5646335d055SEric Miao 	.prepare	= ac97_aux_prepare,
5656335d055SEric Miao };
5666335d055SEric Miao 
5670daaf7e8SMark Brown static struct snd_soc_dai_driver wm9712_dai[] = {
56810c5cf30SRichard Purdie {
569f0fba2adSLiam Girdwood 	.name = "wm9712-hifi",
57010c5cf30SRichard Purdie 	.playback = {
57110c5cf30SRichard Purdie 		.stream_name = "HiFi Playback",
57210c5cf30SRichard Purdie 		.channels_min = 1,
573cbe83b17SLiam Girdwood 		.channels_max = 2,
574cbe83b17SLiam Girdwood 		.rates = WM9712_AC97_RATES,
57533f503c9SMark Brown 		.formats = SND_SOC_STD_AC97_FMTS,},
57610c5cf30SRichard Purdie 	.capture = {
57710c5cf30SRichard Purdie 		.stream_name = "HiFi Capture",
57810c5cf30SRichard Purdie 		.channels_min = 1,
579cbe83b17SLiam Girdwood 		.channels_max = 2,
580cbe83b17SLiam Girdwood 		.rates = WM9712_AC97_RATES,
58133f503c9SMark Brown 		.formats = SND_SOC_STD_AC97_FMTS,},
5826335d055SEric Miao 	.ops = &wm9712_dai_ops_hifi,
58310c5cf30SRichard Purdie },
58410c5cf30SRichard Purdie {
585f0fba2adSLiam Girdwood 	.name = "wm9712-aux",
58610c5cf30SRichard Purdie 	.playback = {
58710c5cf30SRichard Purdie 		.stream_name = "Aux Playback",
58810c5cf30SRichard Purdie 		.channels_min = 1,
589cbe83b17SLiam Girdwood 		.channels_max = 1,
590cbe83b17SLiam Girdwood 		.rates = WM9712_AC97_RATES,
59133f503c9SMark Brown 		.formats = SND_SOC_STD_AC97_FMTS,},
5926335d055SEric Miao 	.ops = &wm9712_dai_ops_aux,
593cbe83b17SLiam Girdwood }
59410c5cf30SRichard Purdie };
59510c5cf30SRichard Purdie 
wm9712_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)596143b4484SKuninori Morimoto static int wm9712_set_bias_level(struct snd_soc_component *component,
5970be9898aSMark Brown 				 enum snd_soc_bias_level level)
59810c5cf30SRichard Purdie {
5990be9898aSMark Brown 	switch (level) {
6000be9898aSMark Brown 	case SND_SOC_BIAS_ON:
6010be9898aSMark Brown 	case SND_SOC_BIAS_PREPARE:
60210c5cf30SRichard Purdie 		break;
6030be9898aSMark Brown 	case SND_SOC_BIAS_STANDBY:
604143b4484SKuninori Morimoto 		snd_soc_component_write(component, AC97_POWERDOWN, 0x0000);
60510c5cf30SRichard Purdie 		break;
6060be9898aSMark Brown 	case SND_SOC_BIAS_OFF:
60710c5cf30SRichard Purdie 		/* disable everything including AC link */
608143b4484SKuninori Morimoto 		snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff);
609143b4484SKuninori Morimoto 		snd_soc_component_write(component, AC97_POWERDOWN, 0xffff);
61010c5cf30SRichard Purdie 		break;
61110c5cf30SRichard Purdie 	}
61210c5cf30SRichard Purdie 	return 0;
61310c5cf30SRichard Purdie }
61410c5cf30SRichard Purdie 
wm9712_soc_resume(struct snd_soc_component * component)615143b4484SKuninori Morimoto static int wm9712_soc_resume(struct snd_soc_component *component)
61610c5cf30SRichard Purdie {
617143b4484SKuninori Morimoto 	struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
618b4f4f2ebSLars-Peter Clausen 	int ret;
61910c5cf30SRichard Purdie 
620a575be4cSLars-Peter Clausen 	ret = snd_ac97_reset(wm9712->ac97, true, WM9712_VENDOR_ID,
621a575be4cSLars-Peter Clausen 		WM9712_VENDOR_ID_MASK);
62212ced338SLars-Peter Clausen 	if (ret < 0)
62310c5cf30SRichard Purdie 		return ret;
62410c5cf30SRichard Purdie 
625143b4484SKuninori Morimoto 	snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
62610c5cf30SRichard Purdie 
627b4f4f2ebSLars-Peter Clausen 	if (ret == 0)
628143b4484SKuninori Morimoto 		snd_soc_component_cache_sync(component);
62910c5cf30SRichard Purdie 
63010c5cf30SRichard Purdie 	return ret;
63110c5cf30SRichard Purdie }
63210c5cf30SRichard Purdie 
wm9712_soc_probe(struct snd_soc_component * component)633143b4484SKuninori Morimoto static int wm9712_soc_probe(struct snd_soc_component *component)
63410c5cf30SRichard Purdie {
635143b4484SKuninori Morimoto 	struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
636b4f4f2ebSLars-Peter Clausen 	struct regmap *regmap;
63710c5cf30SRichard Purdie 
6382ed1a8e0SRobert Jarzmik 	if (wm9712->mfd_pdata) {
6392ed1a8e0SRobert Jarzmik 		wm9712->ac97 = wm9712->mfd_pdata->ac97;
6402ed1a8e0SRobert Jarzmik 		regmap = wm9712->mfd_pdata->regmap;
641576ce407SArnd Bergmann 	} else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) {
64218380dccSArnd Bergmann 		int ret;
64318380dccSArnd Bergmann 
644143b4484SKuninori Morimoto 		wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID,
645a575be4cSLars-Peter Clausen 						      WM9712_VENDOR_ID_MASK);
646358a8bb5SLars-Peter Clausen 		if (IS_ERR(wm9712->ac97)) {
647358a8bb5SLars-Peter Clausen 			ret = PTR_ERR(wm9712->ac97);
648143b4484SKuninori Morimoto 			dev_err(component->dev,
6492ed1a8e0SRobert Jarzmik 				"Failed to register AC97 codec: %d\n", ret);
650f0fba2adSLiam Girdwood 			return ret;
651e35115a5SLiam Girdwood 		}
65210c5cf30SRichard Purdie 
653b4f4f2ebSLars-Peter Clausen 		regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config);
654b4f4f2ebSLars-Peter Clausen 		if (IS_ERR(regmap)) {
655143b4484SKuninori Morimoto 			snd_soc_free_ac97_component(wm9712->ac97);
6566bd25aaeSRobert Jarzmik 			return PTR_ERR(regmap);
6575aa3b03aSMark Brown 		}
658576ce407SArnd Bergmann 	} else {
659576ce407SArnd Bergmann 		return -ENXIO;
660b4f4f2ebSLars-Peter Clausen 	}
661b4f4f2ebSLars-Peter Clausen 
662143b4484SKuninori Morimoto 	snd_soc_component_init_regmap(component, regmap);
663b4f4f2ebSLars-Peter Clausen 
66410c5cf30SRichard Purdie 	/* set alc mux to none */
665143b4484SKuninori Morimoto 	snd_soc_component_update_bits(component, AC97_VIDEO, 0x3000, 0x3000);
66610c5cf30SRichard Purdie 
66710c5cf30SRichard Purdie 	return 0;
66810c5cf30SRichard Purdie }
66910c5cf30SRichard Purdie 
wm9712_soc_remove(struct snd_soc_component * component)670143b4484SKuninori Morimoto static void wm9712_soc_remove(struct snd_soc_component *component)
67110c5cf30SRichard Purdie {
672143b4484SKuninori Morimoto 	struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component);
673358a8bb5SLars-Peter Clausen 
674576ce407SArnd Bergmann 	if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9712->mfd_pdata) {
675143b4484SKuninori Morimoto 		snd_soc_component_exit_regmap(component);
676143b4484SKuninori Morimoto 		snd_soc_free_ac97_component(wm9712->ac97);
6772ed1a8e0SRobert Jarzmik 	}
67810c5cf30SRichard Purdie }
67910c5cf30SRichard Purdie 
680143b4484SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wm9712 = {
68110c5cf30SRichard Purdie 	.probe			= wm9712_soc_probe,
68210c5cf30SRichard Purdie 	.remove			= wm9712_soc_remove,
68310c5cf30SRichard Purdie 	.resume			= wm9712_soc_resume,
684f0fba2adSLiam Girdwood 	.set_bias_level		= wm9712_set_bias_level,
6859a812c6bSLars-Peter Clausen 	.controls		= wm9712_snd_ac97_controls,
6869a812c6bSLars-Peter Clausen 	.num_controls		= ARRAY_SIZE(wm9712_snd_ac97_controls),
68798334778SLu Guanqun 	.dapm_widgets		= wm9712_dapm_widgets,
68898334778SLu Guanqun 	.num_dapm_widgets	= ARRAY_SIZE(wm9712_dapm_widgets),
68998334778SLu Guanqun 	.dapm_routes		= wm9712_audio_map,
69098334778SLu Guanqun 	.num_dapm_routes	= ARRAY_SIZE(wm9712_audio_map),
691143b4484SKuninori Morimoto 	.suspend_bias_off	= 1,
692143b4484SKuninori Morimoto 	.idle_bias_on		= 1,
693143b4484SKuninori Morimoto 	.use_pmdown_time	= 1,
694143b4484SKuninori Morimoto 	.endianness		= 1,
69510c5cf30SRichard Purdie };
696f0fba2adSLiam Girdwood 
wm9712_probe(struct platform_device * pdev)6977a79e94eSBill Pemberton static int wm9712_probe(struct platform_device *pdev)
698f0fba2adSLiam Girdwood {
699cf1f2ebeSLars-Peter Clausen 	struct wm9712_priv *wm9712;
700cf1f2ebeSLars-Peter Clausen 
701cf1f2ebeSLars-Peter Clausen 	wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
702cf1f2ebeSLars-Peter Clausen 	if (wm9712 == NULL)
703cf1f2ebeSLars-Peter Clausen 		return -ENOMEM;
704cf1f2ebeSLars-Peter Clausen 
705cf1f2ebeSLars-Peter Clausen 	mutex_init(&wm9712->lock);
706cf1f2ebeSLars-Peter Clausen 
7072ed1a8e0SRobert Jarzmik 	wm9712->mfd_pdata = dev_get_platdata(&pdev->dev);
708cf1f2ebeSLars-Peter Clausen 	platform_set_drvdata(pdev, wm9712);
709cf1f2ebeSLars-Peter Clausen 
710143b4484SKuninori Morimoto 	return devm_snd_soc_register_component(&pdev->dev,
711143b4484SKuninori Morimoto 			&soc_component_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
712f0fba2adSLiam Girdwood }
713f0fba2adSLiam Girdwood 
714143b4484SKuninori Morimoto static struct platform_driver wm9712_component_driver = {
715f0fba2adSLiam Girdwood 	.driver = {
7165e4cfadaSMarcel Ziswiler 		.name = "wm9712-codec",
717f0fba2adSLiam Girdwood 	},
718f0fba2adSLiam Girdwood 
719f0fba2adSLiam Girdwood 	.probe = wm9712_probe,
720f0fba2adSLiam Girdwood };
721f0fba2adSLiam Girdwood 
722143b4484SKuninori Morimoto module_platform_driver(wm9712_component_driver);
72310c5cf30SRichard Purdie 
72410c5cf30SRichard Purdie MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
72510c5cf30SRichard Purdie MODULE_AUTHOR("Liam Girdwood");
72610c5cf30SRichard Purdie MODULE_LICENSE("GPL");
727