1*83ac08c0SLiam Girdwood /* 2*83ac08c0SLiam Girdwood * wm9713.c -- ALSA Soc WM9713 codec support 3*83ac08c0SLiam Girdwood * 4*83ac08c0SLiam Girdwood * Copyright 2006 Wolfson Microelectronics PLC. 5*83ac08c0SLiam Girdwood * Author: Liam Girdwood 6*83ac08c0SLiam Girdwood * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com 7*83ac08c0SLiam Girdwood * 8*83ac08c0SLiam Girdwood * This program is free software; you can redistribute it and/or modify it 9*83ac08c0SLiam Girdwood * under the terms of the GNU General Public License as published by the 10*83ac08c0SLiam Girdwood * Free Software Foundation; either version 2 of the License, or (at your 11*83ac08c0SLiam Girdwood * option) any later version. 12*83ac08c0SLiam Girdwood * 13*83ac08c0SLiam Girdwood * Revision history 14*83ac08c0SLiam Girdwood * 4th Feb 2006 Initial version. 15*83ac08c0SLiam Girdwood * 16*83ac08c0SLiam Girdwood * Features:- 17*83ac08c0SLiam Girdwood * 18*83ac08c0SLiam Girdwood * o Support for AC97 Codec, Voice DAC and Aux DAC 19*83ac08c0SLiam Girdwood * o Support for DAPM 20*83ac08c0SLiam Girdwood */ 21*83ac08c0SLiam Girdwood 22*83ac08c0SLiam Girdwood #include <linux/init.h> 23*83ac08c0SLiam Girdwood #include <linux/module.h> 24*83ac08c0SLiam Girdwood #include <linux/device.h> 25*83ac08c0SLiam Girdwood #include <sound/core.h> 26*83ac08c0SLiam Girdwood #include <sound/pcm.h> 27*83ac08c0SLiam Girdwood #include <sound/ac97_codec.h> 28*83ac08c0SLiam Girdwood #include <sound/initval.h> 29*83ac08c0SLiam Girdwood #include <sound/pcm_params.h> 30*83ac08c0SLiam Girdwood #include <sound/soc.h> 31*83ac08c0SLiam Girdwood #include <sound/soc-dapm.h> 32*83ac08c0SLiam Girdwood 33*83ac08c0SLiam Girdwood #include "wm9713.h" 34*83ac08c0SLiam Girdwood 35*83ac08c0SLiam Girdwood #define WM9713_VERSION "0.15" 36*83ac08c0SLiam Girdwood 37*83ac08c0SLiam Girdwood struct wm9713_priv { 38*83ac08c0SLiam Girdwood u32 pll_in; /* PLL input frequency */ 39*83ac08c0SLiam Girdwood u32 pll_out; /* PLL output frequency */ 40*83ac08c0SLiam Girdwood }; 41*83ac08c0SLiam Girdwood 42*83ac08c0SLiam Girdwood static unsigned int ac97_read(struct snd_soc_codec *codec, 43*83ac08c0SLiam Girdwood unsigned int reg); 44*83ac08c0SLiam Girdwood static int ac97_write(struct snd_soc_codec *codec, 45*83ac08c0SLiam Girdwood unsigned int reg, unsigned int val); 46*83ac08c0SLiam Girdwood 47*83ac08c0SLiam Girdwood /* 48*83ac08c0SLiam Girdwood * WM9713 register cache 49*83ac08c0SLiam Girdwood * Reg 0x3c bit 15 is used by touch driver. 50*83ac08c0SLiam Girdwood */ 51*83ac08c0SLiam Girdwood static const u16 wm9713_reg[] = { 52*83ac08c0SLiam Girdwood 0x6174, 0x8080, 0x8080, 0x8080, 53*83ac08c0SLiam Girdwood 0xc880, 0xe808, 0xe808, 0x0808, 54*83ac08c0SLiam Girdwood 0x00da, 0x8000, 0xd600, 0xaaa0, 55*83ac08c0SLiam Girdwood 0xaaa0, 0xaaa0, 0x0000, 0x0000, 56*83ac08c0SLiam Girdwood 0x0f0f, 0x0040, 0x0000, 0x7f00, 57*83ac08c0SLiam Girdwood 0x0405, 0x0410, 0xbb80, 0xbb80, 58*83ac08c0SLiam Girdwood 0x0000, 0xbb80, 0x0000, 0x4523, 59*83ac08c0SLiam Girdwood 0x0000, 0x2000, 0x7eff, 0xffff, 60*83ac08c0SLiam Girdwood 0x0000, 0x0000, 0x0080, 0x0000, 61*83ac08c0SLiam Girdwood 0x0000, 0x0000, 0xfffe, 0xffff, 62*83ac08c0SLiam Girdwood 0x0000, 0x0000, 0x0000, 0xfffe, 63*83ac08c0SLiam Girdwood 0x4000, 0x0000, 0x0000, 0x0000, 64*83ac08c0SLiam Girdwood 0xb032, 0x3e00, 0x0000, 0x0000, 65*83ac08c0SLiam Girdwood 0x0000, 0x0000, 0x0000, 0x0000, 66*83ac08c0SLiam Girdwood 0x0000, 0x0000, 0x0000, 0x0006, 67*83ac08c0SLiam Girdwood 0x0001, 0x0000, 0x574d, 0x4c13, 68*83ac08c0SLiam Girdwood 0x0000, 0x0000, 0x0000 69*83ac08c0SLiam Girdwood }; 70*83ac08c0SLiam Girdwood 71*83ac08c0SLiam Girdwood /* virtual HP mixers regs */ 72*83ac08c0SLiam Girdwood #define HPL_MIXER 0x80 73*83ac08c0SLiam Girdwood #define HPR_MIXER 0x82 74*83ac08c0SLiam Girdwood #define MICB_MUX 0x82 75*83ac08c0SLiam Girdwood 76*83ac08c0SLiam Girdwood static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; 77*83ac08c0SLiam Girdwood static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; 78*83ac08c0SLiam Girdwood static const char *wm9713_rec_src[] = 79*83ac08c0SLiam Girdwood {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker", 80*83ac08c0SLiam Girdwood "Mono Out", "Zh"}; 81*83ac08c0SLiam Girdwood static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; 82*83ac08c0SLiam Girdwood static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"}; 83*83ac08c0SLiam Girdwood static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv", 84*83ac08c0SLiam Girdwood "Mono Vmid", "Inv Vmid"}; 85*83ac08c0SLiam Girdwood static const char *wm9713_spk_pga[] = 86*83ac08c0SLiam Girdwood {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid", 87*83ac08c0SLiam Girdwood "Speaker Vmid", "Inv Vmid"}; 88*83ac08c0SLiam Girdwood static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone", 89*83ac08c0SLiam Girdwood "Headphone Vmid"}; 90*83ac08c0SLiam Girdwood static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"}; 91*83ac08c0SLiam Girdwood static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"}; 92*83ac08c0SLiam Girdwood static const char *wm9713_dac_inv[] = 93*83ac08c0SLiam Girdwood {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone", 94*83ac08c0SLiam Girdwood "Headphone Mono", "NC", "Vmid"}; 95*83ac08c0SLiam Girdwood static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"}; 96*83ac08c0SLiam Girdwood static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"}; 97*83ac08c0SLiam Girdwood static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"}; 98*83ac08c0SLiam Girdwood static const char *wm9713_micb_select[] = {"MPB", "MPA"}; 99*83ac08c0SLiam Girdwood 100*83ac08c0SLiam Girdwood static const struct soc_enum wm9713_enum[] = { 101*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */ 102*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */ 103*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */ 104*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */ 105*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/ 106*83ac08c0SLiam Girdwood SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */ 107*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/ 108*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */ 109*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */ 110*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */ 111*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */ 112*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */ 113*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */ 114*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */ 115*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */ 116*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ 117*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ 118*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ 119*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ 120*83ac08c0SLiam Girdwood SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */ 121*83ac08c0SLiam Girdwood }; 122*83ac08c0SLiam Girdwood 123*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = { 124*83ac08c0SLiam Girdwood SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1), 125*83ac08c0SLiam Girdwood SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1), 126*83ac08c0SLiam Girdwood SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), 127*83ac08c0SLiam Girdwood SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1), 128*83ac08c0SLiam Girdwood SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1), 129*83ac08c0SLiam Girdwood SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1), 130*83ac08c0SLiam Girdwood SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1), 131*83ac08c0SLiam Girdwood SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1), 132*83ac08c0SLiam Girdwood 133*83ac08c0SLiam Girdwood SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), 134*83ac08c0SLiam Girdwood SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), 135*83ac08c0SLiam Girdwood 136*83ac08c0SLiam Girdwood SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1), 137*83ac08c0SLiam Girdwood SOC_ENUM("Capture Volume Steps", wm9713_enum[5]), 138*83ac08c0SLiam Girdwood SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0), 139*83ac08c0SLiam Girdwood SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), 140*83ac08c0SLiam Girdwood 141*83ac08c0SLiam Girdwood SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1), 142*83ac08c0SLiam Girdwood SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), 143*83ac08c0SLiam Girdwood SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), 144*83ac08c0SLiam Girdwood 145*83ac08c0SLiam Girdwood SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), 146*83ac08c0SLiam Girdwood SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), 147*83ac08c0SLiam Girdwood SOC_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0), 148*83ac08c0SLiam Girdwood SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), 149*83ac08c0SLiam Girdwood SOC_ENUM("ALC Function", wm9713_enum[6]), 150*83ac08c0SLiam Girdwood SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), 151*83ac08c0SLiam Girdwood SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0), 152*83ac08c0SLiam Girdwood SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), 153*83ac08c0SLiam Girdwood SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), 154*83ac08c0SLiam Girdwood SOC_ENUM("ALC NG Type", wm9713_enum[17]), 155*83ac08c0SLiam Girdwood SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0), 156*83ac08c0SLiam Girdwood 157*83ac08c0SLiam Girdwood SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0), 158*83ac08c0SLiam Girdwood SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), 159*83ac08c0SLiam Girdwood 160*83ac08c0SLiam Girdwood SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1), 161*83ac08c0SLiam Girdwood SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0), 162*83ac08c0SLiam Girdwood SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1), 163*83ac08c0SLiam Girdwood 164*83ac08c0SLiam Girdwood SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1), 165*83ac08c0SLiam Girdwood SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0), 166*83ac08c0SLiam Girdwood SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1), 167*83ac08c0SLiam Girdwood 168*83ac08c0SLiam Girdwood SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1), 169*83ac08c0SLiam Girdwood SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1), 170*83ac08c0SLiam Girdwood SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0), 171*83ac08c0SLiam Girdwood SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1), 172*83ac08c0SLiam Girdwood 173*83ac08c0SLiam Girdwood SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1), 174*83ac08c0SLiam Girdwood SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1), 175*83ac08c0SLiam Girdwood SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1), 176*83ac08c0SLiam Girdwood 177*83ac08c0SLiam Girdwood SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1), 178*83ac08c0SLiam Girdwood SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1), 179*83ac08c0SLiam Girdwood SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1), 180*83ac08c0SLiam Girdwood 181*83ac08c0SLiam Girdwood SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1), 182*83ac08c0SLiam Girdwood SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1), 183*83ac08c0SLiam Girdwood SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1), 184*83ac08c0SLiam Girdwood 185*83ac08c0SLiam Girdwood SOC_ENUM("Bass Control", wm9713_enum[16]), 186*83ac08c0SLiam Girdwood SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), 187*83ac08c0SLiam Girdwood SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1), 188*83ac08c0SLiam Girdwood SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0), 189*83ac08c0SLiam Girdwood SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1), 190*83ac08c0SLiam Girdwood SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1), 191*83ac08c0SLiam Girdwood 192*83ac08c0SLiam Girdwood SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0), 193*83ac08c0SLiam Girdwood SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0), 194*83ac08c0SLiam Girdwood SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), 195*83ac08c0SLiam Girdwood }; 196*83ac08c0SLiam Girdwood 197*83ac08c0SLiam Girdwood /* add non dapm controls */ 198*83ac08c0SLiam Girdwood static int wm9713_add_controls(struct snd_soc_codec *codec) 199*83ac08c0SLiam Girdwood { 200*83ac08c0SLiam Girdwood int err, i; 201*83ac08c0SLiam Girdwood 202*83ac08c0SLiam Girdwood for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) { 203*83ac08c0SLiam Girdwood err = snd_ctl_add(codec->card, 204*83ac08c0SLiam Girdwood snd_soc_cnew(&wm9713_snd_ac97_controls[i], 205*83ac08c0SLiam Girdwood codec, NULL)); 206*83ac08c0SLiam Girdwood if (err < 0) 207*83ac08c0SLiam Girdwood return err; 208*83ac08c0SLiam Girdwood } 209*83ac08c0SLiam Girdwood return 0; 210*83ac08c0SLiam Girdwood } 211*83ac08c0SLiam Girdwood 212*83ac08c0SLiam Girdwood /* We have to create a fake left and right HP mixers because 213*83ac08c0SLiam Girdwood * the codec only has a single control that is shared by both channels. 214*83ac08c0SLiam Girdwood * This makes it impossible to determine the audio path using the current 215*83ac08c0SLiam Girdwood * register map, thus we add a new (virtual) register to help determine the 216*83ac08c0SLiam Girdwood * audio route within the device. 217*83ac08c0SLiam Girdwood */ 218*83ac08c0SLiam Girdwood static int mixer_event(struct snd_soc_dapm_widget *w, 219*83ac08c0SLiam Girdwood struct snd_kcontrol *kcontrol, int event) 220*83ac08c0SLiam Girdwood { 221*83ac08c0SLiam Girdwood u16 l, r, beep, tone, phone, rec, pcm, aux; 222*83ac08c0SLiam Girdwood 223*83ac08c0SLiam Girdwood l = ac97_read(w->codec, HPL_MIXER); 224*83ac08c0SLiam Girdwood r = ac97_read(w->codec, HPR_MIXER); 225*83ac08c0SLiam Girdwood beep = ac97_read(w->codec, AC97_PC_BEEP); 226*83ac08c0SLiam Girdwood tone = ac97_read(w->codec, AC97_MASTER_TONE); 227*83ac08c0SLiam Girdwood phone = ac97_read(w->codec, AC97_PHONE); 228*83ac08c0SLiam Girdwood rec = ac97_read(w->codec, AC97_REC_SEL); 229*83ac08c0SLiam Girdwood pcm = ac97_read(w->codec, AC97_PCM); 230*83ac08c0SLiam Girdwood aux = ac97_read(w->codec, AC97_AUX); 231*83ac08c0SLiam Girdwood 232*83ac08c0SLiam Girdwood if (event & SND_SOC_DAPM_PRE_REG) 233*83ac08c0SLiam Girdwood return 0; 234*83ac08c0SLiam Girdwood if ((l & 0x1) || (r & 0x1)) 235*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); 236*83ac08c0SLiam Girdwood else 237*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); 238*83ac08c0SLiam Girdwood 239*83ac08c0SLiam Girdwood if ((l & 0x2) || (r & 0x2)) 240*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff); 241*83ac08c0SLiam Girdwood else 242*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000); 243*83ac08c0SLiam Girdwood 244*83ac08c0SLiam Girdwood if ((l & 0x4) || (r & 0x4)) 245*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); 246*83ac08c0SLiam Girdwood else 247*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_PHONE, phone | 0x8000); 248*83ac08c0SLiam Girdwood 249*83ac08c0SLiam Girdwood if ((l & 0x8) || (r & 0x8)) 250*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff); 251*83ac08c0SLiam Girdwood else 252*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000); 253*83ac08c0SLiam Girdwood 254*83ac08c0SLiam Girdwood if ((l & 0x10) || (r & 0x10)) 255*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); 256*83ac08c0SLiam Girdwood else 257*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_PCM, pcm | 0x8000); 258*83ac08c0SLiam Girdwood 259*83ac08c0SLiam Girdwood if ((l & 0x20) || (r & 0x20)) 260*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_AUX, aux & 0x7fff); 261*83ac08c0SLiam Girdwood else 262*83ac08c0SLiam Girdwood ac97_write(w->codec, AC97_AUX, aux | 0x8000); 263*83ac08c0SLiam Girdwood 264*83ac08c0SLiam Girdwood return 0; 265*83ac08c0SLiam Girdwood } 266*83ac08c0SLiam Girdwood 267*83ac08c0SLiam Girdwood /* Left Headphone Mixers */ 268*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { 269*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0), 270*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0), 271*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0), 272*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0), 273*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0), 274*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0), 275*83ac08c0SLiam Girdwood }; 276*83ac08c0SLiam Girdwood 277*83ac08c0SLiam Girdwood /* Right Headphone Mixers */ 278*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { 279*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0), 280*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0), 281*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0), 282*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0), 283*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0), 284*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0), 285*83ac08c0SLiam Girdwood }; 286*83ac08c0SLiam Girdwood 287*83ac08c0SLiam Girdwood /* headphone capture mux */ 288*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls = 289*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[1]); 290*83ac08c0SLiam Girdwood 291*83ac08c0SLiam Girdwood /* headphone mic mux */ 292*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls = 293*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[0]); 294*83ac08c0SLiam Girdwood 295*83ac08c0SLiam Girdwood /* Speaker Mixer */ 296*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = { 297*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1), 298*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1), 299*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1), 300*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1), 301*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1), 302*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1), 303*83ac08c0SLiam Girdwood }; 304*83ac08c0SLiam Girdwood 305*83ac08c0SLiam Girdwood /* Mono Mixer */ 306*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = { 307*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1), 308*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1), 309*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1), 310*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1), 311*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1), 312*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1), 313*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1), 314*83ac08c0SLiam Girdwood SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1), 315*83ac08c0SLiam Girdwood }; 316*83ac08c0SLiam Girdwood 317*83ac08c0SLiam Girdwood /* mono mic mux */ 318*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls = 319*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[2]); 320*83ac08c0SLiam Girdwood 321*83ac08c0SLiam Girdwood /* mono output mux */ 322*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mono_mux_controls = 323*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[7]); 324*83ac08c0SLiam Girdwood 325*83ac08c0SLiam Girdwood /* speaker left output mux */ 326*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls = 327*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[8]); 328*83ac08c0SLiam Girdwood 329*83ac08c0SLiam Girdwood /* speaker right output mux */ 330*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls = 331*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[9]); 332*83ac08c0SLiam Girdwood 333*83ac08c0SLiam Girdwood /* headphone left output mux */ 334*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls = 335*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[10]); 336*83ac08c0SLiam Girdwood 337*83ac08c0SLiam Girdwood /* headphone right output mux */ 338*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls = 339*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[11]); 340*83ac08c0SLiam Girdwood 341*83ac08c0SLiam Girdwood /* Out3 mux */ 342*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_out3_mux_controls = 343*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[12]); 344*83ac08c0SLiam Girdwood 345*83ac08c0SLiam Girdwood /* Out4 mux */ 346*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_out4_mux_controls = 347*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[13]); 348*83ac08c0SLiam Girdwood 349*83ac08c0SLiam Girdwood /* DAC inv mux 1 */ 350*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls = 351*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[14]); 352*83ac08c0SLiam Girdwood 353*83ac08c0SLiam Girdwood /* DAC inv mux 2 */ 354*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls = 355*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[15]); 356*83ac08c0SLiam Girdwood 357*83ac08c0SLiam Girdwood /* Capture source left */ 358*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls = 359*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[3]); 360*83ac08c0SLiam Girdwood 361*83ac08c0SLiam Girdwood /* Capture source right */ 362*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls = 363*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[4]); 364*83ac08c0SLiam Girdwood 365*83ac08c0SLiam Girdwood /* mic source */ 366*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls = 367*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[18]); 368*83ac08c0SLiam Girdwood 369*83ac08c0SLiam Girdwood /* mic source B virtual control */ 370*83ac08c0SLiam Girdwood static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls = 371*83ac08c0SLiam Girdwood SOC_DAPM_ENUM("Route", wm9713_enum[19]); 372*83ac08c0SLiam Girdwood 373*83ac08c0SLiam Girdwood static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = { 374*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0, 375*83ac08c0SLiam Girdwood &wm9713_hp_rec_mux_controls), 376*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, 377*83ac08c0SLiam Girdwood &wm9713_hp_mic_mux_controls), 378*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0, 379*83ac08c0SLiam Girdwood &wm9713_mono_mic_mux_controls), 380*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0, 381*83ac08c0SLiam Girdwood &wm9713_mono_mux_controls), 382*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0, 383*83ac08c0SLiam Girdwood &wm9713_hp_spkl_mux_controls), 384*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0, 385*83ac08c0SLiam Girdwood &wm9713_hp_spkr_mux_controls), 386*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0, 387*83ac08c0SLiam Girdwood &wm9713_hpl_out_mux_controls), 388*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0, 389*83ac08c0SLiam Girdwood &wm9713_hpr_out_mux_controls), 390*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0, 391*83ac08c0SLiam Girdwood &wm9713_out3_mux_controls), 392*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0, 393*83ac08c0SLiam Girdwood &wm9713_out4_mux_controls), 394*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0, 395*83ac08c0SLiam Girdwood &wm9713_dac_inv1_mux_controls), 396*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0, 397*83ac08c0SLiam Girdwood &wm9713_dac_inv2_mux_controls), 398*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0, 399*83ac08c0SLiam Girdwood &wm9713_rec_srcl_mux_controls), 400*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0, 401*83ac08c0SLiam Girdwood &wm9713_rec_srcr_mux_controls), 402*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, 403*83ac08c0SLiam Girdwood &wm9713_mic_sel_mux_controls), 404*83ac08c0SLiam Girdwood SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, 405*83ac08c0SLiam Girdwood &wm9713_micb_sel_mux_controls), 406*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, 407*83ac08c0SLiam Girdwood &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls), 408*83ac08c0SLiam Girdwood mixer_event, SND_SOC_DAPM_POST_REG), 409*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, 410*83ac08c0SLiam Girdwood &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls), 411*83ac08c0SLiam Girdwood mixer_event, SND_SOC_DAPM_POST_REG), 412*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, 413*83ac08c0SLiam Girdwood &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), 414*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, 415*83ac08c0SLiam Girdwood &wm9713_speaker_mixer_controls[0], 416*83ac08c0SLiam Girdwood ARRAY_SIZE(wm9713_speaker_mixer_controls)), 417*83ac08c0SLiam Girdwood SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1), 418*83ac08c0SLiam Girdwood SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1), 419*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 420*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 421*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 422*83ac08c0SLiam Girdwood SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), 423*83ac08c0SLiam Girdwood SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1), 424*83ac08c0SLiam Girdwood SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), 425*83ac08c0SLiam Girdwood SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1), 426*83ac08c0SLiam Girdwood SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1), 427*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), 428*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), 429*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), 430*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0), 431*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0), 432*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0), 433*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0), 434*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0), 435*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0), 436*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0), 437*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0), 438*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0), 439*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0), 440*83ac08c0SLiam Girdwood SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0), 441*83ac08c0SLiam Girdwood SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1), 442*83ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("MONO"), 443*83ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("HPL"), 444*83ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("HPR"), 445*83ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("SPKL"), 446*83ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("SPKR"), 447*83ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("OUT3"), 448*83ac08c0SLiam Girdwood SND_SOC_DAPM_OUTPUT("OUT4"), 449*83ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("LINEL"), 450*83ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("LINER"), 451*83ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MONOIN"), 452*83ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("PCBEEP"), 453*83ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MIC1"), 454*83ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MIC2A"), 455*83ac08c0SLiam Girdwood SND_SOC_DAPM_INPUT("MIC2B"), 456*83ac08c0SLiam Girdwood SND_SOC_DAPM_VMID("VMID"), 457*83ac08c0SLiam Girdwood }; 458*83ac08c0SLiam Girdwood 459*83ac08c0SLiam Girdwood static const char *audio_map[][3] = { 460*83ac08c0SLiam Girdwood /* left HP mixer */ 461*83ac08c0SLiam Girdwood {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"}, 462*83ac08c0SLiam Girdwood {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, 463*83ac08c0SLiam Girdwood {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, 464*83ac08c0SLiam Girdwood {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"}, 465*83ac08c0SLiam Girdwood {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, 466*83ac08c0SLiam Girdwood {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"}, 467*83ac08c0SLiam Girdwood {"Left HP Mixer", NULL, "Capture Headphone Mux"}, 468*83ac08c0SLiam Girdwood 469*83ac08c0SLiam Girdwood /* right HP mixer */ 470*83ac08c0SLiam Girdwood {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"}, 471*83ac08c0SLiam Girdwood {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"}, 472*83ac08c0SLiam Girdwood {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, 473*83ac08c0SLiam Girdwood {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"}, 474*83ac08c0SLiam Girdwood {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, 475*83ac08c0SLiam Girdwood {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"}, 476*83ac08c0SLiam Girdwood {"Right HP Mixer", NULL, "Capture Headphone Mux"}, 477*83ac08c0SLiam Girdwood 478*83ac08c0SLiam Girdwood /* virtual mixer - mixes left & right channels for spk and mono */ 479*83ac08c0SLiam Girdwood {"AC97 Mixer", NULL, "Left DAC"}, 480*83ac08c0SLiam Girdwood {"AC97 Mixer", NULL, "Right DAC"}, 481*83ac08c0SLiam Girdwood {"Line Mixer", NULL, "Right Line In"}, 482*83ac08c0SLiam Girdwood {"Line Mixer", NULL, "Left Line In"}, 483*83ac08c0SLiam Girdwood {"HP Mixer", NULL, "Left HP Mixer"}, 484*83ac08c0SLiam Girdwood {"HP Mixer", NULL, "Right HP Mixer"}, 485*83ac08c0SLiam Girdwood {"Capture Mixer", NULL, "Left Capture Source"}, 486*83ac08c0SLiam Girdwood {"Capture Mixer", NULL, "Right Capture Source"}, 487*83ac08c0SLiam Girdwood 488*83ac08c0SLiam Girdwood /* speaker mixer */ 489*83ac08c0SLiam Girdwood {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"}, 490*83ac08c0SLiam Girdwood {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"}, 491*83ac08c0SLiam Girdwood {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, 492*83ac08c0SLiam Girdwood {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"}, 493*83ac08c0SLiam Girdwood {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, 494*83ac08c0SLiam Girdwood {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"}, 495*83ac08c0SLiam Girdwood 496*83ac08c0SLiam Girdwood /* mono mixer */ 497*83ac08c0SLiam Girdwood {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"}, 498*83ac08c0SLiam Girdwood {"Mono Mixer", "Voice Playback Switch", "Voice DAC"}, 499*83ac08c0SLiam Girdwood {"Mono Mixer", "Aux Playback Switch", "Aux DAC"}, 500*83ac08c0SLiam Girdwood {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"}, 501*83ac08c0SLiam Girdwood {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"}, 502*83ac08c0SLiam Girdwood {"Mono Mixer", NULL, "Capture Mono Mux"}, 503*83ac08c0SLiam Girdwood 504*83ac08c0SLiam Girdwood /* DAC inv mux 1 */ 505*83ac08c0SLiam Girdwood {"DAC Inv Mux 1", "Mono", "Mono Mixer"}, 506*83ac08c0SLiam Girdwood {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"}, 507*83ac08c0SLiam Girdwood {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"}, 508*83ac08c0SLiam Girdwood {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"}, 509*83ac08c0SLiam Girdwood {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"}, 510*83ac08c0SLiam Girdwood 511*83ac08c0SLiam Girdwood /* DAC inv mux 2 */ 512*83ac08c0SLiam Girdwood {"DAC Inv Mux 2", "Mono", "Mono Mixer"}, 513*83ac08c0SLiam Girdwood {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"}, 514*83ac08c0SLiam Girdwood {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"}, 515*83ac08c0SLiam Girdwood {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"}, 516*83ac08c0SLiam Girdwood {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"}, 517*83ac08c0SLiam Girdwood 518*83ac08c0SLiam Girdwood /* headphone left mux */ 519*83ac08c0SLiam Girdwood {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"}, 520*83ac08c0SLiam Girdwood 521*83ac08c0SLiam Girdwood /* headphone right mux */ 522*83ac08c0SLiam Girdwood {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"}, 523*83ac08c0SLiam Girdwood 524*83ac08c0SLiam Girdwood /* speaker left mux */ 525*83ac08c0SLiam Girdwood {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"}, 526*83ac08c0SLiam Girdwood {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"}, 527*83ac08c0SLiam Girdwood {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"}, 528*83ac08c0SLiam Girdwood 529*83ac08c0SLiam Girdwood /* speaker right mux */ 530*83ac08c0SLiam Girdwood {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"}, 531*83ac08c0SLiam Girdwood {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"}, 532*83ac08c0SLiam Girdwood {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"}, 533*83ac08c0SLiam Girdwood 534*83ac08c0SLiam Girdwood /* mono mux */ 535*83ac08c0SLiam Girdwood {"Mono Out Mux", "Mono", "Mono Mixer"}, 536*83ac08c0SLiam Girdwood {"Mono Out Mux", "Inv", "DAC Inv Mux 1"}, 537*83ac08c0SLiam Girdwood 538*83ac08c0SLiam Girdwood /* out 3 mux */ 539*83ac08c0SLiam Girdwood {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"}, 540*83ac08c0SLiam Girdwood 541*83ac08c0SLiam Girdwood /* out 4 mux */ 542*83ac08c0SLiam Girdwood {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"}, 543*83ac08c0SLiam Girdwood 544*83ac08c0SLiam Girdwood /* output pga */ 545*83ac08c0SLiam Girdwood {"HPL", NULL, "Left Headphone"}, 546*83ac08c0SLiam Girdwood {"Left Headphone", NULL, "Left Headphone Out Mux"}, 547*83ac08c0SLiam Girdwood {"HPR", NULL, "Right Headphone"}, 548*83ac08c0SLiam Girdwood {"Right Headphone", NULL, "Right Headphone Out Mux"}, 549*83ac08c0SLiam Girdwood {"OUT3", NULL, "Out 3"}, 550*83ac08c0SLiam Girdwood {"Out 3", NULL, "Out 3 Mux"}, 551*83ac08c0SLiam Girdwood {"OUT4", NULL, "Out 4"}, 552*83ac08c0SLiam Girdwood {"Out 4", NULL, "Out 4 Mux"}, 553*83ac08c0SLiam Girdwood {"SPKL", NULL, "Left Speaker"}, 554*83ac08c0SLiam Girdwood {"Left Speaker", NULL, "Left Speaker Out Mux"}, 555*83ac08c0SLiam Girdwood {"SPKR", NULL, "Right Speaker"}, 556*83ac08c0SLiam Girdwood {"Right Speaker", NULL, "Right Speaker Out Mux"}, 557*83ac08c0SLiam Girdwood {"MONO", NULL, "Mono Out"}, 558*83ac08c0SLiam Girdwood {"Mono Out", NULL, "Mono Out Mux"}, 559*83ac08c0SLiam Girdwood 560*83ac08c0SLiam Girdwood /* input pga */ 561*83ac08c0SLiam Girdwood {"Left Line In", NULL, "LINEL"}, 562*83ac08c0SLiam Girdwood {"Right Line In", NULL, "LINER"}, 563*83ac08c0SLiam Girdwood {"Mono In", NULL, "MONOIN"}, 564*83ac08c0SLiam Girdwood {"Mic A PGA", NULL, "Mic A Pre Amp"}, 565*83ac08c0SLiam Girdwood {"Mic B PGA", NULL, "Mic B Pre Amp"}, 566*83ac08c0SLiam Girdwood 567*83ac08c0SLiam Girdwood /* left capture select */ 568*83ac08c0SLiam Girdwood {"Left Capture Source", "Mic 1", "Mic A Pre Amp"}, 569*83ac08c0SLiam Girdwood {"Left Capture Source", "Mic 2", "Mic B Pre Amp"}, 570*83ac08c0SLiam Girdwood {"Left Capture Source", "Line", "LINEL"}, 571*83ac08c0SLiam Girdwood {"Left Capture Source", "Mono In", "MONOIN"}, 572*83ac08c0SLiam Girdwood {"Left Capture Source", "Headphone", "Left HP Mixer"}, 573*83ac08c0SLiam Girdwood {"Left Capture Source", "Speaker", "Speaker Mixer"}, 574*83ac08c0SLiam Girdwood {"Left Capture Source", "Mono Out", "Mono Mixer"}, 575*83ac08c0SLiam Girdwood 576*83ac08c0SLiam Girdwood /* right capture select */ 577*83ac08c0SLiam Girdwood {"Right Capture Source", "Mic 1", "Mic A Pre Amp"}, 578*83ac08c0SLiam Girdwood {"Right Capture Source", "Mic 2", "Mic B Pre Amp"}, 579*83ac08c0SLiam Girdwood {"Right Capture Source", "Line", "LINER"}, 580*83ac08c0SLiam Girdwood {"Right Capture Source", "Mono In", "MONOIN"}, 581*83ac08c0SLiam Girdwood {"Right Capture Source", "Headphone", "Right HP Mixer"}, 582*83ac08c0SLiam Girdwood {"Right Capture Source", "Speaker", "Speaker Mixer"}, 583*83ac08c0SLiam Girdwood {"Right Capture Source", "Mono Out", "Mono Mixer"}, 584*83ac08c0SLiam Girdwood 585*83ac08c0SLiam Girdwood /* left ADC */ 586*83ac08c0SLiam Girdwood {"Left ADC", NULL, "Left Capture Source"}, 587*83ac08c0SLiam Girdwood 588*83ac08c0SLiam Girdwood /* right ADC */ 589*83ac08c0SLiam Girdwood {"Right ADC", NULL, "Right Capture Source"}, 590*83ac08c0SLiam Girdwood 591*83ac08c0SLiam Girdwood /* mic */ 592*83ac08c0SLiam Girdwood {"Mic A Pre Amp", NULL, "Mic A Source"}, 593*83ac08c0SLiam Girdwood {"Mic A Source", "Mic 1", "MIC1"}, 594*83ac08c0SLiam Girdwood {"Mic A Source", "Mic 2 A", "MIC2A"}, 595*83ac08c0SLiam Girdwood {"Mic A Source", "Mic 2 B", "Mic B Source"}, 596*83ac08c0SLiam Girdwood {"Mic B Pre Amp", "MPB", "Mic B Source"}, 597*83ac08c0SLiam Girdwood {"Mic B Source", NULL, "MIC2B"}, 598*83ac08c0SLiam Girdwood 599*83ac08c0SLiam Girdwood /* headphone capture */ 600*83ac08c0SLiam Girdwood {"Capture Headphone Mux", "Stereo", "Capture Mixer"}, 601*83ac08c0SLiam Girdwood {"Capture Headphone Mux", "Left", "Left Capture Source"}, 602*83ac08c0SLiam Girdwood {"Capture Headphone Mux", "Right", "Right Capture Source"}, 603*83ac08c0SLiam Girdwood 604*83ac08c0SLiam Girdwood /* mono capture */ 605*83ac08c0SLiam Girdwood {"Capture Mono Mux", "Stereo", "Capture Mixer"}, 606*83ac08c0SLiam Girdwood {"Capture Mono Mux", "Left", "Left Capture Source"}, 607*83ac08c0SLiam Girdwood {"Capture Mono Mux", "Right", "Right Capture Source"}, 608*83ac08c0SLiam Girdwood 609*83ac08c0SLiam Girdwood {NULL, NULL, NULL}, 610*83ac08c0SLiam Girdwood }; 611*83ac08c0SLiam Girdwood 612*83ac08c0SLiam Girdwood static int wm9713_add_widgets(struct snd_soc_codec *codec) 613*83ac08c0SLiam Girdwood { 614*83ac08c0SLiam Girdwood int i; 615*83ac08c0SLiam Girdwood 616*83ac08c0SLiam Girdwood for (i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++) 617*83ac08c0SLiam Girdwood snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]); 618*83ac08c0SLiam Girdwood 619*83ac08c0SLiam Girdwood /* set up audio path audio_mapnects */ 620*83ac08c0SLiam Girdwood for (i = 0; audio_map[i][0] != NULL; i++) 621*83ac08c0SLiam Girdwood snd_soc_dapm_connect_input(codec, audio_map[i][0], 622*83ac08c0SLiam Girdwood audio_map[i][1], audio_map[i][2]); 623*83ac08c0SLiam Girdwood 624*83ac08c0SLiam Girdwood snd_soc_dapm_new_widgets(codec); 625*83ac08c0SLiam Girdwood return 0; 626*83ac08c0SLiam Girdwood } 627*83ac08c0SLiam Girdwood 628*83ac08c0SLiam Girdwood static unsigned int ac97_read(struct snd_soc_codec *codec, 629*83ac08c0SLiam Girdwood unsigned int reg) 630*83ac08c0SLiam Girdwood { 631*83ac08c0SLiam Girdwood u16 *cache = codec->reg_cache; 632*83ac08c0SLiam Girdwood 633*83ac08c0SLiam Girdwood if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || 634*83ac08c0SLiam Girdwood reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || 635*83ac08c0SLiam Girdwood reg == AC97_CD) 636*83ac08c0SLiam Girdwood return soc_ac97_ops.read(codec->ac97, reg); 637*83ac08c0SLiam Girdwood else { 638*83ac08c0SLiam Girdwood reg = reg >> 1; 639*83ac08c0SLiam Girdwood 640*83ac08c0SLiam Girdwood if (reg > (ARRAY_SIZE(wm9713_reg))) 641*83ac08c0SLiam Girdwood return -EIO; 642*83ac08c0SLiam Girdwood 643*83ac08c0SLiam Girdwood return cache[reg]; 644*83ac08c0SLiam Girdwood } 645*83ac08c0SLiam Girdwood } 646*83ac08c0SLiam Girdwood 647*83ac08c0SLiam Girdwood static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, 648*83ac08c0SLiam Girdwood unsigned int val) 649*83ac08c0SLiam Girdwood { 650*83ac08c0SLiam Girdwood u16 *cache = codec->reg_cache; 651*83ac08c0SLiam Girdwood if (reg < 0x7c) 652*83ac08c0SLiam Girdwood soc_ac97_ops.write(codec->ac97, reg, val); 653*83ac08c0SLiam Girdwood reg = reg >> 1; 654*83ac08c0SLiam Girdwood if (reg <= (ARRAY_SIZE(wm9713_reg))) 655*83ac08c0SLiam Girdwood cache[reg] = val; 656*83ac08c0SLiam Girdwood 657*83ac08c0SLiam Girdwood return 0; 658*83ac08c0SLiam Girdwood } 659*83ac08c0SLiam Girdwood 660*83ac08c0SLiam Girdwood /* PLL divisors */ 661*83ac08c0SLiam Girdwood struct _pll_div { 662*83ac08c0SLiam Girdwood u32 divsel:1; 663*83ac08c0SLiam Girdwood u32 divctl:1; 664*83ac08c0SLiam Girdwood u32 lf:1; 665*83ac08c0SLiam Girdwood u32 n:4; 666*83ac08c0SLiam Girdwood u32 k:24; 667*83ac08c0SLiam Girdwood }; 668*83ac08c0SLiam Girdwood 669*83ac08c0SLiam Girdwood /* The size in bits of the PLL divide multiplied by 10 670*83ac08c0SLiam Girdwood * to allow rounding later */ 671*83ac08c0SLiam Girdwood #define FIXED_PLL_SIZE ((1 << 22) * 10) 672*83ac08c0SLiam Girdwood 673*83ac08c0SLiam Girdwood static void pll_factors(struct _pll_div *pll_div, unsigned int source) 674*83ac08c0SLiam Girdwood { 675*83ac08c0SLiam Girdwood u64 Kpart; 676*83ac08c0SLiam Girdwood unsigned int K, Ndiv, Nmod, target; 677*83ac08c0SLiam Girdwood 678*83ac08c0SLiam Girdwood /* The the PLL output is always 98.304MHz. */ 679*83ac08c0SLiam Girdwood target = 98304000; 680*83ac08c0SLiam Girdwood 681*83ac08c0SLiam Girdwood /* If the input frequency is over 14.4MHz then scale it down. */ 682*83ac08c0SLiam Girdwood if (source > 14400000) { 683*83ac08c0SLiam Girdwood source >>= 1; 684*83ac08c0SLiam Girdwood pll_div->divsel = 1; 685*83ac08c0SLiam Girdwood 686*83ac08c0SLiam Girdwood if (source > 14400000) { 687*83ac08c0SLiam Girdwood source >>= 1; 688*83ac08c0SLiam Girdwood pll_div->divctl = 1; 689*83ac08c0SLiam Girdwood } else 690*83ac08c0SLiam Girdwood pll_div->divctl = 0; 691*83ac08c0SLiam Girdwood 692*83ac08c0SLiam Girdwood } else { 693*83ac08c0SLiam Girdwood pll_div->divsel = 0; 694*83ac08c0SLiam Girdwood pll_div->divctl = 0; 695*83ac08c0SLiam Girdwood } 696*83ac08c0SLiam Girdwood 697*83ac08c0SLiam Girdwood /* Low frequency sources require an additional divide in the 698*83ac08c0SLiam Girdwood * loop. 699*83ac08c0SLiam Girdwood */ 700*83ac08c0SLiam Girdwood if (source < 8192000) { 701*83ac08c0SLiam Girdwood pll_div->lf = 1; 702*83ac08c0SLiam Girdwood target >>= 2; 703*83ac08c0SLiam Girdwood } else 704*83ac08c0SLiam Girdwood pll_div->lf = 0; 705*83ac08c0SLiam Girdwood 706*83ac08c0SLiam Girdwood Ndiv = target / source; 707*83ac08c0SLiam Girdwood if ((Ndiv < 5) || (Ndiv > 12)) 708*83ac08c0SLiam Girdwood printk(KERN_WARNING 709*83ac08c0SLiam Girdwood "WM9713 PLL N value %d out of recommended range!\n", 710*83ac08c0SLiam Girdwood Ndiv); 711*83ac08c0SLiam Girdwood 712*83ac08c0SLiam Girdwood pll_div->n = Ndiv; 713*83ac08c0SLiam Girdwood Nmod = target % source; 714*83ac08c0SLiam Girdwood Kpart = FIXED_PLL_SIZE * (long long)Nmod; 715*83ac08c0SLiam Girdwood 716*83ac08c0SLiam Girdwood do_div(Kpart, source); 717*83ac08c0SLiam Girdwood 718*83ac08c0SLiam Girdwood K = Kpart & 0xFFFFFFFF; 719*83ac08c0SLiam Girdwood 720*83ac08c0SLiam Girdwood /* Check if we need to round */ 721*83ac08c0SLiam Girdwood if ((K % 10) >= 5) 722*83ac08c0SLiam Girdwood K += 5; 723*83ac08c0SLiam Girdwood 724*83ac08c0SLiam Girdwood /* Move down to proper range now rounding is done */ 725*83ac08c0SLiam Girdwood K /= 10; 726*83ac08c0SLiam Girdwood 727*83ac08c0SLiam Girdwood pll_div->k = K; 728*83ac08c0SLiam Girdwood } 729*83ac08c0SLiam Girdwood 730*83ac08c0SLiam Girdwood /** 731*83ac08c0SLiam Girdwood * Please note that changing the PLL input frequency may require 732*83ac08c0SLiam Girdwood * resynchronisation with the AC97 controller. 733*83ac08c0SLiam Girdwood */ 734*83ac08c0SLiam Girdwood static int wm9713_set_pll(struct snd_soc_codec *codec, 735*83ac08c0SLiam Girdwood int pll_id, unsigned int freq_in, unsigned int freq_out) 736*83ac08c0SLiam Girdwood { 737*83ac08c0SLiam Girdwood struct wm9713_priv *wm9713 = codec->private_data; 738*83ac08c0SLiam Girdwood u16 reg, reg2; 739*83ac08c0SLiam Girdwood struct _pll_div pll_div; 740*83ac08c0SLiam Girdwood 741*83ac08c0SLiam Girdwood /* turn PLL off ? */ 742*83ac08c0SLiam Girdwood if (freq_in == 0 || freq_out == 0) { 743*83ac08c0SLiam Girdwood /* disable PLL power and select ext source */ 744*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_HANDSET_RATE); 745*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080); 746*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_EXTENDED_MID); 747*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200); 748*83ac08c0SLiam Girdwood wm9713->pll_out = 0; 749*83ac08c0SLiam Girdwood return 0; 750*83ac08c0SLiam Girdwood } 751*83ac08c0SLiam Girdwood 752*83ac08c0SLiam Girdwood pll_factors(&pll_div, freq_in); 753*83ac08c0SLiam Girdwood 754*83ac08c0SLiam Girdwood if (pll_div.k == 0) { 755*83ac08c0SLiam Girdwood reg = (pll_div.n << 12) | (pll_div.lf << 11) | 756*83ac08c0SLiam Girdwood (pll_div.divsel << 9) | (pll_div.divctl << 8); 757*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg); 758*83ac08c0SLiam Girdwood } else { 759*83ac08c0SLiam Girdwood /* write the fractional k to the reg 0x46 pages */ 760*83ac08c0SLiam Girdwood reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | 761*83ac08c0SLiam Girdwood (pll_div.divsel << 9) | (pll_div.divctl << 8); 762*83ac08c0SLiam Girdwood 763*83ac08c0SLiam Girdwood /* K [21:20] */ 764*83ac08c0SLiam Girdwood reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); 765*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg); 766*83ac08c0SLiam Girdwood 767*83ac08c0SLiam Girdwood /* K [19:16] */ 768*83ac08c0SLiam Girdwood reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); 769*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg); 770*83ac08c0SLiam Girdwood 771*83ac08c0SLiam Girdwood /* K [15:12] */ 772*83ac08c0SLiam Girdwood reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); 773*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg); 774*83ac08c0SLiam Girdwood 775*83ac08c0SLiam Girdwood /* K [11:8] */ 776*83ac08c0SLiam Girdwood reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); 777*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg); 778*83ac08c0SLiam Girdwood 779*83ac08c0SLiam Girdwood /* K [7:4] */ 780*83ac08c0SLiam Girdwood reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); 781*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg); 782*83ac08c0SLiam Girdwood 783*83ac08c0SLiam Girdwood reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ 784*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg); 785*83ac08c0SLiam Girdwood } 786*83ac08c0SLiam Girdwood 787*83ac08c0SLiam Girdwood /* turn PLL on and select as source */ 788*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_EXTENDED_MID); 789*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff); 790*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_HANDSET_RATE); 791*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f); 792*83ac08c0SLiam Girdwood wm9713->pll_out = freq_out; 793*83ac08c0SLiam Girdwood wm9713->pll_in = freq_in; 794*83ac08c0SLiam Girdwood 795*83ac08c0SLiam Girdwood /* wait 10ms AC97 link frames for the link to stabilise */ 796*83ac08c0SLiam Girdwood schedule_timeout_interruptible(msecs_to_jiffies(10)); 797*83ac08c0SLiam Girdwood return 0; 798*83ac08c0SLiam Girdwood } 799*83ac08c0SLiam Girdwood 800*83ac08c0SLiam Girdwood static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai, 801*83ac08c0SLiam Girdwood int pll_id, unsigned int freq_in, unsigned int freq_out) 802*83ac08c0SLiam Girdwood { 803*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = codec_dai->codec; 804*83ac08c0SLiam Girdwood return wm9713_set_pll(codec, pll_id, freq_in, freq_out); 805*83ac08c0SLiam Girdwood } 806*83ac08c0SLiam Girdwood 807*83ac08c0SLiam Girdwood /* 808*83ac08c0SLiam Girdwood * Tristate the PCM DAI lines, tristate can be disabled by calling 809*83ac08c0SLiam Girdwood * wm9713_set_dai_fmt() 810*83ac08c0SLiam Girdwood */ 811*83ac08c0SLiam Girdwood static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai, 812*83ac08c0SLiam Girdwood int tristate) 813*83ac08c0SLiam Girdwood { 814*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = codec_dai->codec; 815*83ac08c0SLiam Girdwood u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff; 816*83ac08c0SLiam Girdwood 817*83ac08c0SLiam Girdwood if (tristate) 818*83ac08c0SLiam Girdwood ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); 819*83ac08c0SLiam Girdwood 820*83ac08c0SLiam Girdwood return 0; 821*83ac08c0SLiam Girdwood } 822*83ac08c0SLiam Girdwood 823*83ac08c0SLiam Girdwood /* 824*83ac08c0SLiam Girdwood * Configure WM9713 clock dividers. 825*83ac08c0SLiam Girdwood * Voice DAC needs 256 FS 826*83ac08c0SLiam Girdwood */ 827*83ac08c0SLiam Girdwood static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai, 828*83ac08c0SLiam Girdwood int div_id, int div) 829*83ac08c0SLiam Girdwood { 830*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = codec_dai->codec; 831*83ac08c0SLiam Girdwood u16 reg; 832*83ac08c0SLiam Girdwood 833*83ac08c0SLiam Girdwood switch (div_id) { 834*83ac08c0SLiam Girdwood case WM9713_PCMCLK_DIV: 835*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff; 836*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, reg | div); 837*83ac08c0SLiam Girdwood break; 838*83ac08c0SLiam Girdwood case WM9713_CLKA_MULT: 839*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd; 840*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, reg | div); 841*83ac08c0SLiam Girdwood break; 842*83ac08c0SLiam Girdwood case WM9713_CLKB_MULT: 843*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb; 844*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, reg | div); 845*83ac08c0SLiam Girdwood break; 846*83ac08c0SLiam Girdwood case WM9713_HIFI_DIV: 847*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff; 848*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, reg | div); 849*83ac08c0SLiam Girdwood break; 850*83ac08c0SLiam Girdwood case WM9713_PCMBCLK_DIV: 851*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff; 852*83ac08c0SLiam Girdwood ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div); 853*83ac08c0SLiam Girdwood break; 854*83ac08c0SLiam Girdwood case WM9713_PCMCLK_PLL_DIV: 855*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; 856*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div); 857*83ac08c0SLiam Girdwood break; 858*83ac08c0SLiam Girdwood case WM9713_HIFI_PLL_DIV: 859*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; 860*83ac08c0SLiam Girdwood ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div); 861*83ac08c0SLiam Girdwood break; 862*83ac08c0SLiam Girdwood default: 863*83ac08c0SLiam Girdwood return -EINVAL; 864*83ac08c0SLiam Girdwood } 865*83ac08c0SLiam Girdwood 866*83ac08c0SLiam Girdwood return 0; 867*83ac08c0SLiam Girdwood } 868*83ac08c0SLiam Girdwood 869*83ac08c0SLiam Girdwood static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, 870*83ac08c0SLiam Girdwood unsigned int fmt) 871*83ac08c0SLiam Girdwood { 872*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = codec_dai->codec; 873*83ac08c0SLiam Girdwood u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5; 874*83ac08c0SLiam Girdwood u16 reg = 0x8000; 875*83ac08c0SLiam Girdwood 876*83ac08c0SLiam Girdwood /* clock masters */ 877*83ac08c0SLiam Girdwood switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 878*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_CBM_CFM: 879*83ac08c0SLiam Girdwood reg |= 0x4000; 880*83ac08c0SLiam Girdwood gpio |= 0x0010; 881*83ac08c0SLiam Girdwood break; 882*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_CBM_CFS: 883*83ac08c0SLiam Girdwood reg |= 0x6000; 884*83ac08c0SLiam Girdwood gpio |= 0x0018; 885*83ac08c0SLiam Girdwood break; 886*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_CBS_CFS: 887*83ac08c0SLiam Girdwood reg |= 0x0200; 888*83ac08c0SLiam Girdwood gpio |= 0x001a; 889*83ac08c0SLiam Girdwood break; 890*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_CBS_CFM: 891*83ac08c0SLiam Girdwood gpio |= 0x0012; 892*83ac08c0SLiam Girdwood break; 893*83ac08c0SLiam Girdwood } 894*83ac08c0SLiam Girdwood 895*83ac08c0SLiam Girdwood /* clock inversion */ 896*83ac08c0SLiam Girdwood switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 897*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_IB_IF: 898*83ac08c0SLiam Girdwood reg |= 0x00c0; 899*83ac08c0SLiam Girdwood break; 900*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_IB_NF: 901*83ac08c0SLiam Girdwood reg |= 0x0080; 902*83ac08c0SLiam Girdwood break; 903*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_NB_IF: 904*83ac08c0SLiam Girdwood reg |= 0x0040; 905*83ac08c0SLiam Girdwood break; 906*83ac08c0SLiam Girdwood } 907*83ac08c0SLiam Girdwood 908*83ac08c0SLiam Girdwood /* DAI format */ 909*83ac08c0SLiam Girdwood switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 910*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_I2S: 911*83ac08c0SLiam Girdwood reg |= 0x0002; 912*83ac08c0SLiam Girdwood break; 913*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_RIGHT_J: 914*83ac08c0SLiam Girdwood break; 915*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_LEFT_J: 916*83ac08c0SLiam Girdwood reg |= 0x0001; 917*83ac08c0SLiam Girdwood break; 918*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_DSP_A: 919*83ac08c0SLiam Girdwood reg |= 0x0003; 920*83ac08c0SLiam Girdwood break; 921*83ac08c0SLiam Girdwood case SND_SOC_DAIFMT_DSP_B: 922*83ac08c0SLiam Girdwood reg |= 0x0043; 923*83ac08c0SLiam Girdwood break; 924*83ac08c0SLiam Girdwood } 925*83ac08c0SLiam Girdwood 926*83ac08c0SLiam Girdwood ac97_write(codec, AC97_GPIO_CFG, gpio); 927*83ac08c0SLiam Girdwood ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); 928*83ac08c0SLiam Girdwood return 0; 929*83ac08c0SLiam Girdwood } 930*83ac08c0SLiam Girdwood 931*83ac08c0SLiam Girdwood static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, 932*83ac08c0SLiam Girdwood struct snd_pcm_hw_params *params) 933*83ac08c0SLiam Girdwood { 934*83ac08c0SLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 935*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = rtd->socdev; 936*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 937*83ac08c0SLiam Girdwood u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3; 938*83ac08c0SLiam Girdwood 939*83ac08c0SLiam Girdwood switch (params_format(params)) { 940*83ac08c0SLiam Girdwood case SNDRV_PCM_FORMAT_S16_LE: 941*83ac08c0SLiam Girdwood break; 942*83ac08c0SLiam Girdwood case SNDRV_PCM_FORMAT_S20_3LE: 943*83ac08c0SLiam Girdwood reg |= 0x0004; 944*83ac08c0SLiam Girdwood break; 945*83ac08c0SLiam Girdwood case SNDRV_PCM_FORMAT_S24_LE: 946*83ac08c0SLiam Girdwood reg |= 0x0008; 947*83ac08c0SLiam Girdwood break; 948*83ac08c0SLiam Girdwood case SNDRV_PCM_FORMAT_S32_LE: 949*83ac08c0SLiam Girdwood reg |= 0x000c; 950*83ac08c0SLiam Girdwood break; 951*83ac08c0SLiam Girdwood } 952*83ac08c0SLiam Girdwood 953*83ac08c0SLiam Girdwood /* enable PCM interface in master mode */ 954*83ac08c0SLiam Girdwood ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); 955*83ac08c0SLiam Girdwood return 0; 956*83ac08c0SLiam Girdwood } 957*83ac08c0SLiam Girdwood 958*83ac08c0SLiam Girdwood static void wm9713_voiceshutdown(struct snd_pcm_substream *substream) 959*83ac08c0SLiam Girdwood { 960*83ac08c0SLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 961*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = rtd->socdev; 962*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 963*83ac08c0SLiam Girdwood u16 status; 964*83ac08c0SLiam Girdwood 965*83ac08c0SLiam Girdwood /* Gracefully shut down the voice interface. */ 966*83ac08c0SLiam Girdwood status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000; 967*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, 0x0280); 968*83ac08c0SLiam Girdwood schedule_timeout_interruptible(msecs_to_jiffies(1)); 969*83ac08c0SLiam Girdwood ac97_write(codec, AC97_HANDSET_RATE, 0x0F80); 970*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_MID, status); 971*83ac08c0SLiam Girdwood } 972*83ac08c0SLiam Girdwood 973*83ac08c0SLiam Girdwood static int ac97_hifi_prepare(struct snd_pcm_substream *substream) 974*83ac08c0SLiam Girdwood { 975*83ac08c0SLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 976*83ac08c0SLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 977*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = rtd->socdev; 978*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 979*83ac08c0SLiam Girdwood int reg; 980*83ac08c0SLiam Girdwood u16 vra; 981*83ac08c0SLiam Girdwood 982*83ac08c0SLiam Girdwood vra = ac97_read(codec, AC97_EXTENDED_STATUS); 983*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); 984*83ac08c0SLiam Girdwood 985*83ac08c0SLiam Girdwood if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 986*83ac08c0SLiam Girdwood reg = AC97_PCM_FRONT_DAC_RATE; 987*83ac08c0SLiam Girdwood else 988*83ac08c0SLiam Girdwood reg = AC97_PCM_LR_ADC_RATE; 989*83ac08c0SLiam Girdwood 990*83ac08c0SLiam Girdwood return ac97_write(codec, reg, runtime->rate); 991*83ac08c0SLiam Girdwood } 992*83ac08c0SLiam Girdwood 993*83ac08c0SLiam Girdwood static int ac97_aux_prepare(struct snd_pcm_substream *substream) 994*83ac08c0SLiam Girdwood { 995*83ac08c0SLiam Girdwood struct snd_pcm_runtime *runtime = substream->runtime; 996*83ac08c0SLiam Girdwood struct snd_soc_pcm_runtime *rtd = substream->private_data; 997*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = rtd->socdev; 998*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 999*83ac08c0SLiam Girdwood u16 vra, xsle; 1000*83ac08c0SLiam Girdwood 1001*83ac08c0SLiam Girdwood vra = ac97_read(codec, AC97_EXTENDED_STATUS); 1002*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); 1003*83ac08c0SLiam Girdwood xsle = ac97_read(codec, AC97_PCI_SID); 1004*83ac08c0SLiam Girdwood ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); 1005*83ac08c0SLiam Girdwood 1006*83ac08c0SLiam Girdwood if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 1007*83ac08c0SLiam Girdwood return -ENODEV; 1008*83ac08c0SLiam Girdwood 1009*83ac08c0SLiam Girdwood return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); 1010*83ac08c0SLiam Girdwood } 1011*83ac08c0SLiam Girdwood 1012*83ac08c0SLiam Girdwood #define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 1013*83ac08c0SLiam Girdwood SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\ 1014*83ac08c0SLiam Girdwood SNDRV_PCM_RATE_48000) 1015*83ac08c0SLiam Girdwood 1016*83ac08c0SLiam Girdwood #define WM9713_PCM_FORMATS \ 1017*83ac08c0SLiam Girdwood (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ 1018*83ac08c0SLiam Girdwood SNDRV_PCM_FORMAT_S24_LE) 1019*83ac08c0SLiam Girdwood 1020*83ac08c0SLiam Girdwood struct snd_soc_codec_dai wm9713_dai[] = { 1021*83ac08c0SLiam Girdwood { 1022*83ac08c0SLiam Girdwood .name = "AC97 HiFi", 1023*83ac08c0SLiam Girdwood .type = SND_SOC_DAI_AC97_BUS, 1024*83ac08c0SLiam Girdwood .playback = { 1025*83ac08c0SLiam Girdwood .stream_name = "HiFi Playback", 1026*83ac08c0SLiam Girdwood .channels_min = 1, 1027*83ac08c0SLiam Girdwood .channels_max = 2, 1028*83ac08c0SLiam Girdwood .rates = WM9713_RATES, 1029*83ac08c0SLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 1030*83ac08c0SLiam Girdwood .capture = { 1031*83ac08c0SLiam Girdwood .stream_name = "HiFi Capture", 1032*83ac08c0SLiam Girdwood .channels_min = 1, 1033*83ac08c0SLiam Girdwood .channels_max = 2, 1034*83ac08c0SLiam Girdwood .rates = WM9713_RATES, 1035*83ac08c0SLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 1036*83ac08c0SLiam Girdwood .ops = { 1037*83ac08c0SLiam Girdwood .prepare = ac97_hifi_prepare,}, 1038*83ac08c0SLiam Girdwood .dai_ops = { 1039*83ac08c0SLiam Girdwood .set_clkdiv = wm9713_set_dai_clkdiv, 1040*83ac08c0SLiam Girdwood .set_pll = wm9713_set_dai_pll,}, 1041*83ac08c0SLiam Girdwood }, 1042*83ac08c0SLiam Girdwood { 1043*83ac08c0SLiam Girdwood .name = "AC97 Aux", 1044*83ac08c0SLiam Girdwood .playback = { 1045*83ac08c0SLiam Girdwood .stream_name = "Aux Playback", 1046*83ac08c0SLiam Girdwood .channels_min = 1, 1047*83ac08c0SLiam Girdwood .channels_max = 1, 1048*83ac08c0SLiam Girdwood .rates = WM9713_RATES, 1049*83ac08c0SLiam Girdwood .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 1050*83ac08c0SLiam Girdwood .ops = { 1051*83ac08c0SLiam Girdwood .prepare = ac97_aux_prepare,}, 1052*83ac08c0SLiam Girdwood .dai_ops = { 1053*83ac08c0SLiam Girdwood .set_clkdiv = wm9713_set_dai_clkdiv, 1054*83ac08c0SLiam Girdwood .set_pll = wm9713_set_dai_pll,}, 1055*83ac08c0SLiam Girdwood }, 1056*83ac08c0SLiam Girdwood { 1057*83ac08c0SLiam Girdwood .name = "WM9713 Voice", 1058*83ac08c0SLiam Girdwood .playback = { 1059*83ac08c0SLiam Girdwood .stream_name = "Voice Playback", 1060*83ac08c0SLiam Girdwood .channels_min = 1, 1061*83ac08c0SLiam Girdwood .channels_max = 1, 1062*83ac08c0SLiam Girdwood .rates = WM9713_RATES, 1063*83ac08c0SLiam Girdwood .formats = WM9713_PCM_FORMATS,}, 1064*83ac08c0SLiam Girdwood .capture = { 1065*83ac08c0SLiam Girdwood .stream_name = "Voice Capture", 1066*83ac08c0SLiam Girdwood .channels_min = 1, 1067*83ac08c0SLiam Girdwood .channels_max = 2, 1068*83ac08c0SLiam Girdwood .rates = WM9713_RATES, 1069*83ac08c0SLiam Girdwood .formats = WM9713_PCM_FORMATS,}, 1070*83ac08c0SLiam Girdwood .ops = { 1071*83ac08c0SLiam Girdwood .hw_params = wm9713_pcm_hw_params, 1072*83ac08c0SLiam Girdwood .shutdown = wm9713_voiceshutdown,}, 1073*83ac08c0SLiam Girdwood .dai_ops = { 1074*83ac08c0SLiam Girdwood .set_clkdiv = wm9713_set_dai_clkdiv, 1075*83ac08c0SLiam Girdwood .set_pll = wm9713_set_dai_pll, 1076*83ac08c0SLiam Girdwood .set_fmt = wm9713_set_dai_fmt, 1077*83ac08c0SLiam Girdwood .set_tristate = wm9713_set_dai_tristate, 1078*83ac08c0SLiam Girdwood }, 1079*83ac08c0SLiam Girdwood }, 1080*83ac08c0SLiam Girdwood }; 1081*83ac08c0SLiam Girdwood EXPORT_SYMBOL_GPL(wm9713_dai); 1082*83ac08c0SLiam Girdwood 1083*83ac08c0SLiam Girdwood int wm9713_reset(struct snd_soc_codec *codec, int try_warm) 1084*83ac08c0SLiam Girdwood { 1085*83ac08c0SLiam Girdwood if (try_warm && soc_ac97_ops.warm_reset) { 1086*83ac08c0SLiam Girdwood soc_ac97_ops.warm_reset(codec->ac97); 1087*83ac08c0SLiam Girdwood if (!(ac97_read(codec, 0) & 0x8000)) 1088*83ac08c0SLiam Girdwood return 1; 1089*83ac08c0SLiam Girdwood } 1090*83ac08c0SLiam Girdwood 1091*83ac08c0SLiam Girdwood soc_ac97_ops.reset(codec->ac97); 1092*83ac08c0SLiam Girdwood if (ac97_read(codec, 0) & 0x8000) 1093*83ac08c0SLiam Girdwood return -EIO; 1094*83ac08c0SLiam Girdwood return 0; 1095*83ac08c0SLiam Girdwood } 1096*83ac08c0SLiam Girdwood EXPORT_SYMBOL_GPL(wm9713_reset); 1097*83ac08c0SLiam Girdwood 1098*83ac08c0SLiam Girdwood static int wm9713_dapm_event(struct snd_soc_codec *codec, int event) 1099*83ac08c0SLiam Girdwood { 1100*83ac08c0SLiam Girdwood u16 reg; 1101*83ac08c0SLiam Girdwood 1102*83ac08c0SLiam Girdwood switch (event) { 1103*83ac08c0SLiam Girdwood case SNDRV_CTL_POWER_D0: /* full On */ 1104*83ac08c0SLiam Girdwood /* enable thermal shutdown */ 1105*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff; 1106*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_MID, reg); 1107*83ac08c0SLiam Girdwood break; 1108*83ac08c0SLiam Girdwood case SNDRV_CTL_POWER_D1: /* partial On */ 1109*83ac08c0SLiam Girdwood case SNDRV_CTL_POWER_D2: /* partial On */ 1110*83ac08c0SLiam Girdwood break; 1111*83ac08c0SLiam Girdwood case SNDRV_CTL_POWER_D3hot: /* Off, with power */ 1112*83ac08c0SLiam Girdwood /* enable master bias and vmid */ 1113*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff; 1114*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_MID, reg); 1115*83ac08c0SLiam Girdwood ac97_write(codec, AC97_POWERDOWN, 0x0000); 1116*83ac08c0SLiam Girdwood break; 1117*83ac08c0SLiam Girdwood case SNDRV_CTL_POWER_D3cold: /* Off, without power */ 1118*83ac08c0SLiam Girdwood /* disable everything including AC link */ 1119*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_MID, 0xffff); 1120*83ac08c0SLiam Girdwood ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); 1121*83ac08c0SLiam Girdwood ac97_write(codec, AC97_POWERDOWN, 0xffff); 1122*83ac08c0SLiam Girdwood break; 1123*83ac08c0SLiam Girdwood } 1124*83ac08c0SLiam Girdwood codec->dapm_state = event; 1125*83ac08c0SLiam Girdwood return 0; 1126*83ac08c0SLiam Girdwood } 1127*83ac08c0SLiam Girdwood 1128*83ac08c0SLiam Girdwood static int wm9713_soc_suspend(struct platform_device *pdev, 1129*83ac08c0SLiam Girdwood pm_message_t state) 1130*83ac08c0SLiam Girdwood { 1131*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1132*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 1133*83ac08c0SLiam Girdwood 1134*83ac08c0SLiam Girdwood wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3cold); 1135*83ac08c0SLiam Girdwood return 0; 1136*83ac08c0SLiam Girdwood } 1137*83ac08c0SLiam Girdwood 1138*83ac08c0SLiam Girdwood static int wm9713_soc_resume(struct platform_device *pdev) 1139*83ac08c0SLiam Girdwood { 1140*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1141*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 1142*83ac08c0SLiam Girdwood struct wm9713_priv *wm9713 = codec->private_data; 1143*83ac08c0SLiam Girdwood int i, ret; 1144*83ac08c0SLiam Girdwood u16 *cache = codec->reg_cache; 1145*83ac08c0SLiam Girdwood 1146*83ac08c0SLiam Girdwood ret = wm9713_reset(codec, 1); 1147*83ac08c0SLiam Girdwood if (ret < 0) { 1148*83ac08c0SLiam Girdwood printk(KERN_ERR "could not reset AC97 codec\n"); 1149*83ac08c0SLiam Girdwood return ret; 1150*83ac08c0SLiam Girdwood } 1151*83ac08c0SLiam Girdwood 1152*83ac08c0SLiam Girdwood wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); 1153*83ac08c0SLiam Girdwood 1154*83ac08c0SLiam Girdwood /* do we need to re-start the PLL ? */ 1155*83ac08c0SLiam Girdwood if (wm9713->pll_out) 1156*83ac08c0SLiam Girdwood wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out); 1157*83ac08c0SLiam Girdwood 1158*83ac08c0SLiam Girdwood /* only synchronise the codec if warm reset failed */ 1159*83ac08c0SLiam Girdwood if (ret == 0) { 1160*83ac08c0SLiam Girdwood for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) { 1161*83ac08c0SLiam Girdwood if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID || 1162*83ac08c0SLiam Girdwood i == AC97_EXTENDED_MSTATUS || i > 0x66) 1163*83ac08c0SLiam Girdwood continue; 1164*83ac08c0SLiam Girdwood soc_ac97_ops.write(codec->ac97, i, cache[i>>1]); 1165*83ac08c0SLiam Girdwood } 1166*83ac08c0SLiam Girdwood } 1167*83ac08c0SLiam Girdwood 1168*83ac08c0SLiam Girdwood if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) 1169*83ac08c0SLiam Girdwood wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0); 1170*83ac08c0SLiam Girdwood 1171*83ac08c0SLiam Girdwood return ret; 1172*83ac08c0SLiam Girdwood } 1173*83ac08c0SLiam Girdwood 1174*83ac08c0SLiam Girdwood static int wm9713_soc_probe(struct platform_device *pdev) 1175*83ac08c0SLiam Girdwood { 1176*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1177*83ac08c0SLiam Girdwood struct snd_soc_codec *codec; 1178*83ac08c0SLiam Girdwood int ret = 0, reg; 1179*83ac08c0SLiam Girdwood 1180*83ac08c0SLiam Girdwood printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION); 1181*83ac08c0SLiam Girdwood 1182*83ac08c0SLiam Girdwood socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 1183*83ac08c0SLiam Girdwood if (socdev->codec == NULL) 1184*83ac08c0SLiam Girdwood return -ENOMEM; 1185*83ac08c0SLiam Girdwood codec = socdev->codec; 1186*83ac08c0SLiam Girdwood mutex_init(&codec->mutex); 1187*83ac08c0SLiam Girdwood 1188*83ac08c0SLiam Girdwood codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL); 1189*83ac08c0SLiam Girdwood if (codec->reg_cache == NULL) { 1190*83ac08c0SLiam Girdwood ret = -ENOMEM; 1191*83ac08c0SLiam Girdwood goto cache_err; 1192*83ac08c0SLiam Girdwood } 1193*83ac08c0SLiam Girdwood codec->reg_cache_size = sizeof(wm9713_reg); 1194*83ac08c0SLiam Girdwood codec->reg_cache_step = 2; 1195*83ac08c0SLiam Girdwood 1196*83ac08c0SLiam Girdwood codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL); 1197*83ac08c0SLiam Girdwood if (codec->private_data == NULL) { 1198*83ac08c0SLiam Girdwood ret = -ENOMEM; 1199*83ac08c0SLiam Girdwood goto priv_err; 1200*83ac08c0SLiam Girdwood } 1201*83ac08c0SLiam Girdwood 1202*83ac08c0SLiam Girdwood codec->name = "WM9713"; 1203*83ac08c0SLiam Girdwood codec->owner = THIS_MODULE; 1204*83ac08c0SLiam Girdwood codec->dai = wm9713_dai; 1205*83ac08c0SLiam Girdwood codec->num_dai = ARRAY_SIZE(wm9713_dai); 1206*83ac08c0SLiam Girdwood codec->write = ac97_write; 1207*83ac08c0SLiam Girdwood codec->read = ac97_read; 1208*83ac08c0SLiam Girdwood codec->dapm_event = wm9713_dapm_event; 1209*83ac08c0SLiam Girdwood INIT_LIST_HEAD(&codec->dapm_widgets); 1210*83ac08c0SLiam Girdwood INIT_LIST_HEAD(&codec->dapm_paths); 1211*83ac08c0SLiam Girdwood 1212*83ac08c0SLiam Girdwood ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); 1213*83ac08c0SLiam Girdwood if (ret < 0) 1214*83ac08c0SLiam Girdwood goto codec_err; 1215*83ac08c0SLiam Girdwood 1216*83ac08c0SLiam Girdwood /* register pcms */ 1217*83ac08c0SLiam Girdwood ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 1218*83ac08c0SLiam Girdwood if (ret < 0) 1219*83ac08c0SLiam Girdwood goto pcm_err; 1220*83ac08c0SLiam Girdwood 1221*83ac08c0SLiam Girdwood /* do a cold reset for the controller and then try 1222*83ac08c0SLiam Girdwood * a warm reset followed by an optional cold reset for codec */ 1223*83ac08c0SLiam Girdwood wm9713_reset(codec, 0); 1224*83ac08c0SLiam Girdwood ret = wm9713_reset(codec, 1); 1225*83ac08c0SLiam Girdwood if (ret < 0) { 1226*83ac08c0SLiam Girdwood printk(KERN_ERR "AC97 link error\n"); 1227*83ac08c0SLiam Girdwood goto reset_err; 1228*83ac08c0SLiam Girdwood } 1229*83ac08c0SLiam Girdwood 1230*83ac08c0SLiam Girdwood wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); 1231*83ac08c0SLiam Girdwood 1232*83ac08c0SLiam Girdwood /* unmute the adc - move to kcontrol */ 1233*83ac08c0SLiam Girdwood reg = ac97_read(codec, AC97_CD) & 0x7fff; 1234*83ac08c0SLiam Girdwood ac97_write(codec, AC97_CD, reg); 1235*83ac08c0SLiam Girdwood 1236*83ac08c0SLiam Girdwood wm9713_add_controls(codec); 1237*83ac08c0SLiam Girdwood wm9713_add_widgets(codec); 1238*83ac08c0SLiam Girdwood ret = snd_soc_register_card(socdev); 1239*83ac08c0SLiam Girdwood if (ret < 0) 1240*83ac08c0SLiam Girdwood goto reset_err; 1241*83ac08c0SLiam Girdwood return 0; 1242*83ac08c0SLiam Girdwood 1243*83ac08c0SLiam Girdwood reset_err: 1244*83ac08c0SLiam Girdwood snd_soc_free_pcms(socdev); 1245*83ac08c0SLiam Girdwood 1246*83ac08c0SLiam Girdwood pcm_err: 1247*83ac08c0SLiam Girdwood snd_soc_free_ac97_codec(codec); 1248*83ac08c0SLiam Girdwood 1249*83ac08c0SLiam Girdwood codec_err: 1250*83ac08c0SLiam Girdwood kfree(codec->private_data); 1251*83ac08c0SLiam Girdwood 1252*83ac08c0SLiam Girdwood priv_err: 1253*83ac08c0SLiam Girdwood kfree(codec->reg_cache); 1254*83ac08c0SLiam Girdwood 1255*83ac08c0SLiam Girdwood cache_err: 1256*83ac08c0SLiam Girdwood kfree(socdev->codec); 1257*83ac08c0SLiam Girdwood socdev->codec = NULL; 1258*83ac08c0SLiam Girdwood return ret; 1259*83ac08c0SLiam Girdwood } 1260*83ac08c0SLiam Girdwood 1261*83ac08c0SLiam Girdwood static int wm9713_soc_remove(struct platform_device *pdev) 1262*83ac08c0SLiam Girdwood { 1263*83ac08c0SLiam Girdwood struct snd_soc_device *socdev = platform_get_drvdata(pdev); 1264*83ac08c0SLiam Girdwood struct snd_soc_codec *codec = socdev->codec; 1265*83ac08c0SLiam Girdwood 1266*83ac08c0SLiam Girdwood if (codec == NULL) 1267*83ac08c0SLiam Girdwood return 0; 1268*83ac08c0SLiam Girdwood 1269*83ac08c0SLiam Girdwood snd_soc_dapm_free(socdev); 1270*83ac08c0SLiam Girdwood snd_soc_free_pcms(socdev); 1271*83ac08c0SLiam Girdwood snd_soc_free_ac97_codec(codec); 1272*83ac08c0SLiam Girdwood kfree(codec->private_data); 1273*83ac08c0SLiam Girdwood kfree(codec->reg_cache); 1274*83ac08c0SLiam Girdwood kfree(codec->dai); 1275*83ac08c0SLiam Girdwood kfree(codec); 1276*83ac08c0SLiam Girdwood return 0; 1277*83ac08c0SLiam Girdwood } 1278*83ac08c0SLiam Girdwood 1279*83ac08c0SLiam Girdwood struct snd_soc_codec_device soc_codec_dev_wm9713 = { 1280*83ac08c0SLiam Girdwood .probe = wm9713_soc_probe, 1281*83ac08c0SLiam Girdwood .remove = wm9713_soc_remove, 1282*83ac08c0SLiam Girdwood .suspend = wm9713_soc_suspend, 1283*83ac08c0SLiam Girdwood .resume = wm9713_soc_resume, 1284*83ac08c0SLiam Girdwood }; 1285*83ac08c0SLiam Girdwood EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713); 1286*83ac08c0SLiam Girdwood 1287*83ac08c0SLiam Girdwood MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver"); 1288*83ac08c0SLiam Girdwood MODULE_AUTHOR("Liam Girdwood"); 1289*83ac08c0SLiam Girdwood MODULE_LICENSE("GPL"); 1290