xref: /linux/sound/soc/codecs/wm9713.c (revision 6bbcb459cd50807511491ddf96bca1ef92006bf8)
183ac08c0SLiam Girdwood /*
283ac08c0SLiam Girdwood  * wm9713.c  --  ALSA Soc WM9713 codec support
383ac08c0SLiam Girdwood  *
483ac08c0SLiam Girdwood  * Copyright 2006 Wolfson Microelectronics PLC.
5d331124dSLiam Girdwood  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
683ac08c0SLiam Girdwood  *
783ac08c0SLiam Girdwood  *  This program is free software; you can redistribute  it and/or modify it
883ac08c0SLiam Girdwood  *  under  the terms of  the GNU General  Public License as published by the
983ac08c0SLiam Girdwood  *  Free Software Foundation;  either version 2 of the  License, or (at your
1083ac08c0SLiam Girdwood  *  option) any later version.
1183ac08c0SLiam Girdwood  *
1283ac08c0SLiam Girdwood  *  Features:-
1383ac08c0SLiam Girdwood  *
1483ac08c0SLiam Girdwood  *   o Support for AC97 Codec, Voice DAC and Aux DAC
1583ac08c0SLiam Girdwood  *   o Support for DAPM
1683ac08c0SLiam Girdwood  */
1783ac08c0SLiam Girdwood 
1883ac08c0SLiam Girdwood #include <linux/init.h>
1983ac08c0SLiam Girdwood #include <linux/module.h>
2083ac08c0SLiam Girdwood #include <linux/device.h>
2183ac08c0SLiam Girdwood #include <sound/core.h>
2283ac08c0SLiam Girdwood #include <sound/pcm.h>
2383ac08c0SLiam Girdwood #include <sound/ac97_codec.h>
2483ac08c0SLiam Girdwood #include <sound/initval.h>
2583ac08c0SLiam Girdwood #include <sound/pcm_params.h>
2683ac08c0SLiam Girdwood #include <sound/soc.h>
2783ac08c0SLiam Girdwood #include <sound/soc-dapm.h>
2883ac08c0SLiam Girdwood 
2983ac08c0SLiam Girdwood #include "wm9713.h"
3083ac08c0SLiam Girdwood 
3183ac08c0SLiam Girdwood #define WM9713_VERSION "0.15"
3283ac08c0SLiam Girdwood 
3383ac08c0SLiam Girdwood struct wm9713_priv {
3483ac08c0SLiam Girdwood 	u32 pll_in; /* PLL input frequency */
3583ac08c0SLiam Girdwood };
3683ac08c0SLiam Girdwood 
3783ac08c0SLiam Girdwood static unsigned int ac97_read(struct snd_soc_codec *codec,
3883ac08c0SLiam Girdwood 	unsigned int reg);
3983ac08c0SLiam Girdwood static int ac97_write(struct snd_soc_codec *codec,
4083ac08c0SLiam Girdwood 	unsigned int reg, unsigned int val);
4183ac08c0SLiam Girdwood 
4283ac08c0SLiam Girdwood /*
4383ac08c0SLiam Girdwood  * WM9713 register cache
4483ac08c0SLiam Girdwood  * Reg 0x3c bit 15 is used by touch driver.
4583ac08c0SLiam Girdwood  */
4683ac08c0SLiam Girdwood static const u16 wm9713_reg[] = {
4783ac08c0SLiam Girdwood 	0x6174, 0x8080, 0x8080, 0x8080,
4883ac08c0SLiam Girdwood 	0xc880, 0xe808, 0xe808, 0x0808,
4983ac08c0SLiam Girdwood 	0x00da, 0x8000, 0xd600, 0xaaa0,
5083ac08c0SLiam Girdwood 	0xaaa0, 0xaaa0, 0x0000, 0x0000,
5183ac08c0SLiam Girdwood 	0x0f0f, 0x0040, 0x0000, 0x7f00,
5283ac08c0SLiam Girdwood 	0x0405, 0x0410, 0xbb80, 0xbb80,
5383ac08c0SLiam Girdwood 	0x0000, 0xbb80, 0x0000, 0x4523,
5483ac08c0SLiam Girdwood 	0x0000, 0x2000, 0x7eff, 0xffff,
5583ac08c0SLiam Girdwood 	0x0000, 0x0000, 0x0080, 0x0000,
5683ac08c0SLiam Girdwood 	0x0000, 0x0000, 0xfffe, 0xffff,
5783ac08c0SLiam Girdwood 	0x0000, 0x0000, 0x0000, 0xfffe,
5883ac08c0SLiam Girdwood 	0x4000, 0x0000, 0x0000, 0x0000,
5983ac08c0SLiam Girdwood 	0xb032, 0x3e00, 0x0000, 0x0000,
6083ac08c0SLiam Girdwood 	0x0000, 0x0000, 0x0000, 0x0000,
6183ac08c0SLiam Girdwood 	0x0000, 0x0000, 0x0000, 0x0006,
6283ac08c0SLiam Girdwood 	0x0001, 0x0000, 0x574d, 0x4c13,
6383ac08c0SLiam Girdwood 	0x0000, 0x0000, 0x0000
6483ac08c0SLiam Girdwood };
6583ac08c0SLiam Girdwood 
6683ac08c0SLiam Girdwood /* virtual HP mixers regs */
6783ac08c0SLiam Girdwood #define HPL_MIXER	0x80
6883ac08c0SLiam Girdwood #define HPR_MIXER	0x82
6983ac08c0SLiam Girdwood #define MICB_MUX	0x82
7083ac08c0SLiam Girdwood 
7183ac08c0SLiam Girdwood static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
7283ac08c0SLiam Girdwood static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
7383ac08c0SLiam Girdwood static const char *wm9713_rec_src[] =
7483ac08c0SLiam Girdwood 	{"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker",
7583ac08c0SLiam Girdwood 	"Mono Out", "Zh"};
7683ac08c0SLiam Girdwood static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
7783ac08c0SLiam Girdwood static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
7883ac08c0SLiam Girdwood static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv",
7983ac08c0SLiam Girdwood 	"Mono Vmid", "Inv Vmid"};
8083ac08c0SLiam Girdwood static const char *wm9713_spk_pga[] =
8183ac08c0SLiam Girdwood 	{"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
8283ac08c0SLiam Girdwood 	"Speaker Vmid", "Inv Vmid"};
8383ac08c0SLiam Girdwood static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone",
8483ac08c0SLiam Girdwood 	"Headphone Vmid"};
8583ac08c0SLiam Girdwood static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"};
8683ac08c0SLiam Girdwood static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"};
8783ac08c0SLiam Girdwood static const char *wm9713_dac_inv[] =
8883ac08c0SLiam Girdwood 	{"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone",
8983ac08c0SLiam Girdwood 	"Headphone Mono", "NC", "Vmid"};
9083ac08c0SLiam Girdwood static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"};
9183ac08c0SLiam Girdwood static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"};
9283ac08c0SLiam Girdwood static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"};
9383ac08c0SLiam Girdwood static const char *wm9713_micb_select[] = {"MPB", "MPA"};
9483ac08c0SLiam Girdwood 
9583ac08c0SLiam Girdwood static const struct soc_enum wm9713_enum[] = {
9683ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */
9783ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */
9883ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux),  /* record mux mono 2 */
9983ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src),  /* record mux left 3 */
10083ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src),  /* record mux right 4*/
10183ac08c0SLiam Girdwood SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */
10283ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/
10383ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */
10483ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */
10583ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */
10683ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */
10783ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */
10883ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */
10983ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */
11083ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */
11183ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
11283ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
11383ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
11483ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
11583ac08c0SLiam Girdwood SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
11683ac08c0SLiam Girdwood };
11783ac08c0SLiam Girdwood 
11883ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
11983ac08c0SLiam Girdwood SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
12083ac08c0SLiam Girdwood SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
12183ac08c0SLiam Girdwood SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
12283ac08c0SLiam Girdwood SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1),
12383ac08c0SLiam Girdwood SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
12483ac08c0SLiam Girdwood SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
12583ac08c0SLiam Girdwood SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
12683ac08c0SLiam Girdwood SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
12783ac08c0SLiam Girdwood 
12883ac08c0SLiam Girdwood SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
12983ac08c0SLiam Girdwood SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
13083ac08c0SLiam Girdwood 
13183ac08c0SLiam Girdwood SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
13283ac08c0SLiam Girdwood SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
13383ac08c0SLiam Girdwood SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
13483ac08c0SLiam Girdwood SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
13583ac08c0SLiam Girdwood 
13683ac08c0SLiam Girdwood SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
13783ac08c0SLiam Girdwood SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
13883ac08c0SLiam Girdwood SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
13983ac08c0SLiam Girdwood 
14083ac08c0SLiam Girdwood SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
14183ac08c0SLiam Girdwood SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
14283ac08c0SLiam Girdwood SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
14383ac08c0SLiam Girdwood SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
14483ac08c0SLiam Girdwood SOC_ENUM("ALC Function", wm9713_enum[6]),
14583ac08c0SLiam Girdwood SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
14683ac08c0SLiam Girdwood SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
14783ac08c0SLiam Girdwood SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
14883ac08c0SLiam Girdwood SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
14983ac08c0SLiam Girdwood SOC_ENUM("ALC NG Type", wm9713_enum[17]),
15083ac08c0SLiam Girdwood SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
15183ac08c0SLiam Girdwood 
15283ac08c0SLiam Girdwood SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0),
15383ac08c0SLiam Girdwood SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
15483ac08c0SLiam Girdwood 
15583ac08c0SLiam Girdwood SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
15683ac08c0SLiam Girdwood SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
15783ac08c0SLiam Girdwood SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1),
15883ac08c0SLiam Girdwood 
15983ac08c0SLiam Girdwood SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
16083ac08c0SLiam Girdwood SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
16183ac08c0SLiam Girdwood SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1),
16283ac08c0SLiam Girdwood 
16383ac08c0SLiam Girdwood SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1),
16483ac08c0SLiam Girdwood SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
16583ac08c0SLiam Girdwood SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
16683ac08c0SLiam Girdwood SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
16783ac08c0SLiam Girdwood 
16883ac08c0SLiam Girdwood SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
16983ac08c0SLiam Girdwood SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
17083ac08c0SLiam Girdwood SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
17183ac08c0SLiam Girdwood 
17283ac08c0SLiam Girdwood SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
17383ac08c0SLiam Girdwood SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
17483ac08c0SLiam Girdwood SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
17583ac08c0SLiam Girdwood 
17683ac08c0SLiam Girdwood SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
17783ac08c0SLiam Girdwood SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
17883ac08c0SLiam Girdwood SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
17983ac08c0SLiam Girdwood 
18083ac08c0SLiam Girdwood SOC_ENUM("Bass Control", wm9713_enum[16]),
18183ac08c0SLiam Girdwood SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
18283ac08c0SLiam Girdwood SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
18383ac08c0SLiam Girdwood SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
18483ac08c0SLiam Girdwood SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
18583ac08c0SLiam Girdwood SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
18683ac08c0SLiam Girdwood 
18783ac08c0SLiam Girdwood SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
18883ac08c0SLiam Girdwood SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
18983ac08c0SLiam Girdwood SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
19083ac08c0SLiam Girdwood };
19183ac08c0SLiam Girdwood 
192*6bbcb459SMark Brown static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
193*6bbcb459SMark Brown 				 struct snd_kcontrol *kcontrol, int event)
194*6bbcb459SMark Brown {
195*6bbcb459SMark Brown 	struct snd_soc_codec *codec = w->codec;
196*6bbcb459SMark Brown 	u16 status, rate;
197*6bbcb459SMark Brown 
198*6bbcb459SMark Brown 	BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
199*6bbcb459SMark Brown 
200*6bbcb459SMark Brown 	/* Gracefully shut down the voice interface. */
201*6bbcb459SMark Brown 	status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
202*6bbcb459SMark Brown 	rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
203*6bbcb459SMark Brown 	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
204*6bbcb459SMark Brown 	schedule_timeout_interruptible(msecs_to_jiffies(1));
205*6bbcb459SMark Brown 	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
206*6bbcb459SMark Brown 	ac97_write(codec, AC97_EXTENDED_MID, status);
207*6bbcb459SMark Brown 
208*6bbcb459SMark Brown 	return 0;
209*6bbcb459SMark Brown }
210*6bbcb459SMark Brown 
211*6bbcb459SMark Brown 
21283ac08c0SLiam Girdwood /* We have to create a fake left and right HP mixers because
21383ac08c0SLiam Girdwood  * the codec only has a single control that is shared by both channels.
21483ac08c0SLiam Girdwood  * This makes it impossible to determine the audio path using the current
21583ac08c0SLiam Girdwood  * register map, thus we add a new (virtual) register to help determine the
21683ac08c0SLiam Girdwood  * audio route within the device.
21783ac08c0SLiam Girdwood  */
21883ac08c0SLiam Girdwood static int mixer_event(struct snd_soc_dapm_widget *w,
21983ac08c0SLiam Girdwood 	struct snd_kcontrol *kcontrol, int event)
22083ac08c0SLiam Girdwood {
22183ac08c0SLiam Girdwood 	u16 l, r, beep, tone, phone, rec, pcm, aux;
22283ac08c0SLiam Girdwood 
22383ac08c0SLiam Girdwood 	l = ac97_read(w->codec, HPL_MIXER);
22483ac08c0SLiam Girdwood 	r = ac97_read(w->codec, HPR_MIXER);
22583ac08c0SLiam Girdwood 	beep = ac97_read(w->codec, AC97_PC_BEEP);
22683ac08c0SLiam Girdwood 	tone = ac97_read(w->codec, AC97_MASTER_TONE);
22783ac08c0SLiam Girdwood 	phone = ac97_read(w->codec, AC97_PHONE);
22883ac08c0SLiam Girdwood 	rec = ac97_read(w->codec, AC97_REC_SEL);
22983ac08c0SLiam Girdwood 	pcm = ac97_read(w->codec, AC97_PCM);
23083ac08c0SLiam Girdwood 	aux = ac97_read(w->codec, AC97_AUX);
23183ac08c0SLiam Girdwood 
23283ac08c0SLiam Girdwood 	if (event & SND_SOC_DAPM_PRE_REG)
23383ac08c0SLiam Girdwood 		return 0;
23483ac08c0SLiam Girdwood 	if ((l & 0x1) || (r & 0x1))
23583ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
23683ac08c0SLiam Girdwood 	else
23783ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
23883ac08c0SLiam Girdwood 
23983ac08c0SLiam Girdwood 	if ((l & 0x2) || (r & 0x2))
24083ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
24183ac08c0SLiam Girdwood 	else
24283ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
24383ac08c0SLiam Girdwood 
24483ac08c0SLiam Girdwood 	if ((l & 0x4) || (r & 0x4))
24583ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
24683ac08c0SLiam Girdwood 	else
24783ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
24883ac08c0SLiam Girdwood 
24983ac08c0SLiam Girdwood 	if ((l & 0x8) || (r & 0x8))
25083ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
25183ac08c0SLiam Girdwood 	else
25283ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
25383ac08c0SLiam Girdwood 
25483ac08c0SLiam Girdwood 	if ((l & 0x10) || (r & 0x10))
25583ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
25683ac08c0SLiam Girdwood 	else
25783ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
25883ac08c0SLiam Girdwood 
25983ac08c0SLiam Girdwood 	if ((l & 0x20) || (r & 0x20))
26083ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
26183ac08c0SLiam Girdwood 	else
26283ac08c0SLiam Girdwood 		ac97_write(w->codec, AC97_AUX, aux | 0x8000);
26383ac08c0SLiam Girdwood 
26483ac08c0SLiam Girdwood 	return 0;
26583ac08c0SLiam Girdwood }
26683ac08c0SLiam Girdwood 
26783ac08c0SLiam Girdwood /* Left Headphone Mixers */
26883ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
26983ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0),
27083ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
27183ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
27283ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
27383ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
27483ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
27583ac08c0SLiam Girdwood };
27683ac08c0SLiam Girdwood 
27783ac08c0SLiam Girdwood /* Right Headphone Mixers */
27883ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
27983ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0),
28083ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
28183ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
28283ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
28383ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
28483ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
28583ac08c0SLiam Girdwood };
28683ac08c0SLiam Girdwood 
28783ac08c0SLiam Girdwood /* headphone capture mux */
28883ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls =
28983ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[1]);
29083ac08c0SLiam Girdwood 
29183ac08c0SLiam Girdwood /* headphone mic mux */
29283ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls =
29383ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[0]);
29483ac08c0SLiam Girdwood 
29583ac08c0SLiam Girdwood /* Speaker Mixer */
29683ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = {
29783ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1),
29883ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1),
29983ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1),
30083ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1),
30183ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1),
30283ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1),
30383ac08c0SLiam Girdwood };
30483ac08c0SLiam Girdwood 
30583ac08c0SLiam Girdwood /* Mono Mixer */
30683ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = {
30783ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1),
30883ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1),
30983ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1),
31083ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1),
31183ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1),
31283ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1),
31383ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1),
31483ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1),
31583ac08c0SLiam Girdwood };
31683ac08c0SLiam Girdwood 
31783ac08c0SLiam Girdwood /* mono mic mux */
31883ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls =
31983ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[2]);
32083ac08c0SLiam Girdwood 
32183ac08c0SLiam Girdwood /* mono output mux */
32283ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mono_mux_controls =
32383ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[7]);
32483ac08c0SLiam Girdwood 
32583ac08c0SLiam Girdwood /* speaker left output mux */
32683ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls =
32783ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[8]);
32883ac08c0SLiam Girdwood 
32983ac08c0SLiam Girdwood /* speaker right output mux */
33083ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls =
33183ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[9]);
33283ac08c0SLiam Girdwood 
33383ac08c0SLiam Girdwood /* headphone left output mux */
33483ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls =
33583ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[10]);
33683ac08c0SLiam Girdwood 
33783ac08c0SLiam Girdwood /* headphone right output mux */
33883ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls =
33983ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[11]);
34083ac08c0SLiam Girdwood 
34183ac08c0SLiam Girdwood /* Out3 mux */
34283ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_out3_mux_controls =
34383ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[12]);
34483ac08c0SLiam Girdwood 
34583ac08c0SLiam Girdwood /* Out4 mux */
34683ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_out4_mux_controls =
34783ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[13]);
34883ac08c0SLiam Girdwood 
34983ac08c0SLiam Girdwood /* DAC inv mux 1 */
35083ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls =
35183ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[14]);
35283ac08c0SLiam Girdwood 
35383ac08c0SLiam Girdwood /* DAC inv mux 2 */
35483ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls =
35583ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[15]);
35683ac08c0SLiam Girdwood 
35783ac08c0SLiam Girdwood /* Capture source left */
35883ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls =
35983ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[3]);
36083ac08c0SLiam Girdwood 
36183ac08c0SLiam Girdwood /* Capture source right */
36283ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls =
36383ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[4]);
36483ac08c0SLiam Girdwood 
36583ac08c0SLiam Girdwood /* mic source */
36683ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls =
36783ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[18]);
36883ac08c0SLiam Girdwood 
36983ac08c0SLiam Girdwood /* mic source B virtual control */
37083ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls =
37183ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[19]);
37283ac08c0SLiam Girdwood 
37383ac08c0SLiam Girdwood static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = {
37483ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0,
37583ac08c0SLiam Girdwood 	&wm9713_hp_rec_mux_controls),
37683ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
37783ac08c0SLiam Girdwood 	&wm9713_hp_mic_mux_controls),
37883ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0,
37983ac08c0SLiam Girdwood 	&wm9713_mono_mic_mux_controls),
38083ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0,
38183ac08c0SLiam Girdwood 	&wm9713_mono_mux_controls),
38283ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0,
38383ac08c0SLiam Girdwood 	&wm9713_hp_spkl_mux_controls),
38483ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0,
38583ac08c0SLiam Girdwood 	&wm9713_hp_spkr_mux_controls),
38683ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0,
38783ac08c0SLiam Girdwood 	&wm9713_hpl_out_mux_controls),
38883ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0,
38983ac08c0SLiam Girdwood 	&wm9713_hpr_out_mux_controls),
39083ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0,
39183ac08c0SLiam Girdwood 	&wm9713_out3_mux_controls),
39283ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0,
39383ac08c0SLiam Girdwood 	&wm9713_out4_mux_controls),
39483ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0,
39583ac08c0SLiam Girdwood 	&wm9713_dac_inv1_mux_controls),
39683ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0,
39783ac08c0SLiam Girdwood 	&wm9713_dac_inv2_mux_controls),
39883ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
39983ac08c0SLiam Girdwood 	&wm9713_rec_srcl_mux_controls),
40083ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
40183ac08c0SLiam Girdwood 	&wm9713_rec_srcr_mux_controls),
40283ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
40383ac08c0SLiam Girdwood 	&wm9713_mic_sel_mux_controls),
40483ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
40583ac08c0SLiam Girdwood 	&wm9713_micb_sel_mux_controls),
40683ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
40783ac08c0SLiam Girdwood 	&wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
40883ac08c0SLiam Girdwood 	mixer_event, SND_SOC_DAPM_POST_REG),
40983ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
41083ac08c0SLiam Girdwood 	&wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
41183ac08c0SLiam Girdwood 	mixer_event, SND_SOC_DAPM_POST_REG),
41283ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
41383ac08c0SLiam Girdwood 	&wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
41483ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
41583ac08c0SLiam Girdwood 	&wm9713_speaker_mixer_controls[0],
41683ac08c0SLiam Girdwood 	ARRAY_SIZE(wm9713_speaker_mixer_controls)),
41783ac08c0SLiam Girdwood SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1),
41883ac08c0SLiam Girdwood SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1),
41983ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
42083ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
42183ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
42283ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
423*6bbcb459SMark Brown SND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1,
424*6bbcb459SMark Brown 		   wm9713_voice_shutdown, SND_SOC_DAPM_PRE_PMD),
42583ac08c0SLiam Girdwood SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
426ccbc301eSMark Brown SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0),
427ccbc301eSMark Brown SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0),
428ccbc301eSMark Brown SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0),
429ccbc301eSMark Brown SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0),
430ccbc301eSMark Brown SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0),
431ccbc301eSMark Brown SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0),
43283ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
43383ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
43483ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
43583ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0),
43683ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0),
43783ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0),
43883ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0),
43983ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0),
44083ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0),
44183ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0),
44283ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0),
44383ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0),
44483ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0),
44583ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0),
44683ac08c0SLiam Girdwood SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1),
44783ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("MONO"),
44883ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("HPL"),
44983ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("HPR"),
45083ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("SPKL"),
45183ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("SPKR"),
45283ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("OUT3"),
45383ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("OUT4"),
45483ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("LINEL"),
45583ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("LINER"),
45683ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MONOIN"),
45783ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("PCBEEP"),
45883ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MIC1"),
45983ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MIC2A"),
46083ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MIC2B"),
46183ac08c0SLiam Girdwood SND_SOC_DAPM_VMID("VMID"),
46283ac08c0SLiam Girdwood };
46383ac08c0SLiam Girdwood 
464a65f0568SMark Brown static const struct snd_soc_dapm_route audio_map[] = {
46583ac08c0SLiam Girdwood 	/* left HP mixer */
46683ac08c0SLiam Girdwood 	{"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
46783ac08c0SLiam Girdwood 	{"Left HP Mixer", "Voice Playback Switch",   "Voice DAC"},
46883ac08c0SLiam Girdwood 	{"Left HP Mixer", "Aux Playback Switch",     "Aux DAC"},
46983ac08c0SLiam Girdwood 	{"Left HP Mixer", "Bypass Playback Switch",  "Left Line In"},
47083ac08c0SLiam Girdwood 	{"Left HP Mixer", "PCM Playback Switch",     "Left DAC"},
47183ac08c0SLiam Girdwood 	{"Left HP Mixer", "MonoIn Playback Switch",  "Mono In"},
47283ac08c0SLiam Girdwood 	{"Left HP Mixer", NULL,  "Capture Headphone Mux"},
47383ac08c0SLiam Girdwood 
47483ac08c0SLiam Girdwood 	/* right HP mixer */
47583ac08c0SLiam Girdwood 	{"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
47683ac08c0SLiam Girdwood 	{"Right HP Mixer", "Voice Playback Switch",   "Voice DAC"},
47783ac08c0SLiam Girdwood 	{"Right HP Mixer", "Aux Playback Switch",     "Aux DAC"},
47883ac08c0SLiam Girdwood 	{"Right HP Mixer", "Bypass Playback Switch",  "Right Line In"},
47983ac08c0SLiam Girdwood 	{"Right HP Mixer", "PCM Playback Switch",     "Right DAC"},
48083ac08c0SLiam Girdwood 	{"Right HP Mixer", "MonoIn Playback Switch",  "Mono In"},
48183ac08c0SLiam Girdwood 	{"Right HP Mixer", NULL,  "Capture Headphone Mux"},
48283ac08c0SLiam Girdwood 
48383ac08c0SLiam Girdwood 	/* virtual mixer - mixes left & right channels for spk and mono */
48483ac08c0SLiam Girdwood 	{"AC97 Mixer", NULL, "Left DAC"},
48583ac08c0SLiam Girdwood 	{"AC97 Mixer", NULL, "Right DAC"},
48683ac08c0SLiam Girdwood 	{"Line Mixer", NULL, "Right Line In"},
48783ac08c0SLiam Girdwood 	{"Line Mixer", NULL, "Left Line In"},
48883ac08c0SLiam Girdwood 	{"HP Mixer", NULL, "Left HP Mixer"},
48983ac08c0SLiam Girdwood 	{"HP Mixer", NULL, "Right HP Mixer"},
49083ac08c0SLiam Girdwood 	{"Capture Mixer", NULL, "Left Capture Source"},
49183ac08c0SLiam Girdwood 	{"Capture Mixer", NULL, "Right Capture Source"},
49283ac08c0SLiam Girdwood 
49383ac08c0SLiam Girdwood 	/* speaker mixer */
49483ac08c0SLiam Girdwood 	{"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"},
49583ac08c0SLiam Girdwood 	{"Speaker Mixer", "Voice Playback Switch",   "Voice DAC"},
49683ac08c0SLiam Girdwood 	{"Speaker Mixer", "Aux Playback Switch",     "Aux DAC"},
49783ac08c0SLiam Girdwood 	{"Speaker Mixer", "Bypass Playback Switch",  "Line Mixer"},
49883ac08c0SLiam Girdwood 	{"Speaker Mixer", "PCM Playback Switch",     "AC97 Mixer"},
49983ac08c0SLiam Girdwood 	{"Speaker Mixer", "MonoIn Playback Switch",  "Mono In"},
50083ac08c0SLiam Girdwood 
50183ac08c0SLiam Girdwood 	/* mono mixer */
50283ac08c0SLiam Girdwood 	{"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"},
50383ac08c0SLiam Girdwood 	{"Mono Mixer", "Voice Playback Switch",   "Voice DAC"},
50483ac08c0SLiam Girdwood 	{"Mono Mixer", "Aux Playback Switch",     "Aux DAC"},
50583ac08c0SLiam Girdwood 	{"Mono Mixer", "Bypass Playback Switch",  "Line Mixer"},
50683ac08c0SLiam Girdwood 	{"Mono Mixer", "PCM Playback Switch",     "AC97 Mixer"},
507c0bbf48dSRobert Jarzmik 	{"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"},
508c0bbf48dSRobert Jarzmik 	{"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"},
50983ac08c0SLiam Girdwood 	{"Mono Mixer", NULL,  "Capture Mono Mux"},
51083ac08c0SLiam Girdwood 
51183ac08c0SLiam Girdwood 	/* DAC inv mux 1 */
51283ac08c0SLiam Girdwood 	{"DAC Inv Mux 1", "Mono", "Mono Mixer"},
51383ac08c0SLiam Girdwood 	{"DAC Inv Mux 1", "Speaker", "Speaker Mixer"},
51483ac08c0SLiam Girdwood 	{"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"},
51583ac08c0SLiam Girdwood 	{"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"},
51683ac08c0SLiam Girdwood 	{"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"},
51783ac08c0SLiam Girdwood 
51883ac08c0SLiam Girdwood 	/* DAC inv mux 2 */
51983ac08c0SLiam Girdwood 	{"DAC Inv Mux 2", "Mono", "Mono Mixer"},
52083ac08c0SLiam Girdwood 	{"DAC Inv Mux 2", "Speaker", "Speaker Mixer"},
52183ac08c0SLiam Girdwood 	{"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"},
52283ac08c0SLiam Girdwood 	{"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"},
52383ac08c0SLiam Girdwood 	{"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"},
52483ac08c0SLiam Girdwood 
52583ac08c0SLiam Girdwood 	/* headphone left mux */
52683ac08c0SLiam Girdwood 	{"Left Headphone Out Mux", "Headphone", "Left HP Mixer"},
52783ac08c0SLiam Girdwood 
52883ac08c0SLiam Girdwood 	/* headphone right mux */
52983ac08c0SLiam Girdwood 	{"Right Headphone Out Mux", "Headphone", "Right HP Mixer"},
53083ac08c0SLiam Girdwood 
53183ac08c0SLiam Girdwood 	/* speaker left mux */
53283ac08c0SLiam Girdwood 	{"Left Speaker Out Mux", "Headphone", "Left HP Mixer"},
53383ac08c0SLiam Girdwood 	{"Left Speaker Out Mux", "Speaker", "Speaker Mixer"},
53483ac08c0SLiam Girdwood 	{"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"},
53583ac08c0SLiam Girdwood 
53683ac08c0SLiam Girdwood 	/* speaker right mux */
53783ac08c0SLiam Girdwood 	{"Right Speaker Out Mux", "Headphone", "Right HP Mixer"},
53883ac08c0SLiam Girdwood 	{"Right Speaker Out Mux", "Speaker", "Speaker Mixer"},
53983ac08c0SLiam Girdwood 	{"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"},
54083ac08c0SLiam Girdwood 
54183ac08c0SLiam Girdwood 	/* mono mux */
54283ac08c0SLiam Girdwood 	{"Mono Out Mux", "Mono", "Mono Mixer"},
54383ac08c0SLiam Girdwood 	{"Mono Out Mux", "Inv", "DAC Inv Mux 1"},
54483ac08c0SLiam Girdwood 
54583ac08c0SLiam Girdwood 	/* out 3 mux */
54683ac08c0SLiam Girdwood 	{"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"},
54783ac08c0SLiam Girdwood 
54883ac08c0SLiam Girdwood 	/* out 4 mux */
54983ac08c0SLiam Girdwood 	{"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"},
55083ac08c0SLiam Girdwood 
55183ac08c0SLiam Girdwood 	/* output pga */
55283ac08c0SLiam Girdwood 	{"HPL", NULL, "Left Headphone"},
55383ac08c0SLiam Girdwood 	{"Left Headphone", NULL, "Left Headphone Out Mux"},
55483ac08c0SLiam Girdwood 	{"HPR", NULL, "Right Headphone"},
55583ac08c0SLiam Girdwood 	{"Right Headphone", NULL, "Right Headphone Out Mux"},
55683ac08c0SLiam Girdwood 	{"OUT3", NULL, "Out 3"},
55783ac08c0SLiam Girdwood 	{"Out 3", NULL, "Out 3 Mux"},
55883ac08c0SLiam Girdwood 	{"OUT4", NULL, "Out 4"},
55983ac08c0SLiam Girdwood 	{"Out 4", NULL, "Out 4 Mux"},
56083ac08c0SLiam Girdwood 	{"SPKL", NULL, "Left Speaker"},
56183ac08c0SLiam Girdwood 	{"Left Speaker", NULL, "Left Speaker Out Mux"},
56283ac08c0SLiam Girdwood 	{"SPKR", NULL, "Right Speaker"},
56383ac08c0SLiam Girdwood 	{"Right Speaker", NULL, "Right Speaker Out Mux"},
56483ac08c0SLiam Girdwood 	{"MONO", NULL, "Mono Out"},
56583ac08c0SLiam Girdwood 	{"Mono Out", NULL, "Mono Out Mux"},
56683ac08c0SLiam Girdwood 
56783ac08c0SLiam Girdwood 	/* input pga */
56883ac08c0SLiam Girdwood 	{"Left Line In", NULL, "LINEL"},
56983ac08c0SLiam Girdwood 	{"Right Line In", NULL, "LINER"},
57083ac08c0SLiam Girdwood 	{"Mono In", NULL, "MONOIN"},
57183ac08c0SLiam Girdwood 	{"Mic A PGA", NULL, "Mic A Pre Amp"},
57283ac08c0SLiam Girdwood 	{"Mic B PGA", NULL, "Mic B Pre Amp"},
57383ac08c0SLiam Girdwood 
57483ac08c0SLiam Girdwood 	/* left capture select */
57583ac08c0SLiam Girdwood 	{"Left Capture Source", "Mic 1", "Mic A Pre Amp"},
57683ac08c0SLiam Girdwood 	{"Left Capture Source", "Mic 2", "Mic B Pre Amp"},
57783ac08c0SLiam Girdwood 	{"Left Capture Source", "Line", "LINEL"},
57883ac08c0SLiam Girdwood 	{"Left Capture Source", "Mono In", "MONOIN"},
57983ac08c0SLiam Girdwood 	{"Left Capture Source", "Headphone", "Left HP Mixer"},
58083ac08c0SLiam Girdwood 	{"Left Capture Source", "Speaker", "Speaker Mixer"},
58183ac08c0SLiam Girdwood 	{"Left Capture Source", "Mono Out", "Mono Mixer"},
58283ac08c0SLiam Girdwood 
58383ac08c0SLiam Girdwood 	/* right capture select */
58483ac08c0SLiam Girdwood 	{"Right Capture Source", "Mic 1", "Mic A Pre Amp"},
58583ac08c0SLiam Girdwood 	{"Right Capture Source", "Mic 2", "Mic B Pre Amp"},
58683ac08c0SLiam Girdwood 	{"Right Capture Source", "Line", "LINER"},
58783ac08c0SLiam Girdwood 	{"Right Capture Source", "Mono In", "MONOIN"},
58883ac08c0SLiam Girdwood 	{"Right Capture Source", "Headphone", "Right HP Mixer"},
58983ac08c0SLiam Girdwood 	{"Right Capture Source", "Speaker", "Speaker Mixer"},
59083ac08c0SLiam Girdwood 	{"Right Capture Source", "Mono Out", "Mono Mixer"},
59183ac08c0SLiam Girdwood 
59283ac08c0SLiam Girdwood 	/* left ADC */
59383ac08c0SLiam Girdwood 	{"Left ADC", NULL, "Left Capture Source"},
594ccbc301eSMark Brown 	{"Left Voice ADC", NULL, "Left ADC"},
595ccbc301eSMark Brown 	{"Left HiFi ADC", NULL, "Left ADC"},
59683ac08c0SLiam Girdwood 
59783ac08c0SLiam Girdwood 	/* right ADC */
59883ac08c0SLiam Girdwood 	{"Right ADC", NULL, "Right Capture Source"},
599ccbc301eSMark Brown 	{"Right Voice ADC", NULL, "Right ADC"},
600ccbc301eSMark Brown 	{"Right HiFi ADC", NULL, "Right ADC"},
60183ac08c0SLiam Girdwood 
60283ac08c0SLiam Girdwood 	/* mic */
60383ac08c0SLiam Girdwood 	{"Mic A Pre Amp", NULL, "Mic A Source"},
60483ac08c0SLiam Girdwood 	{"Mic A Source", "Mic 1", "MIC1"},
60583ac08c0SLiam Girdwood 	{"Mic A Source", "Mic 2 A", "MIC2A"},
60683ac08c0SLiam Girdwood 	{"Mic A Source", "Mic 2 B", "Mic B Source"},
60783ac08c0SLiam Girdwood 	{"Mic B Pre Amp", "MPB", "Mic B Source"},
60883ac08c0SLiam Girdwood 	{"Mic B Source", NULL, "MIC2B"},
60983ac08c0SLiam Girdwood 
61083ac08c0SLiam Girdwood 	/* headphone capture */
61183ac08c0SLiam Girdwood 	{"Capture Headphone Mux", "Stereo", "Capture Mixer"},
61283ac08c0SLiam Girdwood 	{"Capture Headphone Mux", "Left", "Left Capture Source"},
61383ac08c0SLiam Girdwood 	{"Capture Headphone Mux", "Right", "Right Capture Source"},
61483ac08c0SLiam Girdwood 
61583ac08c0SLiam Girdwood 	/* mono capture */
61683ac08c0SLiam Girdwood 	{"Capture Mono Mux", "Stereo", "Capture Mixer"},
61783ac08c0SLiam Girdwood 	{"Capture Mono Mux", "Left", "Left Capture Source"},
61883ac08c0SLiam Girdwood 	{"Capture Mono Mux", "Right", "Right Capture Source"},
61983ac08c0SLiam Girdwood };
62083ac08c0SLiam Girdwood 
62183ac08c0SLiam Girdwood static int wm9713_add_widgets(struct snd_soc_codec *codec)
62283ac08c0SLiam Girdwood {
623a65f0568SMark Brown 	snd_soc_dapm_new_controls(codec, wm9713_dapm_widgets,
624a65f0568SMark Brown 				  ARRAY_SIZE(wm9713_dapm_widgets));
62583ac08c0SLiam Girdwood 
626a65f0568SMark Brown 	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
62783ac08c0SLiam Girdwood 
62883ac08c0SLiam Girdwood 	snd_soc_dapm_new_widgets(codec);
62983ac08c0SLiam Girdwood 	return 0;
63083ac08c0SLiam Girdwood }
63183ac08c0SLiam Girdwood 
63283ac08c0SLiam Girdwood static unsigned int ac97_read(struct snd_soc_codec *codec,
63383ac08c0SLiam Girdwood 	unsigned int reg)
63483ac08c0SLiam Girdwood {
63583ac08c0SLiam Girdwood 	u16 *cache = codec->reg_cache;
63683ac08c0SLiam Girdwood 
63783ac08c0SLiam Girdwood 	if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
63883ac08c0SLiam Girdwood 		reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
63983ac08c0SLiam Girdwood 		reg == AC97_CD)
64083ac08c0SLiam Girdwood 		return soc_ac97_ops.read(codec->ac97, reg);
64183ac08c0SLiam Girdwood 	else {
64283ac08c0SLiam Girdwood 		reg = reg >> 1;
64383ac08c0SLiam Girdwood 
64491432e97SIan Molton 		if (reg >= (ARRAY_SIZE(wm9713_reg)))
64583ac08c0SLiam Girdwood 			return -EIO;
64683ac08c0SLiam Girdwood 
64783ac08c0SLiam Girdwood 		return cache[reg];
64883ac08c0SLiam Girdwood 	}
64983ac08c0SLiam Girdwood }
65083ac08c0SLiam Girdwood 
65183ac08c0SLiam Girdwood static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
65283ac08c0SLiam Girdwood 	unsigned int val)
65383ac08c0SLiam Girdwood {
65483ac08c0SLiam Girdwood 	u16 *cache = codec->reg_cache;
65583ac08c0SLiam Girdwood 	if (reg < 0x7c)
65683ac08c0SLiam Girdwood 		soc_ac97_ops.write(codec->ac97, reg, val);
65783ac08c0SLiam Girdwood 	reg = reg >> 1;
65891432e97SIan Molton 	if (reg < (ARRAY_SIZE(wm9713_reg)))
65983ac08c0SLiam Girdwood 		cache[reg] = val;
66083ac08c0SLiam Girdwood 
66183ac08c0SLiam Girdwood 	return 0;
66283ac08c0SLiam Girdwood }
66383ac08c0SLiam Girdwood 
66483ac08c0SLiam Girdwood /* PLL divisors */
66583ac08c0SLiam Girdwood struct _pll_div {
66683ac08c0SLiam Girdwood 	u32 divsel:1;
66783ac08c0SLiam Girdwood 	u32 divctl:1;
66883ac08c0SLiam Girdwood 	u32 lf:1;
66983ac08c0SLiam Girdwood 	u32 n:4;
67083ac08c0SLiam Girdwood 	u32 k:24;
67183ac08c0SLiam Girdwood };
67283ac08c0SLiam Girdwood 
67383ac08c0SLiam Girdwood /* The size in bits of the PLL divide multiplied by 10
67483ac08c0SLiam Girdwood  * to allow rounding later */
67583ac08c0SLiam Girdwood #define FIXED_PLL_SIZE ((1 << 22) * 10)
67683ac08c0SLiam Girdwood 
67783ac08c0SLiam Girdwood static void pll_factors(struct _pll_div *pll_div, unsigned int source)
67883ac08c0SLiam Girdwood {
67983ac08c0SLiam Girdwood 	u64 Kpart;
68083ac08c0SLiam Girdwood 	unsigned int K, Ndiv, Nmod, target;
68183ac08c0SLiam Girdwood 
68283ac08c0SLiam Girdwood 	/* The the PLL output is always 98.304MHz. */
68383ac08c0SLiam Girdwood 	target = 98304000;
68483ac08c0SLiam Girdwood 
68583ac08c0SLiam Girdwood 	/* If the input frequency is over 14.4MHz then scale it down. */
68683ac08c0SLiam Girdwood 	if (source > 14400000) {
68783ac08c0SLiam Girdwood 		source >>= 1;
68883ac08c0SLiam Girdwood 		pll_div->divsel = 1;
68983ac08c0SLiam Girdwood 
69083ac08c0SLiam Girdwood 		if (source > 14400000) {
69183ac08c0SLiam Girdwood 			source >>= 1;
69283ac08c0SLiam Girdwood 			pll_div->divctl = 1;
69383ac08c0SLiam Girdwood 		} else
69483ac08c0SLiam Girdwood 			pll_div->divctl = 0;
69583ac08c0SLiam Girdwood 
69683ac08c0SLiam Girdwood 	} else {
69783ac08c0SLiam Girdwood 		pll_div->divsel = 0;
69883ac08c0SLiam Girdwood 		pll_div->divctl = 0;
69983ac08c0SLiam Girdwood 	}
70083ac08c0SLiam Girdwood 
70183ac08c0SLiam Girdwood 	/* Low frequency sources require an additional divide in the
70283ac08c0SLiam Girdwood 	 * loop.
70383ac08c0SLiam Girdwood 	 */
70483ac08c0SLiam Girdwood 	if (source < 8192000) {
70583ac08c0SLiam Girdwood 		pll_div->lf = 1;
70683ac08c0SLiam Girdwood 		target >>= 2;
70783ac08c0SLiam Girdwood 	} else
70883ac08c0SLiam Girdwood 		pll_div->lf = 0;
70983ac08c0SLiam Girdwood 
71083ac08c0SLiam Girdwood 	Ndiv = target / source;
71183ac08c0SLiam Girdwood 	if ((Ndiv < 5) || (Ndiv > 12))
71283ac08c0SLiam Girdwood 		printk(KERN_WARNING
71383ac08c0SLiam Girdwood 			"WM9713 PLL N value %d out of recommended range!\n",
71483ac08c0SLiam Girdwood 			Ndiv);
71583ac08c0SLiam Girdwood 
71683ac08c0SLiam Girdwood 	pll_div->n = Ndiv;
71783ac08c0SLiam Girdwood 	Nmod = target % source;
71883ac08c0SLiam Girdwood 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
71983ac08c0SLiam Girdwood 
72083ac08c0SLiam Girdwood 	do_div(Kpart, source);
72183ac08c0SLiam Girdwood 
72283ac08c0SLiam Girdwood 	K = Kpart & 0xFFFFFFFF;
72383ac08c0SLiam Girdwood 
72483ac08c0SLiam Girdwood 	/* Check if we need to round */
72583ac08c0SLiam Girdwood 	if ((K % 10) >= 5)
72683ac08c0SLiam Girdwood 		K += 5;
72783ac08c0SLiam Girdwood 
72883ac08c0SLiam Girdwood 	/* Move down to proper range now rounding is done */
72983ac08c0SLiam Girdwood 	K /= 10;
73083ac08c0SLiam Girdwood 
73183ac08c0SLiam Girdwood 	pll_div->k = K;
73283ac08c0SLiam Girdwood }
73383ac08c0SLiam Girdwood 
73483ac08c0SLiam Girdwood /**
73583ac08c0SLiam Girdwood  * Please note that changing the PLL input frequency may require
73683ac08c0SLiam Girdwood  * resynchronisation with the AC97 controller.
73783ac08c0SLiam Girdwood  */
73883ac08c0SLiam Girdwood static int wm9713_set_pll(struct snd_soc_codec *codec,
73983ac08c0SLiam Girdwood 	int pll_id, unsigned int freq_in, unsigned int freq_out)
74083ac08c0SLiam Girdwood {
74183ac08c0SLiam Girdwood 	struct wm9713_priv *wm9713 = codec->private_data;
74283ac08c0SLiam Girdwood 	u16 reg, reg2;
74383ac08c0SLiam Girdwood 	struct _pll_div pll_div;
74483ac08c0SLiam Girdwood 
74583ac08c0SLiam Girdwood 	/* turn PLL off ? */
746c42f69bbSMark Brown 	if (freq_in == 0) {
74783ac08c0SLiam Girdwood 		/* disable PLL power and select ext source */
74883ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_HANDSET_RATE);
74983ac08c0SLiam Girdwood 		ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
75083ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_EXTENDED_MID);
75183ac08c0SLiam Girdwood 		ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
752c42f69bbSMark Brown 		wm9713->pll_in = 0;
75383ac08c0SLiam Girdwood 		return 0;
75483ac08c0SLiam Girdwood 	}
75583ac08c0SLiam Girdwood 
75683ac08c0SLiam Girdwood 	pll_factors(&pll_div, freq_in);
75783ac08c0SLiam Girdwood 
75883ac08c0SLiam Girdwood 	if (pll_div.k == 0) {
75983ac08c0SLiam Girdwood 		reg = (pll_div.n << 12) | (pll_div.lf << 11) |
76083ac08c0SLiam Girdwood 			(pll_div.divsel << 9) | (pll_div.divctl << 8);
76183ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg);
76283ac08c0SLiam Girdwood 	} else {
76383ac08c0SLiam Girdwood 		/* write the fractional k to the reg 0x46 pages */
76483ac08c0SLiam Girdwood 		reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) |
76583ac08c0SLiam Girdwood 			(pll_div.divsel << 9) | (pll_div.divctl << 8);
76683ac08c0SLiam Girdwood 
76783ac08c0SLiam Girdwood 		/* K [21:20] */
76883ac08c0SLiam Girdwood 		reg = reg2 | (0x5 << 4) | (pll_div.k >> 20);
76983ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg);
77083ac08c0SLiam Girdwood 
77183ac08c0SLiam Girdwood 		/* K [19:16] */
77283ac08c0SLiam Girdwood 		reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf);
77383ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg);
77483ac08c0SLiam Girdwood 
77583ac08c0SLiam Girdwood 		/* K [15:12] */
77683ac08c0SLiam Girdwood 		reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf);
77783ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg);
77883ac08c0SLiam Girdwood 
77983ac08c0SLiam Girdwood 		/* K [11:8] */
78083ac08c0SLiam Girdwood 		reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf);
78183ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg);
78283ac08c0SLiam Girdwood 
78383ac08c0SLiam Girdwood 		/* K [7:4] */
78483ac08c0SLiam Girdwood 		reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf);
78583ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg);
78683ac08c0SLiam Girdwood 
78783ac08c0SLiam Girdwood 		reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */
78883ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg);
78983ac08c0SLiam Girdwood 	}
79083ac08c0SLiam Girdwood 
79183ac08c0SLiam Girdwood 	/* turn PLL on and select as source */
79283ac08c0SLiam Girdwood 	reg = ac97_read(codec, AC97_EXTENDED_MID);
79383ac08c0SLiam Girdwood 	ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
79483ac08c0SLiam Girdwood 	reg = ac97_read(codec, AC97_HANDSET_RATE);
79583ac08c0SLiam Girdwood 	ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
79683ac08c0SLiam Girdwood 	wm9713->pll_in = freq_in;
79783ac08c0SLiam Girdwood 
79883ac08c0SLiam Girdwood 	/* wait 10ms AC97 link frames for the link to stabilise */
79983ac08c0SLiam Girdwood 	schedule_timeout_interruptible(msecs_to_jiffies(10));
80083ac08c0SLiam Girdwood 	return 0;
80183ac08c0SLiam Girdwood }
80283ac08c0SLiam Girdwood 
803e550e17fSLiam Girdwood static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
80483ac08c0SLiam Girdwood 		int pll_id, unsigned int freq_in, unsigned int freq_out)
80583ac08c0SLiam Girdwood {
80683ac08c0SLiam Girdwood 	struct snd_soc_codec *codec = codec_dai->codec;
80783ac08c0SLiam Girdwood 	return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
80883ac08c0SLiam Girdwood }
80983ac08c0SLiam Girdwood 
81083ac08c0SLiam Girdwood /*
81183ac08c0SLiam Girdwood  * Tristate the PCM DAI lines, tristate can be disabled by calling
81283ac08c0SLiam Girdwood  * wm9713_set_dai_fmt()
81383ac08c0SLiam Girdwood  */
814e550e17fSLiam Girdwood static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai,
81583ac08c0SLiam Girdwood 	int tristate)
81683ac08c0SLiam Girdwood {
81783ac08c0SLiam Girdwood 	struct snd_soc_codec *codec = codec_dai->codec;
81883ac08c0SLiam Girdwood 	u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff;
81983ac08c0SLiam Girdwood 
82083ac08c0SLiam Girdwood 	if (tristate)
82183ac08c0SLiam Girdwood 		ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
82283ac08c0SLiam Girdwood 
82383ac08c0SLiam Girdwood 	return 0;
82483ac08c0SLiam Girdwood }
82583ac08c0SLiam Girdwood 
82683ac08c0SLiam Girdwood /*
82783ac08c0SLiam Girdwood  * Configure WM9713 clock dividers.
82883ac08c0SLiam Girdwood  * Voice DAC needs 256 FS
82983ac08c0SLiam Girdwood  */
830e550e17fSLiam Girdwood static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
83183ac08c0SLiam Girdwood 		int div_id, int div)
83283ac08c0SLiam Girdwood {
83383ac08c0SLiam Girdwood 	struct snd_soc_codec *codec = codec_dai->codec;
83483ac08c0SLiam Girdwood 	u16 reg;
83583ac08c0SLiam Girdwood 
83683ac08c0SLiam Girdwood 	switch (div_id) {
83783ac08c0SLiam Girdwood 	case WM9713_PCMCLK_DIV:
83883ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
83983ac08c0SLiam Girdwood 		ac97_write(codec, AC97_HANDSET_RATE, reg | div);
84083ac08c0SLiam Girdwood 		break;
84183ac08c0SLiam Girdwood 	case WM9713_CLKA_MULT:
84283ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd;
84383ac08c0SLiam Girdwood 		ac97_write(codec, AC97_HANDSET_RATE, reg | div);
84483ac08c0SLiam Girdwood 		break;
84583ac08c0SLiam Girdwood 	case WM9713_CLKB_MULT:
84683ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb;
84783ac08c0SLiam Girdwood 		ac97_write(codec, AC97_HANDSET_RATE, reg | div);
84883ac08c0SLiam Girdwood 		break;
84983ac08c0SLiam Girdwood 	case WM9713_HIFI_DIV:
85083ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff;
85183ac08c0SLiam Girdwood 		ac97_write(codec, AC97_HANDSET_RATE, reg | div);
85283ac08c0SLiam Girdwood 		break;
85383ac08c0SLiam Girdwood 	case WM9713_PCMBCLK_DIV:
85483ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff;
85583ac08c0SLiam Girdwood 		ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div);
85683ac08c0SLiam Girdwood 		break;
85783ac08c0SLiam Girdwood 	case WM9713_PCMCLK_PLL_DIV:
85883ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
85983ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div);
86083ac08c0SLiam Girdwood 		break;
86183ac08c0SLiam Girdwood 	case WM9713_HIFI_PLL_DIV:
86283ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80;
86383ac08c0SLiam Girdwood 		ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div);
86483ac08c0SLiam Girdwood 		break;
86583ac08c0SLiam Girdwood 	default:
86683ac08c0SLiam Girdwood 		return -EINVAL;
86783ac08c0SLiam Girdwood 	}
86883ac08c0SLiam Girdwood 
86983ac08c0SLiam Girdwood 	return 0;
87083ac08c0SLiam Girdwood }
87183ac08c0SLiam Girdwood 
872e550e17fSLiam Girdwood static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
87383ac08c0SLiam Girdwood 		unsigned int fmt)
87483ac08c0SLiam Girdwood {
87583ac08c0SLiam Girdwood 	struct snd_soc_codec *codec = codec_dai->codec;
87683ac08c0SLiam Girdwood 	u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5;
87783ac08c0SLiam Girdwood 	u16 reg = 0x8000;
87883ac08c0SLiam Girdwood 
87983ac08c0SLiam Girdwood 	/* clock masters */
88083ac08c0SLiam Girdwood 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
88183ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_CBM_CFM:
88283ac08c0SLiam Girdwood 		reg |= 0x4000;
88383ac08c0SLiam Girdwood 		gpio |= 0x0010;
88483ac08c0SLiam Girdwood 		break;
88583ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_CBM_CFS:
88683ac08c0SLiam Girdwood 		reg |= 0x6000;
88783ac08c0SLiam Girdwood 		gpio |= 0x0018;
88883ac08c0SLiam Girdwood 		break;
88983ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_CBS_CFS:
890d133b0ceSMark Brown 		reg |= 0x2000;
89183ac08c0SLiam Girdwood 		gpio |= 0x001a;
89283ac08c0SLiam Girdwood 		break;
89383ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_CBS_CFM:
89483ac08c0SLiam Girdwood 		gpio |= 0x0012;
89583ac08c0SLiam Girdwood 		break;
89683ac08c0SLiam Girdwood 	}
89783ac08c0SLiam Girdwood 
89883ac08c0SLiam Girdwood 	/* clock inversion */
89983ac08c0SLiam Girdwood 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
90083ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_IB_IF:
90183ac08c0SLiam Girdwood 		reg |= 0x00c0;
90283ac08c0SLiam Girdwood 		break;
90383ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_IB_NF:
90483ac08c0SLiam Girdwood 		reg |= 0x0080;
90583ac08c0SLiam Girdwood 		break;
90683ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_NB_IF:
90783ac08c0SLiam Girdwood 		reg |= 0x0040;
90883ac08c0SLiam Girdwood 		break;
90983ac08c0SLiam Girdwood 	}
91083ac08c0SLiam Girdwood 
91183ac08c0SLiam Girdwood 	/* DAI format */
91283ac08c0SLiam Girdwood 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
91383ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_I2S:
91483ac08c0SLiam Girdwood 		reg |= 0x0002;
91583ac08c0SLiam Girdwood 		break;
91683ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_RIGHT_J:
91783ac08c0SLiam Girdwood 		break;
91883ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_LEFT_J:
91983ac08c0SLiam Girdwood 		reg |= 0x0001;
92083ac08c0SLiam Girdwood 		break;
92183ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_DSP_A:
92283ac08c0SLiam Girdwood 		reg |= 0x0003;
92383ac08c0SLiam Girdwood 		break;
92483ac08c0SLiam Girdwood 	case SND_SOC_DAIFMT_DSP_B:
92583ac08c0SLiam Girdwood 		reg |= 0x0043;
92683ac08c0SLiam Girdwood 		break;
92783ac08c0SLiam Girdwood 	}
92883ac08c0SLiam Girdwood 
92983ac08c0SLiam Girdwood 	ac97_write(codec, AC97_GPIO_CFG, gpio);
93083ac08c0SLiam Girdwood 	ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
93183ac08c0SLiam Girdwood 	return 0;
93283ac08c0SLiam Girdwood }
93383ac08c0SLiam Girdwood 
93483ac08c0SLiam Girdwood static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
935dee89c4dSMark Brown 				struct snd_pcm_hw_params *params,
936dee89c4dSMark Brown 				struct snd_soc_dai *dai)
93783ac08c0SLiam Girdwood {
93855b8bac5SMark Brown 	struct snd_soc_codec *codec = dai->codec;
93983ac08c0SLiam Girdwood 	u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
94083ac08c0SLiam Girdwood 
94183ac08c0SLiam Girdwood 	switch (params_format(params)) {
94283ac08c0SLiam Girdwood 	case SNDRV_PCM_FORMAT_S16_LE:
94383ac08c0SLiam Girdwood 		break;
94483ac08c0SLiam Girdwood 	case SNDRV_PCM_FORMAT_S20_3LE:
94583ac08c0SLiam Girdwood 		reg |= 0x0004;
94683ac08c0SLiam Girdwood 		break;
94783ac08c0SLiam Girdwood 	case SNDRV_PCM_FORMAT_S24_LE:
94883ac08c0SLiam Girdwood 		reg |= 0x0008;
94983ac08c0SLiam Girdwood 		break;
95083ac08c0SLiam Girdwood 	case SNDRV_PCM_FORMAT_S32_LE:
95183ac08c0SLiam Girdwood 		reg |= 0x000c;
95283ac08c0SLiam Girdwood 		break;
95383ac08c0SLiam Girdwood 	}
95483ac08c0SLiam Girdwood 
95583ac08c0SLiam Girdwood 	/* enable PCM interface in master mode */
95683ac08c0SLiam Girdwood 	ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
95783ac08c0SLiam Girdwood 	return 0;
95883ac08c0SLiam Girdwood }
95983ac08c0SLiam Girdwood 
960dee89c4dSMark Brown static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
961dee89c4dSMark Brown 			     struct snd_soc_dai *dai)
96283ac08c0SLiam Girdwood {
96355b8bac5SMark Brown 	struct snd_soc_codec *codec = dai->codec;
96483ac08c0SLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
96583ac08c0SLiam Girdwood 	int reg;
96683ac08c0SLiam Girdwood 	u16 vra;
96783ac08c0SLiam Girdwood 
96883ac08c0SLiam Girdwood 	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
96983ac08c0SLiam Girdwood 	ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
97083ac08c0SLiam Girdwood 
97183ac08c0SLiam Girdwood 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
97283ac08c0SLiam Girdwood 		reg = AC97_PCM_FRONT_DAC_RATE;
97383ac08c0SLiam Girdwood 	else
97483ac08c0SLiam Girdwood 		reg = AC97_PCM_LR_ADC_RATE;
97583ac08c0SLiam Girdwood 
97683ac08c0SLiam Girdwood 	return ac97_write(codec, reg, runtime->rate);
97783ac08c0SLiam Girdwood }
97883ac08c0SLiam Girdwood 
979dee89c4dSMark Brown static int ac97_aux_prepare(struct snd_pcm_substream *substream,
980dee89c4dSMark Brown 			    struct snd_soc_dai *dai)
98183ac08c0SLiam Girdwood {
98255b8bac5SMark Brown 	struct snd_soc_codec *codec = dai->codec;
98383ac08c0SLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
98483ac08c0SLiam Girdwood 	u16 vra, xsle;
98583ac08c0SLiam Girdwood 
98683ac08c0SLiam Girdwood 	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
98783ac08c0SLiam Girdwood 	ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
98883ac08c0SLiam Girdwood 	xsle = ac97_read(codec, AC97_PCI_SID);
98983ac08c0SLiam Girdwood 	ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
99083ac08c0SLiam Girdwood 
99183ac08c0SLiam Girdwood 	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
99283ac08c0SLiam Girdwood 		return -ENODEV;
99383ac08c0SLiam Girdwood 
99483ac08c0SLiam Girdwood 	return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
99583ac08c0SLiam Girdwood }
99683ac08c0SLiam Girdwood 
9975415552dSMark Brown #define WM9713_RATES (SNDRV_PCM_RATE_8000  |	\
9985415552dSMark Brown 		      SNDRV_PCM_RATE_11025 |	\
9995415552dSMark Brown 		      SNDRV_PCM_RATE_22050 |	\
10005415552dSMark Brown 		      SNDRV_PCM_RATE_44100 |	\
10015415552dSMark Brown 		      SNDRV_PCM_RATE_48000)
10025415552dSMark Brown 
10035415552dSMark Brown #define WM9713_PCM_RATES (SNDRV_PCM_RATE_8000  |	\
10045415552dSMark Brown 			  SNDRV_PCM_RATE_11025 |	\
10055415552dSMark Brown 			  SNDRV_PCM_RATE_16000 |	\
10065415552dSMark Brown 			  SNDRV_PCM_RATE_22050 |	\
10075415552dSMark Brown 			  SNDRV_PCM_RATE_44100 |	\
100883ac08c0SLiam Girdwood 			  SNDRV_PCM_RATE_48000)
100983ac08c0SLiam Girdwood 
101083ac08c0SLiam Girdwood #define WM9713_PCM_FORMATS \
101183ac08c0SLiam Girdwood 	(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
101283ac08c0SLiam Girdwood 	 SNDRV_PCM_FORMAT_S24_LE)
101383ac08c0SLiam Girdwood 
10146335d055SEric Miao static struct snd_soc_dai_ops wm9713_dai_ops_hifi = {
10156335d055SEric Miao 	.prepare	= ac97_hifi_prepare,
10166335d055SEric Miao 	.set_clkdiv	= wm9713_set_dai_clkdiv,
10176335d055SEric Miao 	.set_pll	= wm9713_set_dai_pll,
10186335d055SEric Miao };
10196335d055SEric Miao 
10206335d055SEric Miao static struct snd_soc_dai_ops wm9713_dai_ops_aux = {
10216335d055SEric Miao 	.prepare	= ac97_aux_prepare,
10226335d055SEric Miao 	.set_clkdiv	= wm9713_set_dai_clkdiv,
10236335d055SEric Miao 	.set_pll	= wm9713_set_dai_pll,
10246335d055SEric Miao };
10256335d055SEric Miao 
10266335d055SEric Miao static struct snd_soc_dai_ops wm9713_dai_ops_voice = {
10276335d055SEric Miao 	.hw_params	= wm9713_pcm_hw_params,
10286335d055SEric Miao 	.set_clkdiv	= wm9713_set_dai_clkdiv,
10296335d055SEric Miao 	.set_pll	= wm9713_set_dai_pll,
10306335d055SEric Miao 	.set_fmt	= wm9713_set_dai_fmt,
10316335d055SEric Miao 	.set_tristate	= wm9713_set_dai_tristate,
10326335d055SEric Miao };
10336335d055SEric Miao 
1034e550e17fSLiam Girdwood struct snd_soc_dai wm9713_dai[] = {
103583ac08c0SLiam Girdwood {
103683ac08c0SLiam Girdwood 	.name = "AC97 HiFi",
10373ba9e10aSMark Brown 	.ac97_control = 1,
103883ac08c0SLiam Girdwood 	.playback = {
103983ac08c0SLiam Girdwood 		.stream_name = "HiFi Playback",
104083ac08c0SLiam Girdwood 		.channels_min = 1,
104183ac08c0SLiam Girdwood 		.channels_max = 2,
104283ac08c0SLiam Girdwood 		.rates = WM9713_RATES,
104383ac08c0SLiam Girdwood 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
104483ac08c0SLiam Girdwood 	.capture = {
104583ac08c0SLiam Girdwood 		.stream_name = "HiFi Capture",
104683ac08c0SLiam Girdwood 		.channels_min = 1,
104783ac08c0SLiam Girdwood 		.channels_max = 2,
104883ac08c0SLiam Girdwood 		.rates = WM9713_RATES,
104983ac08c0SLiam Girdwood 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
10506335d055SEric Miao 	.ops = &wm9713_dai_ops_hifi,
105183ac08c0SLiam Girdwood 	},
105283ac08c0SLiam Girdwood 	{
105383ac08c0SLiam Girdwood 	.name = "AC97 Aux",
105483ac08c0SLiam Girdwood 	.playback = {
105583ac08c0SLiam Girdwood 		.stream_name = "Aux Playback",
105683ac08c0SLiam Girdwood 		.channels_min = 1,
105783ac08c0SLiam Girdwood 		.channels_max = 1,
105883ac08c0SLiam Girdwood 		.rates = WM9713_RATES,
105983ac08c0SLiam Girdwood 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
10606335d055SEric Miao 	.ops = &wm9713_dai_ops_aux,
106183ac08c0SLiam Girdwood 	},
106283ac08c0SLiam Girdwood 	{
106383ac08c0SLiam Girdwood 	.name = "WM9713 Voice",
106483ac08c0SLiam Girdwood 	.playback = {
106583ac08c0SLiam Girdwood 		.stream_name = "Voice Playback",
106683ac08c0SLiam Girdwood 		.channels_min = 1,
106783ac08c0SLiam Girdwood 		.channels_max = 1,
10685415552dSMark Brown 		.rates = WM9713_PCM_RATES,
106983ac08c0SLiam Girdwood 		.formats = WM9713_PCM_FORMATS,},
107083ac08c0SLiam Girdwood 	.capture = {
107183ac08c0SLiam Girdwood 		.stream_name = "Voice Capture",
107283ac08c0SLiam Girdwood 		.channels_min = 1,
107383ac08c0SLiam Girdwood 		.channels_max = 2,
10745415552dSMark Brown 		.rates = WM9713_PCM_RATES,
107583ac08c0SLiam Girdwood 		.formats = WM9713_PCM_FORMATS,},
10766335d055SEric Miao 	.ops = &wm9713_dai_ops_voice,
1077f4976116SMark Brown 	.symmetric_rates = 1,
107883ac08c0SLiam Girdwood 	},
107983ac08c0SLiam Girdwood };
108083ac08c0SLiam Girdwood EXPORT_SYMBOL_GPL(wm9713_dai);
108183ac08c0SLiam Girdwood 
108283ac08c0SLiam Girdwood int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
108383ac08c0SLiam Girdwood {
108483ac08c0SLiam Girdwood 	if (try_warm && soc_ac97_ops.warm_reset) {
108583ac08c0SLiam Girdwood 		soc_ac97_ops.warm_reset(codec->ac97);
1086abb68c26SMark Brown 		if (ac97_read(codec, 0) == wm9713_reg[0])
108783ac08c0SLiam Girdwood 			return 1;
108883ac08c0SLiam Girdwood 	}
108983ac08c0SLiam Girdwood 
109083ac08c0SLiam Girdwood 	soc_ac97_ops.reset(codec->ac97);
1091e775f6c0SMark Brown 	if (soc_ac97_ops.warm_reset)
1092e775f6c0SMark Brown 		soc_ac97_ops.warm_reset(codec->ac97);
1093abb68c26SMark Brown 	if (ac97_read(codec, 0) != wm9713_reg[0])
109483ac08c0SLiam Girdwood 		return -EIO;
109583ac08c0SLiam Girdwood 	return 0;
109683ac08c0SLiam Girdwood }
109783ac08c0SLiam Girdwood EXPORT_SYMBOL_GPL(wm9713_reset);
109883ac08c0SLiam Girdwood 
10990be9898aSMark Brown static int wm9713_set_bias_level(struct snd_soc_codec *codec,
11000be9898aSMark Brown 				 enum snd_soc_bias_level level)
110183ac08c0SLiam Girdwood {
110283ac08c0SLiam Girdwood 	u16 reg;
110383ac08c0SLiam Girdwood 
11040be9898aSMark Brown 	switch (level) {
11050be9898aSMark Brown 	case SND_SOC_BIAS_ON:
110683ac08c0SLiam Girdwood 		/* enable thermal shutdown */
110783ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
110883ac08c0SLiam Girdwood 		ac97_write(codec, AC97_EXTENDED_MID, reg);
110983ac08c0SLiam Girdwood 		break;
11100be9898aSMark Brown 	case SND_SOC_BIAS_PREPARE:
111183ac08c0SLiam Girdwood 		break;
11120be9898aSMark Brown 	case SND_SOC_BIAS_STANDBY:
111383ac08c0SLiam Girdwood 		/* enable master bias and vmid */
111483ac08c0SLiam Girdwood 		reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
111583ac08c0SLiam Girdwood 		ac97_write(codec, AC97_EXTENDED_MID, reg);
111683ac08c0SLiam Girdwood 		ac97_write(codec, AC97_POWERDOWN, 0x0000);
111783ac08c0SLiam Girdwood 		break;
11180be9898aSMark Brown 	case SND_SOC_BIAS_OFF:
111983ac08c0SLiam Girdwood 		/* disable everything including AC link */
112083ac08c0SLiam Girdwood 		ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
112183ac08c0SLiam Girdwood 		ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
112283ac08c0SLiam Girdwood 		ac97_write(codec, AC97_POWERDOWN, 0xffff);
112383ac08c0SLiam Girdwood 		break;
112483ac08c0SLiam Girdwood 	}
11250be9898aSMark Brown 	codec->bias_level = level;
112683ac08c0SLiam Girdwood 	return 0;
112783ac08c0SLiam Girdwood }
112883ac08c0SLiam Girdwood 
112983ac08c0SLiam Girdwood static int wm9713_soc_suspend(struct platform_device *pdev,
113083ac08c0SLiam Girdwood 	pm_message_t state)
113183ac08c0SLiam Girdwood {
113283ac08c0SLiam Girdwood 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
11336627a653SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
113487b57fe2SMark Brown 	u16 reg;
113583ac08c0SLiam Girdwood 
113687b57fe2SMark Brown 	/* Disable everything except touchpanel - that will be handled
113787b57fe2SMark Brown 	 * by the touch driver and left disabled if touch is not in
113887b57fe2SMark Brown 	 * use. */
113987b57fe2SMark Brown 	reg = ac97_read(codec, AC97_EXTENDED_MID);
114087b57fe2SMark Brown 	ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff);
114187b57fe2SMark Brown 	ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
114287b57fe2SMark Brown 	ac97_write(codec, AC97_POWERDOWN, 0x6f00);
114387b57fe2SMark Brown 	ac97_write(codec, AC97_POWERDOWN, 0xffff);
114487b57fe2SMark Brown 
114583ac08c0SLiam Girdwood 	return 0;
114683ac08c0SLiam Girdwood }
114783ac08c0SLiam Girdwood 
114883ac08c0SLiam Girdwood static int wm9713_soc_resume(struct platform_device *pdev)
114983ac08c0SLiam Girdwood {
115083ac08c0SLiam Girdwood 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
11516627a653SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
115283ac08c0SLiam Girdwood 	struct wm9713_priv *wm9713 = codec->private_data;
115383ac08c0SLiam Girdwood 	int i, ret;
115483ac08c0SLiam Girdwood 	u16 *cache = codec->reg_cache;
115583ac08c0SLiam Girdwood 
115683ac08c0SLiam Girdwood 	ret = wm9713_reset(codec, 1);
115783ac08c0SLiam Girdwood 	if (ret < 0) {
115883ac08c0SLiam Girdwood 		printk(KERN_ERR "could not reset AC97 codec\n");
115983ac08c0SLiam Girdwood 		return ret;
116083ac08c0SLiam Girdwood 	}
116183ac08c0SLiam Girdwood 
11620be9898aSMark Brown 	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
116383ac08c0SLiam Girdwood 
116483ac08c0SLiam Girdwood 	/* do we need to re-start the PLL ? */
1165c42f69bbSMark Brown 	if (wm9713->pll_in)
1166c42f69bbSMark Brown 		wm9713_set_pll(codec, 0, wm9713->pll_in, 0);
116783ac08c0SLiam Girdwood 
116883ac08c0SLiam Girdwood 	/* only synchronise the codec if warm reset failed */
116983ac08c0SLiam Girdwood 	if (ret == 0) {
117083ac08c0SLiam Girdwood 		for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) {
117183ac08c0SLiam Girdwood 			if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
117283ac08c0SLiam Girdwood 				i == AC97_EXTENDED_MSTATUS || i > 0x66)
117383ac08c0SLiam Girdwood 				continue;
117483ac08c0SLiam Girdwood 			soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
117583ac08c0SLiam Girdwood 		}
117683ac08c0SLiam Girdwood 	}
117783ac08c0SLiam Girdwood 
11780be9898aSMark Brown 	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
11790be9898aSMark Brown 		wm9713_set_bias_level(codec, SND_SOC_BIAS_ON);
118083ac08c0SLiam Girdwood 
118183ac08c0SLiam Girdwood 	return ret;
118283ac08c0SLiam Girdwood }
118383ac08c0SLiam Girdwood 
118483ac08c0SLiam Girdwood static int wm9713_soc_probe(struct platform_device *pdev)
118583ac08c0SLiam Girdwood {
118683ac08c0SLiam Girdwood 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
118783ac08c0SLiam Girdwood 	struct snd_soc_codec *codec;
118883ac08c0SLiam Girdwood 	int ret = 0, reg;
118983ac08c0SLiam Girdwood 
119083ac08c0SLiam Girdwood 	printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
119183ac08c0SLiam Girdwood 
11926627a653SMark Brown 	socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
11936627a653SMark Brown 				      GFP_KERNEL);
11946627a653SMark Brown 	if (socdev->card->codec == NULL)
119583ac08c0SLiam Girdwood 		return -ENOMEM;
11966627a653SMark Brown 	codec = socdev->card->codec;
119783ac08c0SLiam Girdwood 	mutex_init(&codec->mutex);
119883ac08c0SLiam Girdwood 
119983ac08c0SLiam Girdwood 	codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL);
120083ac08c0SLiam Girdwood 	if (codec->reg_cache == NULL) {
120183ac08c0SLiam Girdwood 		ret = -ENOMEM;
120283ac08c0SLiam Girdwood 		goto cache_err;
120383ac08c0SLiam Girdwood 	}
120483ac08c0SLiam Girdwood 	codec->reg_cache_size = sizeof(wm9713_reg);
120583ac08c0SLiam Girdwood 	codec->reg_cache_step = 2;
120683ac08c0SLiam Girdwood 
120783ac08c0SLiam Girdwood 	codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
120883ac08c0SLiam Girdwood 	if (codec->private_data == NULL) {
120983ac08c0SLiam Girdwood 		ret = -ENOMEM;
121083ac08c0SLiam Girdwood 		goto priv_err;
121183ac08c0SLiam Girdwood 	}
121283ac08c0SLiam Girdwood 
121383ac08c0SLiam Girdwood 	codec->name = "WM9713";
121483ac08c0SLiam Girdwood 	codec->owner = THIS_MODULE;
121583ac08c0SLiam Girdwood 	codec->dai = wm9713_dai;
121683ac08c0SLiam Girdwood 	codec->num_dai = ARRAY_SIZE(wm9713_dai);
121783ac08c0SLiam Girdwood 	codec->write = ac97_write;
121883ac08c0SLiam Girdwood 	codec->read = ac97_read;
12190be9898aSMark Brown 	codec->set_bias_level = wm9713_set_bias_level;
122083ac08c0SLiam Girdwood 	INIT_LIST_HEAD(&codec->dapm_widgets);
122183ac08c0SLiam Girdwood 	INIT_LIST_HEAD(&codec->dapm_paths);
122283ac08c0SLiam Girdwood 
122383ac08c0SLiam Girdwood 	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
122483ac08c0SLiam Girdwood 	if (ret < 0)
122583ac08c0SLiam Girdwood 		goto codec_err;
122683ac08c0SLiam Girdwood 
122783ac08c0SLiam Girdwood 	/* register pcms */
122883ac08c0SLiam Girdwood 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
122983ac08c0SLiam Girdwood 	if (ret < 0)
123083ac08c0SLiam Girdwood 		goto pcm_err;
123183ac08c0SLiam Girdwood 
123283ac08c0SLiam Girdwood 	/* do a cold reset for the controller and then try
123383ac08c0SLiam Girdwood 	 * a warm reset followed by an optional cold reset for codec */
123483ac08c0SLiam Girdwood 	wm9713_reset(codec, 0);
123583ac08c0SLiam Girdwood 	ret = wm9713_reset(codec, 1);
123683ac08c0SLiam Girdwood 	if (ret < 0) {
123739639fabSMark Brown 		printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
123883ac08c0SLiam Girdwood 		goto reset_err;
123983ac08c0SLiam Girdwood 	}
124083ac08c0SLiam Girdwood 
12410be9898aSMark Brown 	wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
124283ac08c0SLiam Girdwood 
124383ac08c0SLiam Girdwood 	/* unmute the adc - move to kcontrol */
124483ac08c0SLiam Girdwood 	reg = ac97_read(codec, AC97_CD) & 0x7fff;
124583ac08c0SLiam Girdwood 	ac97_write(codec, AC97_CD, reg);
124683ac08c0SLiam Girdwood 
12473e8e1952SIan Molton 	snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
12483e8e1952SIan Molton 				ARRAY_SIZE(wm9713_snd_ac97_controls));
124983ac08c0SLiam Girdwood 	wm9713_add_widgets(codec);
1250968a6025SMark Brown 	ret = snd_soc_init_card(socdev);
125183ac08c0SLiam Girdwood 	if (ret < 0)
125283ac08c0SLiam Girdwood 		goto reset_err;
125383ac08c0SLiam Girdwood 	return 0;
125483ac08c0SLiam Girdwood 
125583ac08c0SLiam Girdwood reset_err:
125683ac08c0SLiam Girdwood 	snd_soc_free_pcms(socdev);
125783ac08c0SLiam Girdwood 
125883ac08c0SLiam Girdwood pcm_err:
125983ac08c0SLiam Girdwood 	snd_soc_free_ac97_codec(codec);
126083ac08c0SLiam Girdwood 
126183ac08c0SLiam Girdwood codec_err:
126283ac08c0SLiam Girdwood 	kfree(codec->private_data);
126383ac08c0SLiam Girdwood 
126483ac08c0SLiam Girdwood priv_err:
126583ac08c0SLiam Girdwood 	kfree(codec->reg_cache);
126683ac08c0SLiam Girdwood 
126783ac08c0SLiam Girdwood cache_err:
12686627a653SMark Brown 	kfree(socdev->card->codec);
12696627a653SMark Brown 	socdev->card->codec = NULL;
127083ac08c0SLiam Girdwood 	return ret;
127183ac08c0SLiam Girdwood }
127283ac08c0SLiam Girdwood 
127383ac08c0SLiam Girdwood static int wm9713_soc_remove(struct platform_device *pdev)
127483ac08c0SLiam Girdwood {
127583ac08c0SLiam Girdwood 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
12766627a653SMark Brown 	struct snd_soc_codec *codec = socdev->card->codec;
127783ac08c0SLiam Girdwood 
127883ac08c0SLiam Girdwood 	if (codec == NULL)
127983ac08c0SLiam Girdwood 		return 0;
128083ac08c0SLiam Girdwood 
128183ac08c0SLiam Girdwood 	snd_soc_dapm_free(socdev);
128283ac08c0SLiam Girdwood 	snd_soc_free_pcms(socdev);
128383ac08c0SLiam Girdwood 	snd_soc_free_ac97_codec(codec);
128483ac08c0SLiam Girdwood 	kfree(codec->private_data);
128583ac08c0SLiam Girdwood 	kfree(codec->reg_cache);
128683ac08c0SLiam Girdwood 	kfree(codec);
128783ac08c0SLiam Girdwood 	return 0;
128883ac08c0SLiam Girdwood }
128983ac08c0SLiam Girdwood 
129083ac08c0SLiam Girdwood struct snd_soc_codec_device soc_codec_dev_wm9713 = {
129183ac08c0SLiam Girdwood 	.probe = 	wm9713_soc_probe,
129283ac08c0SLiam Girdwood 	.remove = 	wm9713_soc_remove,
129383ac08c0SLiam Girdwood 	.suspend =	wm9713_soc_suspend,
129483ac08c0SLiam Girdwood 	.resume = 	wm9713_soc_resume,
129583ac08c0SLiam Girdwood };
129683ac08c0SLiam Girdwood EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713);
129783ac08c0SLiam Girdwood 
129883ac08c0SLiam Girdwood MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver");
129983ac08c0SLiam Girdwood MODULE_AUTHOR("Liam Girdwood");
130083ac08c0SLiam Girdwood MODULE_LICENSE("GPL");
1301