1f2644a2cSMark Brown /* 2f2644a2cSMark Brown * wm8960.c -- WM8960 ALSA SoC Audio driver 3f2644a2cSMark Brown * 4656baaebSMark Brown * Copyright 2007-11 Wolfson Microelectronics, plc 5656baaebSMark Brown * 6f2644a2cSMark Brown * Author: Liam Girdwood 7f2644a2cSMark Brown * 8f2644a2cSMark Brown * This program is free software; you can redistribute it and/or modify 9f2644a2cSMark Brown * it under the terms of the GNU General Public License version 2 as 10f2644a2cSMark Brown * published by the Free Software Foundation. 11f2644a2cSMark Brown */ 12f2644a2cSMark Brown 13f2644a2cSMark Brown #include <linux/module.h> 14f2644a2cSMark Brown #include <linux/moduleparam.h> 15f2644a2cSMark Brown #include <linux/init.h> 16f2644a2cSMark Brown #include <linux/delay.h> 17f2644a2cSMark Brown #include <linux/pm.h> 18f2644a2cSMark Brown #include <linux/i2c.h> 195a0e3ad6STejun Heo #include <linux/slab.h> 20f2644a2cSMark Brown #include <sound/core.h> 21f2644a2cSMark Brown #include <sound/pcm.h> 22f2644a2cSMark Brown #include <sound/pcm_params.h> 23f2644a2cSMark Brown #include <sound/soc.h> 24f2644a2cSMark Brown #include <sound/initval.h> 25f2644a2cSMark Brown #include <sound/tlv.h> 26b6877a47SMark Brown #include <sound/wm8960.h> 27f2644a2cSMark Brown 28f2644a2cSMark Brown #include "wm8960.h" 29f2644a2cSMark Brown 30f2644a2cSMark Brown /* R25 - Power 1 */ 31913d7b4cSMark Brown #define WM8960_VMID_MASK 0x180 32f2644a2cSMark Brown #define WM8960_VREF 0x40 33f2644a2cSMark Brown 34913d7b4cSMark Brown /* R26 - Power 2 */ 35913d7b4cSMark Brown #define WM8960_PWR2_LOUT1 0x40 36913d7b4cSMark Brown #define WM8960_PWR2_ROUT1 0x20 37913d7b4cSMark Brown #define WM8960_PWR2_OUT3 0x02 38913d7b4cSMark Brown 39f2644a2cSMark Brown /* R28 - Anti-pop 1 */ 40f2644a2cSMark Brown #define WM8960_POBCTRL 0x80 41f2644a2cSMark Brown #define WM8960_BUFDCOPEN 0x10 42f2644a2cSMark Brown #define WM8960_BUFIOEN 0x08 43f2644a2cSMark Brown #define WM8960_SOFT_ST 0x04 44f2644a2cSMark Brown #define WM8960_HPSTBY 0x01 45f2644a2cSMark Brown 46f2644a2cSMark Brown /* R29 - Anti-pop 2 */ 47f2644a2cSMark Brown #define WM8960_DISOP 0x40 48913d7b4cSMark Brown #define WM8960_DRES_MASK 0x30 49f2644a2cSMark Brown 50f2644a2cSMark Brown /* 51f2644a2cSMark Brown * wm8960 register cache 52f2644a2cSMark Brown * We can't read the WM8960 register space when we are 53f2644a2cSMark Brown * using 2 wire for device control, so we cache them instead. 54f2644a2cSMark Brown */ 550ebe36c6SMark Brown static const struct reg_default wm8960_reg_defaults[] = { 56b3df026eSMark Brown { 0x0, 0x00a7 }, 57b3df026eSMark Brown { 0x1, 0x00a7 }, 580ebe36c6SMark Brown { 0x2, 0x0000 }, 590ebe36c6SMark Brown { 0x3, 0x0000 }, 600ebe36c6SMark Brown { 0x4, 0x0000 }, 610ebe36c6SMark Brown { 0x5, 0x0008 }, 620ebe36c6SMark Brown { 0x6, 0x0000 }, 630ebe36c6SMark Brown { 0x7, 0x000a }, 640ebe36c6SMark Brown { 0x8, 0x01c0 }, 650ebe36c6SMark Brown { 0x9, 0x0000 }, 660ebe36c6SMark Brown { 0xa, 0x00ff }, 670ebe36c6SMark Brown { 0xb, 0x00ff }, 680ebe36c6SMark Brown 690ebe36c6SMark Brown { 0x10, 0x0000 }, 700ebe36c6SMark Brown { 0x11, 0x007b }, 710ebe36c6SMark Brown { 0x12, 0x0100 }, 720ebe36c6SMark Brown { 0x13, 0x0032 }, 730ebe36c6SMark Brown { 0x14, 0x0000 }, 740ebe36c6SMark Brown { 0x15, 0x00c3 }, 750ebe36c6SMark Brown { 0x16, 0x00c3 }, 760ebe36c6SMark Brown { 0x17, 0x01c0 }, 770ebe36c6SMark Brown { 0x18, 0x0000 }, 780ebe36c6SMark Brown { 0x19, 0x0000 }, 790ebe36c6SMark Brown { 0x1a, 0x0000 }, 800ebe36c6SMark Brown { 0x1b, 0x0000 }, 810ebe36c6SMark Brown { 0x1c, 0x0000 }, 820ebe36c6SMark Brown { 0x1d, 0x0000 }, 830ebe36c6SMark Brown 840ebe36c6SMark Brown { 0x20, 0x0100 }, 850ebe36c6SMark Brown { 0x21, 0x0100 }, 860ebe36c6SMark Brown { 0x22, 0x0050 }, 870ebe36c6SMark Brown 880ebe36c6SMark Brown { 0x25, 0x0050 }, 890ebe36c6SMark Brown { 0x26, 0x0000 }, 900ebe36c6SMark Brown { 0x27, 0x0000 }, 910ebe36c6SMark Brown { 0x28, 0x0000 }, 920ebe36c6SMark Brown { 0x29, 0x0000 }, 930ebe36c6SMark Brown { 0x2a, 0x0040 }, 940ebe36c6SMark Brown { 0x2b, 0x0000 }, 950ebe36c6SMark Brown { 0x2c, 0x0000 }, 960ebe36c6SMark Brown { 0x2d, 0x0050 }, 970ebe36c6SMark Brown { 0x2e, 0x0050 }, 980ebe36c6SMark Brown { 0x2f, 0x0000 }, 990ebe36c6SMark Brown { 0x30, 0x0002 }, 1000ebe36c6SMark Brown { 0x31, 0x0037 }, 1010ebe36c6SMark Brown 1020ebe36c6SMark Brown { 0x33, 0x0080 }, 1030ebe36c6SMark Brown { 0x34, 0x0008 }, 1040ebe36c6SMark Brown { 0x35, 0x0031 }, 1050ebe36c6SMark Brown { 0x36, 0x0026 }, 1060ebe36c6SMark Brown { 0x37, 0x00e9 }, 107f2644a2cSMark Brown }; 108f2644a2cSMark Brown 1090ebe36c6SMark Brown static bool wm8960_volatile(struct device *dev, unsigned int reg) 1100ebe36c6SMark Brown { 1110ebe36c6SMark Brown switch (reg) { 1120ebe36c6SMark Brown case WM8960_RESET: 1130ebe36c6SMark Brown return true; 1140ebe36c6SMark Brown default: 1150ebe36c6SMark Brown return false; 1160ebe36c6SMark Brown } 1170ebe36c6SMark Brown } 1180ebe36c6SMark Brown 119f2644a2cSMark Brown struct wm8960_priv { 1200ebe36c6SMark Brown struct regmap *regmap; 121f0fba2adSLiam Girdwood int (*set_bias_level)(struct snd_soc_codec *, 122f0fba2adSLiam Girdwood enum snd_soc_bias_level level); 123913d7b4cSMark Brown struct snd_soc_dapm_widget *lout1; 124913d7b4cSMark Brown struct snd_soc_dapm_widget *rout1; 125913d7b4cSMark Brown struct snd_soc_dapm_widget *out3; 126afd6d36aSMark Brown bool deemph; 127afd6d36aSMark Brown int playback_fs; 128f2644a2cSMark Brown }; 129f2644a2cSMark Brown 13017a52fd6SMark Brown #define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) 131f2644a2cSMark Brown 132f2644a2cSMark Brown /* enumerated controls */ 133f2644a2cSMark Brown static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", 134f2644a2cSMark Brown "Right Inverted", "Stereo Inversion"}; 135f2644a2cSMark Brown static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; 136f2644a2cSMark Brown static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"}; 137f2644a2cSMark Brown static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; 138f2644a2cSMark Brown static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; 139f2644a2cSMark Brown 140f2644a2cSMark Brown static const struct soc_enum wm8960_enum[] = { 141f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), 142f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), 143f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), 144f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff), 145f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc), 146f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), 147f2644a2cSMark Brown }; 148f2644a2cSMark Brown 149afd6d36aSMark Brown static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; 150afd6d36aSMark Brown 151afd6d36aSMark Brown static int wm8960_set_deemph(struct snd_soc_codec *codec) 152afd6d36aSMark Brown { 153afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 154afd6d36aSMark Brown int val, i, best; 155afd6d36aSMark Brown 156afd6d36aSMark Brown /* If we're using deemphasis select the nearest available sample 157afd6d36aSMark Brown * rate. 158afd6d36aSMark Brown */ 159afd6d36aSMark Brown if (wm8960->deemph) { 160afd6d36aSMark Brown best = 1; 161afd6d36aSMark Brown for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { 162afd6d36aSMark Brown if (abs(deemph_settings[i] - wm8960->playback_fs) < 163afd6d36aSMark Brown abs(deemph_settings[best] - wm8960->playback_fs)) 164afd6d36aSMark Brown best = i; 165afd6d36aSMark Brown } 166afd6d36aSMark Brown 167afd6d36aSMark Brown val = best << 1; 168afd6d36aSMark Brown } else { 169afd6d36aSMark Brown val = 0; 170afd6d36aSMark Brown } 171afd6d36aSMark Brown 172afd6d36aSMark Brown dev_dbg(codec->dev, "Set deemphasis %d\n", val); 173afd6d36aSMark Brown 174afd6d36aSMark Brown return snd_soc_update_bits(codec, WM8960_DACCTL1, 175afd6d36aSMark Brown 0x6, val); 176afd6d36aSMark Brown } 177afd6d36aSMark Brown 178afd6d36aSMark Brown static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, 179afd6d36aSMark Brown struct snd_ctl_elem_value *ucontrol) 180afd6d36aSMark Brown { 181afd6d36aSMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 182afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 183afd6d36aSMark Brown 1843f343f85SDmitry Artamonow ucontrol->value.enumerated.item[0] = wm8960->deemph; 1853f343f85SDmitry Artamonow return 0; 186afd6d36aSMark Brown } 187afd6d36aSMark Brown 188afd6d36aSMark Brown static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, 189afd6d36aSMark Brown struct snd_ctl_elem_value *ucontrol) 190afd6d36aSMark Brown { 191afd6d36aSMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 192afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 193afd6d36aSMark Brown int deemph = ucontrol->value.enumerated.item[0]; 194afd6d36aSMark Brown 195afd6d36aSMark Brown if (deemph > 1) 196afd6d36aSMark Brown return -EINVAL; 197afd6d36aSMark Brown 198afd6d36aSMark Brown wm8960->deemph = deemph; 199afd6d36aSMark Brown 200afd6d36aSMark Brown return wm8960_set_deemph(codec); 201afd6d36aSMark Brown } 202afd6d36aSMark Brown 203f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); 204f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); 205f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); 206f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 207f2644a2cSMark Brown 208f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = { 209f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, 210f2644a2cSMark Brown 0, 63, 0, adc_tlv), 211f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 212f2644a2cSMark Brown 6, 1, 0), 213f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 214f2644a2cSMark Brown 7, 1, 0), 215f2644a2cSMark Brown 216f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 217f2644a2cSMark Brown 0, 255, 0, dac_tlv), 218f2644a2cSMark Brown 219f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, 220f2644a2cSMark Brown 0, 127, 0, out_tlv), 221f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1, 222f2644a2cSMark Brown 7, 1, 0), 223f2644a2cSMark Brown 224f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, 225f2644a2cSMark Brown 0, 127, 0, out_tlv), 226f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2, 227f2644a2cSMark Brown 7, 1, 0), 228f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), 229f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), 230f2644a2cSMark Brown 231f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), 2324faaa8d9SMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[0]), 233f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), 234f2644a2cSMark Brown 235f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]), 236afd6d36aSMark Brown SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, 237afd6d36aSMark Brown wm8960_get_deemph, wm8960_put_deemph), 238f2644a2cSMark Brown 2394faaa8d9SMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), 2404faaa8d9SMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), 241f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), 242f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), 243f2644a2cSMark Brown 2444faaa8d9SMark Brown SOC_ENUM("ALC Function", wm8960_enum[4]), 245f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), 246f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), 247f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), 248f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), 2494faaa8d9SMark Brown SOC_ENUM("ALC Mode", wm8960_enum[5]), 250f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), 251f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), 252f2644a2cSMark Brown 253f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), 254f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), 255f2644a2cSMark Brown 256f2644a2cSMark Brown SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH, 257f2644a2cSMark Brown 0, 127, 0), 258f2644a2cSMark Brown 259f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", 260f2644a2cSMark Brown WM8960_BYPASS1, 4, 7, 1, bypass_tlv), 261f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume", 262f2644a2cSMark Brown WM8960_LOUTMIX, 4, 7, 1, bypass_tlv), 263f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume", 264f2644a2cSMark Brown WM8960_BYPASS2, 4, 7, 1, bypass_tlv), 265f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", 266f2644a2cSMark Brown WM8960_ROUTMIX, 4, 7, 1, bypass_tlv), 267f2644a2cSMark Brown }; 268f2644a2cSMark Brown 269f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = { 270f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0), 271f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0), 272f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0), 273f2644a2cSMark Brown }; 274f2644a2cSMark Brown 275f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = { 276f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0), 277f2644a2cSMark Brown }; 278f2644a2cSMark Brown 279f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = { 280f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0), 281f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0), 282f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0), 283f2644a2cSMark Brown }; 284f2644a2cSMark Brown 285f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = { 286f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0), 287f2644a2cSMark Brown }; 288f2644a2cSMark Brown 289f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = { 290f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0), 291f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0), 292f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0), 293f2644a2cSMark Brown }; 294f2644a2cSMark Brown 295f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = { 296f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0), 297f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0), 298f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0), 299f2644a2cSMark Brown }; 300f2644a2cSMark Brown 301f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = { 302f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0), 303f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0), 304f2644a2cSMark Brown }; 305f2644a2cSMark Brown 306f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = { 307f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"), 308f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"), 309f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"), 310f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"), 311f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"), 312f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"), 313f2644a2cSMark Brown 314187774cbSMark Brown SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), 315f2644a2cSMark Brown 316f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0, 317f2644a2cSMark Brown wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)), 318f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0, 319f2644a2cSMark Brown wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)), 320f2644a2cSMark Brown 321f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, 322f2644a2cSMark Brown wm8960_lin, ARRAY_SIZE(wm8960_lin)), 323f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0, 324f2644a2cSMark Brown wm8960_rin, ARRAY_SIZE(wm8960_rin)), 325f2644a2cSMark Brown 326f2644a2cSMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0), 327f2644a2cSMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0), 328f2644a2cSMark Brown 329f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0), 330f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0), 331f2644a2cSMark Brown 332f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0, 333f2644a2cSMark Brown &wm8960_loutput_mixer[0], 334f2644a2cSMark Brown ARRAY_SIZE(wm8960_loutput_mixer)), 335f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0, 336f2644a2cSMark Brown &wm8960_routput_mixer[0], 337f2644a2cSMark Brown ARRAY_SIZE(wm8960_routput_mixer)), 338f2644a2cSMark Brown 339f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), 340f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), 341f2644a2cSMark Brown 342f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0), 343f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0), 344f2644a2cSMark Brown 345f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0), 346f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0), 347f2644a2cSMark Brown 348f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"), 349f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"), 350f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"), 351f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"), 352f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"), 353f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"), 354f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"), 355f2644a2cSMark Brown }; 356f2644a2cSMark Brown 357913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = { 358913d7b4cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0, 359913d7b4cSMark Brown &wm8960_mono_out[0], 360913d7b4cSMark Brown ARRAY_SIZE(wm8960_mono_out)), 361913d7b4cSMark Brown }; 362913d7b4cSMark Brown 363913d7b4cSMark Brown /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */ 364913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = { 365913d7b4cSMark Brown SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0), 366913d7b4cSMark Brown }; 367913d7b4cSMark Brown 368f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = { 369f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, 370f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, 371f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, 372f2644a2cSMark Brown 373f2644a2cSMark Brown { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", }, 374f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */ 375f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT2" }, 376f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT3" }, 377f2644a2cSMark Brown 378f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" }, 379f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" }, 380f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" }, 381f2644a2cSMark Brown 382f2644a2cSMark Brown { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", }, 383f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */ 384f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT2" }, 385f2644a2cSMark Brown { "Right Input Mixer", NULL, "LINPUT3" }, 386f2644a2cSMark Brown 387f2644a2cSMark Brown { "Left ADC", NULL, "Left Input Mixer" }, 388f2644a2cSMark Brown { "Right ADC", NULL, "Right Input Mixer" }, 389f2644a2cSMark Brown 390f2644a2cSMark Brown { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" }, 391f2644a2cSMark Brown { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} , 392f2644a2cSMark Brown { "Left Output Mixer", "PCM Playback Switch", "Left DAC" }, 393f2644a2cSMark Brown 394f2644a2cSMark Brown { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" }, 395f2644a2cSMark Brown { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } , 396f2644a2cSMark Brown { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, 397f2644a2cSMark Brown 398f2644a2cSMark Brown { "LOUT1 PGA", NULL, "Left Output Mixer" }, 399f2644a2cSMark Brown { "ROUT1 PGA", NULL, "Right Output Mixer" }, 400f2644a2cSMark Brown 401f2644a2cSMark Brown { "HP_L", NULL, "LOUT1 PGA" }, 402f2644a2cSMark Brown { "HP_R", NULL, "ROUT1 PGA" }, 403f2644a2cSMark Brown 404f2644a2cSMark Brown { "Left Speaker PGA", NULL, "Left Output Mixer" }, 405f2644a2cSMark Brown { "Right Speaker PGA", NULL, "Right Output Mixer" }, 406f2644a2cSMark Brown 407f2644a2cSMark Brown { "Left Speaker Output", NULL, "Left Speaker PGA" }, 408f2644a2cSMark Brown { "Right Speaker Output", NULL, "Right Speaker PGA" }, 409f2644a2cSMark Brown 410f2644a2cSMark Brown { "SPK_LN", NULL, "Left Speaker Output" }, 411f2644a2cSMark Brown { "SPK_LP", NULL, "Left Speaker Output" }, 412f2644a2cSMark Brown { "SPK_RN", NULL, "Right Speaker Output" }, 413f2644a2cSMark Brown { "SPK_RP", NULL, "Right Speaker Output" }, 414913d7b4cSMark Brown }; 415913d7b4cSMark Brown 416913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_out3[] = { 417913d7b4cSMark Brown { "Mono Output Mixer", "Left Switch", "Left Output Mixer" }, 418913d7b4cSMark Brown { "Mono Output Mixer", "Right Switch", "Right Output Mixer" }, 419f2644a2cSMark Brown 420f2644a2cSMark Brown { "OUT3", NULL, "Mono Output Mixer", } 421f2644a2cSMark Brown }; 422f2644a2cSMark Brown 423913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_capless[] = { 424913d7b4cSMark Brown { "HP_L", NULL, "OUT3 VMID" }, 425913d7b4cSMark Brown { "HP_R", NULL, "OUT3 VMID" }, 426913d7b4cSMark Brown 427913d7b4cSMark Brown { "OUT3 VMID", NULL, "Left Output Mixer" }, 428913d7b4cSMark Brown { "OUT3 VMID", NULL, "Right Output Mixer" }, 429913d7b4cSMark Brown }; 430913d7b4cSMark Brown 431f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec) 432f2644a2cSMark Brown { 433913d7b4cSMark Brown struct wm8960_data *pdata = codec->dev->platform_data; 434b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 435ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 436913d7b4cSMark Brown struct snd_soc_dapm_widget *w; 437913d7b4cSMark Brown 438ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, 439f2644a2cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets)); 440f2644a2cSMark Brown 441ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); 442f2644a2cSMark Brown 443913d7b4cSMark Brown /* In capless mode OUT3 is used to provide VMID for the 444913d7b4cSMark Brown * headphone outputs, otherwise it is used as a mono mixer. 445913d7b4cSMark Brown */ 446913d7b4cSMark Brown if (pdata && pdata->capless) { 447ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, 448913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_capless)); 449913d7b4cSMark Brown 450ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_capless, 451913d7b4cSMark Brown ARRAY_SIZE(audio_paths_capless)); 452913d7b4cSMark Brown } else { 453ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, 454913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_out3)); 455913d7b4cSMark Brown 456ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_out3, 457913d7b4cSMark Brown ARRAY_SIZE(audio_paths_out3)); 458913d7b4cSMark Brown } 459913d7b4cSMark Brown 460913d7b4cSMark Brown /* We need to power up the headphone output stage out of 461913d7b4cSMark Brown * sequence for capless mode. To save scanning the widget 462913d7b4cSMark Brown * list each time to find the desired power state do so now 463913d7b4cSMark Brown * and save the result. 464913d7b4cSMark Brown */ 46597c866deSJarkko Nikula list_for_each_entry(w, &codec->card->widgets, list) { 46697c866deSJarkko Nikula if (w->dapm != &codec->dapm) 46797c866deSJarkko Nikula continue; 468913d7b4cSMark Brown if (strcmp(w->name, "LOUT1 PGA") == 0) 469913d7b4cSMark Brown wm8960->lout1 = w; 470913d7b4cSMark Brown if (strcmp(w->name, "ROUT1 PGA") == 0) 471913d7b4cSMark Brown wm8960->rout1 = w; 472913d7b4cSMark Brown if (strcmp(w->name, "OUT3 VMID") == 0) 473913d7b4cSMark Brown wm8960->out3 = w; 474913d7b4cSMark Brown } 475913d7b4cSMark Brown 476f2644a2cSMark Brown return 0; 477f2644a2cSMark Brown } 478f2644a2cSMark Brown 479f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, 480f2644a2cSMark Brown unsigned int fmt) 481f2644a2cSMark Brown { 482f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 483f2644a2cSMark Brown u16 iface = 0; 484f2644a2cSMark Brown 485f2644a2cSMark Brown /* set master/slave audio interface */ 486f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 487f2644a2cSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 488f2644a2cSMark Brown iface |= 0x0040; 489f2644a2cSMark Brown break; 490f2644a2cSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 491f2644a2cSMark Brown break; 492f2644a2cSMark Brown default: 493f2644a2cSMark Brown return -EINVAL; 494f2644a2cSMark Brown } 495f2644a2cSMark Brown 496f2644a2cSMark Brown /* interface format */ 497f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 498f2644a2cSMark Brown case SND_SOC_DAIFMT_I2S: 499f2644a2cSMark Brown iface |= 0x0002; 500f2644a2cSMark Brown break; 501f2644a2cSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 502f2644a2cSMark Brown break; 503f2644a2cSMark Brown case SND_SOC_DAIFMT_LEFT_J: 504f2644a2cSMark Brown iface |= 0x0001; 505f2644a2cSMark Brown break; 506f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_A: 507f2644a2cSMark Brown iface |= 0x0003; 508f2644a2cSMark Brown break; 509f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_B: 510f2644a2cSMark Brown iface |= 0x0013; 511f2644a2cSMark Brown break; 512f2644a2cSMark Brown default: 513f2644a2cSMark Brown return -EINVAL; 514f2644a2cSMark Brown } 515f2644a2cSMark Brown 516f2644a2cSMark Brown /* clock inversion */ 517f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 518f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_NF: 519f2644a2cSMark Brown break; 520f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_IF: 521f2644a2cSMark Brown iface |= 0x0090; 522f2644a2cSMark Brown break; 523f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_NF: 524f2644a2cSMark Brown iface |= 0x0080; 525f2644a2cSMark Brown break; 526f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_IF: 527f2644a2cSMark Brown iface |= 0x0010; 528f2644a2cSMark Brown break; 529f2644a2cSMark Brown default: 530f2644a2cSMark Brown return -EINVAL; 531f2644a2cSMark Brown } 532f2644a2cSMark Brown 533f2644a2cSMark Brown /* set iface */ 53417a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 535f2644a2cSMark Brown return 0; 536f2644a2cSMark Brown } 537f2644a2cSMark Brown 538db059c0fSMark Brown static struct { 539db059c0fSMark Brown int rate; 540db059c0fSMark Brown unsigned int val; 541db059c0fSMark Brown } alc_rates[] = { 542db059c0fSMark Brown { 48000, 0 }, 543db059c0fSMark Brown { 44100, 0 }, 544db059c0fSMark Brown { 32000, 1 }, 545db059c0fSMark Brown { 22050, 2 }, 546db059c0fSMark Brown { 24000, 2 }, 547db059c0fSMark Brown { 16000, 3 }, 548db059c0fSMark Brown { 11250, 4 }, 549db059c0fSMark Brown { 12000, 4 }, 550db059c0fSMark Brown { 8000, 5 }, 551db059c0fSMark Brown }; 552db059c0fSMark Brown 553f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream, 554f2644a2cSMark Brown struct snd_pcm_hw_params *params, 555f2644a2cSMark Brown struct snd_soc_dai *dai) 556f2644a2cSMark Brown { 557e6968a17SMark Brown struct snd_soc_codec *codec = dai->codec; 558afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 55917a52fd6SMark Brown u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; 5604c2474c0STimur Tabi snd_pcm_format_t format = params_format(params); 561db059c0fSMark Brown int i; 562f2644a2cSMark Brown 563f2644a2cSMark Brown /* bit size */ 5644c2474c0STimur Tabi switch (format) { 565f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S16_LE: 5664c2474c0STimur Tabi case SNDRV_PCM_FORMAT_S16_BE: 567f2644a2cSMark Brown break; 568f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S20_3LE: 5694c2474c0STimur Tabi case SNDRV_PCM_FORMAT_S20_3BE: 570f2644a2cSMark Brown iface |= 0x0004; 571f2644a2cSMark Brown break; 572f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S24_LE: 5734c2474c0STimur Tabi case SNDRV_PCM_FORMAT_S24_BE: 574f2644a2cSMark Brown iface |= 0x0008; 575f2644a2cSMark Brown break; 5764c2474c0STimur Tabi default: 5774c2474c0STimur Tabi dev_err(codec->dev, "unsupported format %i\n", format); 5784c2474c0STimur Tabi return -EINVAL; 579f2644a2cSMark Brown } 580f2644a2cSMark Brown 581afd6d36aSMark Brown /* Update filters for the new rate */ 582afd6d36aSMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 583afd6d36aSMark Brown wm8960->playback_fs = params_rate(params); 584afd6d36aSMark Brown wm8960_set_deemph(codec); 585db059c0fSMark Brown } else { 586db059c0fSMark Brown for (i = 0; i < ARRAY_SIZE(alc_rates); i++) 587db059c0fSMark Brown if (alc_rates[i].rate == params_rate(params)) 588db059c0fSMark Brown snd_soc_update_bits(codec, 589db059c0fSMark Brown WM8960_ADDCTL3, 0x7, 590db059c0fSMark Brown alc_rates[i].val); 591afd6d36aSMark Brown } 592afd6d36aSMark Brown 593f2644a2cSMark Brown /* set iface */ 59417a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 595f2644a2cSMark Brown return 0; 596f2644a2cSMark Brown } 597f2644a2cSMark Brown 598f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute) 599f2644a2cSMark Brown { 600f2644a2cSMark Brown struct snd_soc_codec *codec = dai->codec; 601f2644a2cSMark Brown 602f2644a2cSMark Brown if (mute) 60316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); 604f2644a2cSMark Brown else 60516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); 606f2644a2cSMark Brown return 0; 607f2644a2cSMark Brown } 608f2644a2cSMark Brown 609913d7b4cSMark Brown static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, 610f2644a2cSMark Brown enum snd_soc_bias_level level) 611f2644a2cSMark Brown { 6120ebe36c6SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 6130ebe36c6SMark Brown 614f2644a2cSMark Brown switch (level) { 615f2644a2cSMark Brown case SND_SOC_BIAS_ON: 616f2644a2cSMark Brown break; 617f2644a2cSMark Brown 618f2644a2cSMark Brown case SND_SOC_BIAS_PREPARE: 619f2644a2cSMark Brown /* Set VMID to 2x50k */ 62016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); 621f2644a2cSMark Brown break; 622f2644a2cSMark Brown 623f2644a2cSMark Brown case SND_SOC_BIAS_STANDBY: 624ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 6250ebe36c6SMark Brown regcache_sync(wm8960->regmap); 626bc45df2dSAxel Lin 627f2644a2cSMark Brown /* Enable anti-pop features */ 62817a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 629f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 630f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 631f2644a2cSMark Brown 632f2644a2cSMark Brown /* Enable & ramp VMID at 2x50k */ 63316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); 634f2644a2cSMark Brown msleep(100); 635f2644a2cSMark Brown 636f2644a2cSMark Brown /* Enable VREF */ 63716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, 63816b24881SAxel Lin WM8960_VREF); 639f2644a2cSMark Brown 640f2644a2cSMark Brown /* Disable anti-pop features */ 64117a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); 642f2644a2cSMark Brown } 643f2644a2cSMark Brown 644f2644a2cSMark Brown /* Set VMID to 2x250k */ 64516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); 646f2644a2cSMark Brown break; 647f2644a2cSMark Brown 648f2644a2cSMark Brown case SND_SOC_BIAS_OFF: 649f2644a2cSMark Brown /* Enable anti-pop features */ 65017a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 651f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 652f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 653f2644a2cSMark Brown 654f2644a2cSMark Brown /* Disable VMID and VREF, let them discharge */ 65517a52fd6SMark Brown snd_soc_write(codec, WM8960_POWER1, 0); 656f2644a2cSMark Brown msleep(600); 657913d7b4cSMark Brown break; 658913d7b4cSMark Brown } 659f2644a2cSMark Brown 660ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 661913d7b4cSMark Brown 662913d7b4cSMark Brown return 0; 663913d7b4cSMark Brown } 664913d7b4cSMark Brown 665913d7b4cSMark Brown static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, 666913d7b4cSMark Brown enum snd_soc_bias_level level) 667913d7b4cSMark Brown { 668b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 669913d7b4cSMark Brown int reg; 670913d7b4cSMark Brown 671913d7b4cSMark Brown switch (level) { 672913d7b4cSMark Brown case SND_SOC_BIAS_ON: 673913d7b4cSMark Brown break; 674913d7b4cSMark Brown 675913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 676ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 677913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 678913d7b4cSMark Brown /* Enable anti pop mode */ 679913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 680913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 681913d7b4cSMark Brown WM8960_BUFDCOPEN, 682913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 683913d7b4cSMark Brown WM8960_BUFDCOPEN); 684913d7b4cSMark Brown 685913d7b4cSMark Brown /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ 686913d7b4cSMark Brown reg = 0; 687913d7b4cSMark Brown if (wm8960->lout1 && wm8960->lout1->power) 688913d7b4cSMark Brown reg |= WM8960_PWR2_LOUT1; 689913d7b4cSMark Brown if (wm8960->rout1 && wm8960->rout1->power) 690913d7b4cSMark Brown reg |= WM8960_PWR2_ROUT1; 691913d7b4cSMark Brown if (wm8960->out3 && wm8960->out3->power) 692913d7b4cSMark Brown reg |= WM8960_PWR2_OUT3; 693913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER2, 694913d7b4cSMark Brown WM8960_PWR2_LOUT1 | 695913d7b4cSMark Brown WM8960_PWR2_ROUT1 | 696913d7b4cSMark Brown WM8960_PWR2_OUT3, reg); 697913d7b4cSMark Brown 698913d7b4cSMark Brown /* Enable VMID at 2*50k */ 699913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 700913d7b4cSMark Brown WM8960_VMID_MASK, 0x80); 701913d7b4cSMark Brown 702913d7b4cSMark Brown /* Ramp */ 703913d7b4cSMark Brown msleep(100); 704913d7b4cSMark Brown 705913d7b4cSMark Brown /* Enable VREF */ 706913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 707913d7b4cSMark Brown WM8960_VREF, WM8960_VREF); 708913d7b4cSMark Brown 709913d7b4cSMark Brown msleep(100); 710913d7b4cSMark Brown break; 711913d7b4cSMark Brown 712913d7b4cSMark Brown case SND_SOC_BIAS_ON: 713913d7b4cSMark Brown /* Enable anti-pop mode */ 714913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 715913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 716913d7b4cSMark Brown WM8960_BUFDCOPEN, 717913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 718913d7b4cSMark Brown WM8960_BUFDCOPEN); 719913d7b4cSMark Brown 720913d7b4cSMark Brown /* Disable VMID and VREF */ 721913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 722913d7b4cSMark Brown WM8960_VREF | WM8960_VMID_MASK, 0); 723913d7b4cSMark Brown break; 724913d7b4cSMark Brown 725bc45df2dSAxel Lin case SND_SOC_BIAS_OFF: 7260ebe36c6SMark Brown regcache_sync(wm8960->regmap); 727bc45df2dSAxel Lin break; 728913d7b4cSMark Brown default: 729913d7b4cSMark Brown break; 730913d7b4cSMark Brown } 731913d7b4cSMark Brown break; 732913d7b4cSMark Brown 733913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 734ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 735913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 736913d7b4cSMark Brown /* Disable HP discharge */ 737913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP2, 738913d7b4cSMark Brown WM8960_DISOP | WM8960_DRES_MASK, 739913d7b4cSMark Brown 0); 740913d7b4cSMark Brown 741913d7b4cSMark Brown /* Disable anti-pop features */ 742913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 743913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 744913d7b4cSMark Brown WM8960_BUFDCOPEN, 745913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 746913d7b4cSMark Brown WM8960_BUFDCOPEN); 747913d7b4cSMark Brown break; 748913d7b4cSMark Brown 749913d7b4cSMark Brown default: 750913d7b4cSMark Brown break; 751913d7b4cSMark Brown } 752913d7b4cSMark Brown break; 753913d7b4cSMark Brown 754913d7b4cSMark Brown case SND_SOC_BIAS_OFF: 755f2644a2cSMark Brown break; 756f2644a2cSMark Brown } 757f2644a2cSMark Brown 758ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 759f2644a2cSMark Brown 760f2644a2cSMark Brown return 0; 761f2644a2cSMark Brown } 762f2644a2cSMark Brown 763f2644a2cSMark Brown /* PLL divisors */ 764f2644a2cSMark Brown struct _pll_div { 765f2644a2cSMark Brown u32 pre_div:1; 766f2644a2cSMark Brown u32 n:4; 767f2644a2cSMark Brown u32 k:24; 768f2644a2cSMark Brown }; 769f2644a2cSMark Brown 770f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10 771f2644a2cSMark Brown * to allow rounding later */ 772f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10) 773f2644a2cSMark Brown 774f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target, 775f2644a2cSMark Brown struct _pll_div *pll_div) 776f2644a2cSMark Brown { 777f2644a2cSMark Brown unsigned long long Kpart; 778f2644a2cSMark Brown unsigned int K, Ndiv, Nmod; 779f2644a2cSMark Brown 780f2644a2cSMark Brown pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target); 781f2644a2cSMark Brown 782f2644a2cSMark Brown /* Scale up target to PLL operating frequency */ 783f2644a2cSMark Brown target *= 4; 784f2644a2cSMark Brown 785f2644a2cSMark Brown Ndiv = target / source; 786f2644a2cSMark Brown if (Ndiv < 6) { 787f2644a2cSMark Brown source >>= 1; 788f2644a2cSMark Brown pll_div->pre_div = 1; 789f2644a2cSMark Brown Ndiv = target / source; 790f2644a2cSMark Brown } else 791f2644a2cSMark Brown pll_div->pre_div = 0; 792f2644a2cSMark Brown 793f2644a2cSMark Brown if ((Ndiv < 6) || (Ndiv > 12)) { 794f2644a2cSMark Brown pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); 795f2644a2cSMark Brown return -EINVAL; 796f2644a2cSMark Brown } 797f2644a2cSMark Brown 798f2644a2cSMark Brown pll_div->n = Ndiv; 799f2644a2cSMark Brown Nmod = target % source; 800f2644a2cSMark Brown Kpart = FIXED_PLL_SIZE * (long long)Nmod; 801f2644a2cSMark Brown 802f2644a2cSMark Brown do_div(Kpart, source); 803f2644a2cSMark Brown 804f2644a2cSMark Brown K = Kpart & 0xFFFFFFFF; 805f2644a2cSMark Brown 806f2644a2cSMark Brown /* Check if we need to round */ 807f2644a2cSMark Brown if ((K % 10) >= 5) 808f2644a2cSMark Brown K += 5; 809f2644a2cSMark Brown 810f2644a2cSMark Brown /* Move down to proper range now rounding is done */ 811f2644a2cSMark Brown K /= 10; 812f2644a2cSMark Brown 813f2644a2cSMark Brown pll_div->k = K; 814f2644a2cSMark Brown 815f2644a2cSMark Brown pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n", 816f2644a2cSMark Brown pll_div->n, pll_div->k, pll_div->pre_div); 817f2644a2cSMark Brown 818f2644a2cSMark Brown return 0; 819f2644a2cSMark Brown } 820f2644a2cSMark Brown 82185488037SMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 82285488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 823f2644a2cSMark Brown { 824f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 825f2644a2cSMark Brown u16 reg; 826f2644a2cSMark Brown static struct _pll_div pll_div; 827f2644a2cSMark Brown int ret; 828f2644a2cSMark Brown 829f2644a2cSMark Brown if (freq_in && freq_out) { 830f2644a2cSMark Brown ret = pll_factors(freq_in, freq_out, &pll_div); 831f2644a2cSMark Brown if (ret != 0) 832f2644a2cSMark Brown return ret; 833f2644a2cSMark Brown } 834f2644a2cSMark Brown 835f2644a2cSMark Brown /* Disable the PLL: even if we are changing the frequency the 836f2644a2cSMark Brown * PLL needs to be disabled while we do so. */ 83716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); 83816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); 839f2644a2cSMark Brown 840f2644a2cSMark Brown if (!freq_in || !freq_out) 841f2644a2cSMark Brown return 0; 842f2644a2cSMark Brown 84317a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; 844f2644a2cSMark Brown reg |= pll_div.pre_div << 4; 845f2644a2cSMark Brown reg |= pll_div.n; 846f2644a2cSMark Brown 847f2644a2cSMark Brown if (pll_div.k) { 848f2644a2cSMark Brown reg |= 0x20; 849f2644a2cSMark Brown 85017a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f); 85117a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff); 85217a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff); 853f2644a2cSMark Brown } 85417a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg); 855f2644a2cSMark Brown 856f2644a2cSMark Brown /* Turn it on */ 85716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); 858f2644a2cSMark Brown msleep(250); 85916b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); 860f2644a2cSMark Brown 861f2644a2cSMark Brown return 0; 862f2644a2cSMark Brown } 863f2644a2cSMark Brown 864f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 865f2644a2cSMark Brown int div_id, int div) 866f2644a2cSMark Brown { 867f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 868f2644a2cSMark Brown u16 reg; 869f2644a2cSMark Brown 870f2644a2cSMark Brown switch (div_id) { 871f2644a2cSMark Brown case WM8960_SYSCLKDIV: 87217a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; 87317a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 874f2644a2cSMark Brown break; 875f2644a2cSMark Brown case WM8960_DACDIV: 87617a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; 87717a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 878f2644a2cSMark Brown break; 879f2644a2cSMark Brown case WM8960_OPCLKDIV: 88017a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; 88117a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg | div); 882f2644a2cSMark Brown break; 883f2644a2cSMark Brown case WM8960_DCLKDIV: 88417a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; 88517a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK2, reg | div); 886f2644a2cSMark Brown break; 887f2644a2cSMark Brown case WM8960_TOCLKSEL: 88817a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; 88917a52fd6SMark Brown snd_soc_write(codec, WM8960_ADDCTL1, reg | div); 890f2644a2cSMark Brown break; 891f2644a2cSMark Brown default: 892f2644a2cSMark Brown return -EINVAL; 893f2644a2cSMark Brown } 894f2644a2cSMark Brown 895f2644a2cSMark Brown return 0; 896f2644a2cSMark Brown } 897f2644a2cSMark Brown 898f0fba2adSLiam Girdwood static int wm8960_set_bias_level(struct snd_soc_codec *codec, 899f0fba2adSLiam Girdwood enum snd_soc_bias_level level) 900f0fba2adSLiam Girdwood { 901f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 902f0fba2adSLiam Girdwood 903f0fba2adSLiam Girdwood return wm8960->set_bias_level(codec, level); 904f0fba2adSLiam Girdwood } 905f0fba2adSLiam Girdwood 906f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000 907f2644a2cSMark Brown 908f2644a2cSMark Brown #define WM8960_FORMATS \ 909f2644a2cSMark Brown (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 910f2644a2cSMark Brown SNDRV_PCM_FMTBIT_S24_LE) 911f2644a2cSMark Brown 91285e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8960_dai_ops = { 913f2644a2cSMark Brown .hw_params = wm8960_hw_params, 914f2644a2cSMark Brown .digital_mute = wm8960_mute, 915f2644a2cSMark Brown .set_fmt = wm8960_set_dai_fmt, 916f2644a2cSMark Brown .set_clkdiv = wm8960_set_dai_clkdiv, 917f2644a2cSMark Brown .set_pll = wm8960_set_dai_pll, 918f2644a2cSMark Brown }; 919f2644a2cSMark Brown 920f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8960_dai = { 921f0fba2adSLiam Girdwood .name = "wm8960-hifi", 922f2644a2cSMark Brown .playback = { 923f2644a2cSMark Brown .stream_name = "Playback", 924f2644a2cSMark Brown .channels_min = 1, 925f2644a2cSMark Brown .channels_max = 2, 926f2644a2cSMark Brown .rates = WM8960_RATES, 927f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 928f2644a2cSMark Brown .capture = { 929f2644a2cSMark Brown .stream_name = "Capture", 930f2644a2cSMark Brown .channels_min = 1, 931f2644a2cSMark Brown .channels_max = 2, 932f2644a2cSMark Brown .rates = WM8960_RATES, 933f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 934f2644a2cSMark Brown .ops = &wm8960_dai_ops, 935f2644a2cSMark Brown .symmetric_rates = 1, 936f2644a2cSMark Brown }; 937f2644a2cSMark Brown 93884b315eeSLars-Peter Clausen static int wm8960_suspend(struct snd_soc_codec *codec) 939f2644a2cSMark Brown { 940f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 941f2644a2cSMark Brown 942f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); 943f2644a2cSMark Brown return 0; 944f2644a2cSMark Brown } 945f2644a2cSMark Brown 946f0fba2adSLiam Girdwood static int wm8960_resume(struct snd_soc_codec *codec) 947f2644a2cSMark Brown { 948f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 949f2644a2cSMark Brown 950f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); 951f2644a2cSMark Brown return 0; 952f2644a2cSMark Brown } 953f2644a2cSMark Brown 954f0fba2adSLiam Girdwood static int wm8960_probe(struct snd_soc_codec *codec) 955f2644a2cSMark Brown { 956f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 957f0fba2adSLiam Girdwood struct wm8960_data *pdata = dev_get_platdata(codec->dev); 958f2644a2cSMark Brown int ret; 959f2644a2cSMark Brown 960f0fba2adSLiam Girdwood wm8960->set_bias_level = wm8960_set_bias_level_out3; 961913d7b4cSMark Brown 962f2644a2cSMark Brown if (!pdata) { 963f2644a2cSMark Brown dev_warn(codec->dev, "No platform data supplied\n"); 964f2644a2cSMark Brown } else { 965913d7b4cSMark Brown if (pdata->capless) 966f0fba2adSLiam Girdwood wm8960->set_bias_level = wm8960_set_bias_level_capless; 967f2644a2cSMark Brown } 968f2644a2cSMark Brown 9690ebe36c6SMark Brown ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP); 97017a52fd6SMark Brown if (ret < 0) { 97117a52fd6SMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 972f0fba2adSLiam Girdwood return ret; 97317a52fd6SMark Brown } 97417a52fd6SMark Brown 975f2644a2cSMark Brown ret = wm8960_reset(codec); 976f2644a2cSMark Brown if (ret < 0) { 977f2644a2cSMark Brown dev_err(codec->dev, "Failed to issue reset\n"); 978f0fba2adSLiam Girdwood return ret; 979f2644a2cSMark Brown } 980f2644a2cSMark Brown 981f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); 982f2644a2cSMark Brown 983f2644a2cSMark Brown /* Latch the update bits */ 98416b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100); 98516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100); 98616b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100); 98716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100); 98816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100); 98916b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100); 99016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100); 99116b24881SAxel Lin snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100); 99216b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100); 99316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100); 994f2644a2cSMark Brown 995022658beSLiam Girdwood snd_soc_add_codec_controls(codec, wm8960_snd_controls, 996f0fba2adSLiam Girdwood ARRAY_SIZE(wm8960_snd_controls)); 997f0fba2adSLiam Girdwood wm8960_add_widgets(codec); 998f2644a2cSMark Brown 999f2644a2cSMark Brown return 0; 1000f2644a2cSMark Brown } 1001f2644a2cSMark Brown 1002f0fba2adSLiam Girdwood /* power down chip */ 1003f0fba2adSLiam Girdwood static int wm8960_remove(struct snd_soc_codec *codec) 1004f2644a2cSMark Brown { 1005f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 1006f0fba2adSLiam Girdwood 1007f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); 1008f0fba2adSLiam Girdwood return 0; 1009f2644a2cSMark Brown } 1010f2644a2cSMark Brown 1011f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8960 = { 1012f0fba2adSLiam Girdwood .probe = wm8960_probe, 1013f0fba2adSLiam Girdwood .remove = wm8960_remove, 1014f0fba2adSLiam Girdwood .suspend = wm8960_suspend, 1015f0fba2adSLiam Girdwood .resume = wm8960_resume, 1016f0fba2adSLiam Girdwood .set_bias_level = wm8960_set_bias_level, 10170ebe36c6SMark Brown }; 10180ebe36c6SMark Brown 10190ebe36c6SMark Brown static const struct regmap_config wm8960_regmap = { 10200ebe36c6SMark Brown .reg_bits = 7, 10210ebe36c6SMark Brown .val_bits = 9, 10220ebe36c6SMark Brown .max_register = WM8960_PLL4, 10230ebe36c6SMark Brown 10240ebe36c6SMark Brown .reg_defaults = wm8960_reg_defaults, 10250ebe36c6SMark Brown .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), 10260ebe36c6SMark Brown .cache_type = REGCACHE_RBTREE, 10270ebe36c6SMark Brown 10280ebe36c6SMark Brown .volatile_reg = wm8960_volatile, 1029f0fba2adSLiam Girdwood }; 1030f0fba2adSLiam Girdwood 10317a79e94eSBill Pemberton static int wm8960_i2c_probe(struct i2c_client *i2c, 1032f2644a2cSMark Brown const struct i2c_device_id *id) 1033f2644a2cSMark Brown { 103437061631SMark Brown struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); 1035f2644a2cSMark Brown struct wm8960_priv *wm8960; 1036f0fba2adSLiam Girdwood int ret; 1037f2644a2cSMark Brown 1038b9791c01SMark Brown wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), 1039b9791c01SMark Brown GFP_KERNEL); 1040f2644a2cSMark Brown if (wm8960 == NULL) 1041f2644a2cSMark Brown return -ENOMEM; 1042f2644a2cSMark Brown 1043c5e6f5faSSachin Kamat wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); 10440ebe36c6SMark Brown if (IS_ERR(wm8960->regmap)) 10450ebe36c6SMark Brown return PTR_ERR(wm8960->regmap); 10460ebe36c6SMark Brown 104737061631SMark Brown if (pdata && pdata->shared_lrclk) { 104837061631SMark Brown ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 104937061631SMark Brown 0x4, 0x4); 105037061631SMark Brown if (ret != 0) { 105137061631SMark Brown dev_err(&i2c->dev, "Failed to enable LRCM: %d\n", 105237061631SMark Brown ret); 105337061631SMark Brown return ret; 105437061631SMark Brown } 105537061631SMark Brown } 105637061631SMark Brown 1057f2644a2cSMark Brown i2c_set_clientdata(i2c, wm8960); 1058f2644a2cSMark Brown 1059f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 1060f0fba2adSLiam Girdwood &soc_codec_dev_wm8960, &wm8960_dai, 1); 1061b9791c01SMark Brown 1062f0fba2adSLiam Girdwood return ret; 1063f2644a2cSMark Brown } 1064f2644a2cSMark Brown 10657a79e94eSBill Pemberton static int wm8960_i2c_remove(struct i2c_client *client) 1066f2644a2cSMark Brown { 1067f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 1068f2644a2cSMark Brown return 0; 1069f2644a2cSMark Brown } 1070f2644a2cSMark Brown 1071f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = { 1072f2644a2cSMark Brown { "wm8960", 0 }, 1073f2644a2cSMark Brown { } 1074f2644a2cSMark Brown }; 1075f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); 1076f2644a2cSMark Brown 1077f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = { 1078f2644a2cSMark Brown .driver = { 1079091edccfSMark Brown .name = "wm8960", 1080f2644a2cSMark Brown .owner = THIS_MODULE, 1081f2644a2cSMark Brown }, 1082f2644a2cSMark Brown .probe = wm8960_i2c_probe, 10837a79e94eSBill Pemberton .remove = wm8960_i2c_remove, 1084f2644a2cSMark Brown .id_table = wm8960_i2c_id, 1085f2644a2cSMark Brown }; 1086f2644a2cSMark Brown 10873c010e60SSachin Kamat module_i2c_driver(wm8960_i2c_driver); 1088f2644a2cSMark Brown 1089f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver"); 1090f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood"); 1091f2644a2cSMark Brown MODULE_LICENSE("GPL"); 1092