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); 20721eb2693SMark Brown static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1); 208f2644a2cSMark Brown 209f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = { 210f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, 211f2644a2cSMark Brown 0, 63, 0, adc_tlv), 212f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 213f2644a2cSMark Brown 6, 1, 0), 214f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 215f2644a2cSMark Brown 7, 1, 0), 216f2644a2cSMark Brown 21721eb2693SMark Brown SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", 21821eb2693SMark Brown WM8960_INBMIX1, 4, 7, 0, boost_tlv), 21921eb2693SMark Brown SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", 22021eb2693SMark Brown WM8960_INBMIX1, 1, 7, 0, boost_tlv), 22121eb2693SMark Brown SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", 22221eb2693SMark Brown WM8960_INBMIX2, 4, 7, 0, boost_tlv), 22321eb2693SMark Brown SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", 22421eb2693SMark Brown WM8960_INBMIX2, 1, 7, 0, boost_tlv), 22521eb2693SMark Brown 226f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 227f2644a2cSMark Brown 0, 255, 0, dac_tlv), 228f2644a2cSMark Brown 229f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, 230f2644a2cSMark Brown 0, 127, 0, out_tlv), 231f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1, 232f2644a2cSMark Brown 7, 1, 0), 233f2644a2cSMark Brown 234f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, 235f2644a2cSMark Brown 0, 127, 0, out_tlv), 236f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2, 237f2644a2cSMark Brown 7, 1, 0), 238f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), 239f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), 240f2644a2cSMark Brown 241f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), 2424faaa8d9SMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[0]), 243f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), 244f2644a2cSMark Brown 245f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]), 246afd6d36aSMark Brown SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, 247afd6d36aSMark Brown wm8960_get_deemph, wm8960_put_deemph), 248f2644a2cSMark Brown 2494faaa8d9SMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), 2504faaa8d9SMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), 251f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), 252f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), 253f2644a2cSMark Brown 2544faaa8d9SMark Brown SOC_ENUM("ALC Function", wm8960_enum[4]), 255f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), 256f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), 257f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), 258f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), 2594faaa8d9SMark Brown SOC_ENUM("ALC Mode", wm8960_enum[5]), 260f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), 261f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), 262f2644a2cSMark Brown 263f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), 264f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), 265f2644a2cSMark Brown 266f2644a2cSMark Brown SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH, 267f2644a2cSMark Brown 0, 127, 0), 268f2644a2cSMark Brown 269f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", 270f2644a2cSMark Brown WM8960_BYPASS1, 4, 7, 1, bypass_tlv), 271f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume", 272f2644a2cSMark Brown WM8960_LOUTMIX, 4, 7, 1, bypass_tlv), 273f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume", 274f2644a2cSMark Brown WM8960_BYPASS2, 4, 7, 1, bypass_tlv), 275f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", 276f2644a2cSMark Brown WM8960_ROUTMIX, 4, 7, 1, bypass_tlv), 277f2644a2cSMark Brown }; 278f2644a2cSMark Brown 279f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = { 280f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0), 281f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0), 282f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0), 283f2644a2cSMark Brown }; 284f2644a2cSMark Brown 285f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = { 286f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0), 287f2644a2cSMark Brown }; 288f2644a2cSMark Brown 289f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = { 290f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0), 291f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0), 292f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0), 293f2644a2cSMark Brown }; 294f2644a2cSMark Brown 295f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = { 296f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0), 297f2644a2cSMark Brown }; 298f2644a2cSMark Brown 299f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = { 300f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0), 301f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0), 302f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0), 303f2644a2cSMark Brown }; 304f2644a2cSMark Brown 305f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = { 306f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0), 307f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0), 308f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0), 309f2644a2cSMark Brown }; 310f2644a2cSMark Brown 311f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = { 312f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0), 313f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0), 314f2644a2cSMark Brown }; 315f2644a2cSMark Brown 316f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = { 317f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"), 318f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"), 319f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"), 320f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"), 321f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"), 322f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"), 323f2644a2cSMark Brown 324187774cbSMark Brown SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), 325f2644a2cSMark Brown 326f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0, 327f2644a2cSMark Brown wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)), 328f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0, 329f2644a2cSMark Brown wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)), 330f2644a2cSMark Brown 331f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, 332f2644a2cSMark Brown wm8960_lin, ARRAY_SIZE(wm8960_lin)), 333f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0, 334f2644a2cSMark Brown wm8960_rin, ARRAY_SIZE(wm8960_rin)), 335f2644a2cSMark Brown 33644426de4SMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER1, 3, 0), 33744426de4SMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER1, 2, 0), 338f2644a2cSMark Brown 339f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0), 340f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0), 341f2644a2cSMark Brown 342f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0, 343f2644a2cSMark Brown &wm8960_loutput_mixer[0], 344f2644a2cSMark Brown ARRAY_SIZE(wm8960_loutput_mixer)), 345f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0, 346f2644a2cSMark Brown &wm8960_routput_mixer[0], 347f2644a2cSMark Brown ARRAY_SIZE(wm8960_routput_mixer)), 348f2644a2cSMark Brown 349f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), 350f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), 351f2644a2cSMark Brown 352f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0), 353f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0), 354f2644a2cSMark Brown 355f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0), 356f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0), 357f2644a2cSMark Brown 358f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"), 359f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"), 360f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"), 361f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"), 362f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"), 363f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"), 364f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"), 365f2644a2cSMark Brown }; 366f2644a2cSMark Brown 367913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = { 368913d7b4cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0, 369913d7b4cSMark Brown &wm8960_mono_out[0], 370913d7b4cSMark Brown ARRAY_SIZE(wm8960_mono_out)), 371913d7b4cSMark Brown }; 372913d7b4cSMark Brown 373913d7b4cSMark Brown /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */ 374913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = { 375913d7b4cSMark Brown SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0), 376913d7b4cSMark Brown }; 377913d7b4cSMark Brown 378f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = { 379f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, 380f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, 381f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, 382f2644a2cSMark Brown 383f2644a2cSMark Brown { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", }, 384f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */ 385f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT2" }, 386f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT3" }, 387f2644a2cSMark Brown 388f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" }, 389f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" }, 390f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" }, 391f2644a2cSMark Brown 392f2644a2cSMark Brown { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", }, 393f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */ 394f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT2" }, 395f2644a2cSMark Brown { "Right Input Mixer", NULL, "LINPUT3" }, 396f2644a2cSMark Brown 397f2644a2cSMark Brown { "Left ADC", NULL, "Left Input Mixer" }, 398f2644a2cSMark Brown { "Right ADC", NULL, "Right Input Mixer" }, 399f2644a2cSMark Brown 400f2644a2cSMark Brown { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" }, 401f2644a2cSMark Brown { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} , 402f2644a2cSMark Brown { "Left Output Mixer", "PCM Playback Switch", "Left DAC" }, 403f2644a2cSMark Brown 404f2644a2cSMark Brown { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" }, 405f2644a2cSMark Brown { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } , 406f2644a2cSMark Brown { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, 407f2644a2cSMark Brown 408f2644a2cSMark Brown { "LOUT1 PGA", NULL, "Left Output Mixer" }, 409f2644a2cSMark Brown { "ROUT1 PGA", NULL, "Right Output Mixer" }, 410f2644a2cSMark Brown 411f2644a2cSMark Brown { "HP_L", NULL, "LOUT1 PGA" }, 412f2644a2cSMark Brown { "HP_R", NULL, "ROUT1 PGA" }, 413f2644a2cSMark Brown 414f2644a2cSMark Brown { "Left Speaker PGA", NULL, "Left Output Mixer" }, 415f2644a2cSMark Brown { "Right Speaker PGA", NULL, "Right Output Mixer" }, 416f2644a2cSMark Brown 417f2644a2cSMark Brown { "Left Speaker Output", NULL, "Left Speaker PGA" }, 418f2644a2cSMark Brown { "Right Speaker Output", NULL, "Right Speaker PGA" }, 419f2644a2cSMark Brown 420f2644a2cSMark Brown { "SPK_LN", NULL, "Left Speaker Output" }, 421f2644a2cSMark Brown { "SPK_LP", NULL, "Left Speaker Output" }, 422f2644a2cSMark Brown { "SPK_RN", NULL, "Right Speaker Output" }, 423f2644a2cSMark Brown { "SPK_RP", NULL, "Right Speaker Output" }, 424913d7b4cSMark Brown }; 425913d7b4cSMark Brown 426913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_out3[] = { 427913d7b4cSMark Brown { "Mono Output Mixer", "Left Switch", "Left Output Mixer" }, 428913d7b4cSMark Brown { "Mono Output Mixer", "Right Switch", "Right Output Mixer" }, 429f2644a2cSMark Brown 430f2644a2cSMark Brown { "OUT3", NULL, "Mono Output Mixer", } 431f2644a2cSMark Brown }; 432f2644a2cSMark Brown 433913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_capless[] = { 434913d7b4cSMark Brown { "HP_L", NULL, "OUT3 VMID" }, 435913d7b4cSMark Brown { "HP_R", NULL, "OUT3 VMID" }, 436913d7b4cSMark Brown 437913d7b4cSMark Brown { "OUT3 VMID", NULL, "Left Output Mixer" }, 438913d7b4cSMark Brown { "OUT3 VMID", NULL, "Right Output Mixer" }, 439913d7b4cSMark Brown }; 440913d7b4cSMark Brown 441f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec) 442f2644a2cSMark Brown { 443913d7b4cSMark Brown struct wm8960_data *pdata = codec->dev->platform_data; 444b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 445ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 446913d7b4cSMark Brown struct snd_soc_dapm_widget *w; 447913d7b4cSMark Brown 448ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, 449f2644a2cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets)); 450f2644a2cSMark Brown 451ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); 452f2644a2cSMark Brown 453913d7b4cSMark Brown /* In capless mode OUT3 is used to provide VMID for the 454913d7b4cSMark Brown * headphone outputs, otherwise it is used as a mono mixer. 455913d7b4cSMark Brown */ 456913d7b4cSMark Brown if (pdata && pdata->capless) { 457ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, 458913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_capless)); 459913d7b4cSMark Brown 460ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_capless, 461913d7b4cSMark Brown ARRAY_SIZE(audio_paths_capless)); 462913d7b4cSMark Brown } else { 463ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, 464913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_out3)); 465913d7b4cSMark Brown 466ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_out3, 467913d7b4cSMark Brown ARRAY_SIZE(audio_paths_out3)); 468913d7b4cSMark Brown } 469913d7b4cSMark Brown 470913d7b4cSMark Brown /* We need to power up the headphone output stage out of 471913d7b4cSMark Brown * sequence for capless mode. To save scanning the widget 472913d7b4cSMark Brown * list each time to find the desired power state do so now 473913d7b4cSMark Brown * and save the result. 474913d7b4cSMark Brown */ 47597c866deSJarkko Nikula list_for_each_entry(w, &codec->card->widgets, list) { 47697c866deSJarkko Nikula if (w->dapm != &codec->dapm) 47797c866deSJarkko Nikula continue; 478913d7b4cSMark Brown if (strcmp(w->name, "LOUT1 PGA") == 0) 479913d7b4cSMark Brown wm8960->lout1 = w; 480913d7b4cSMark Brown if (strcmp(w->name, "ROUT1 PGA") == 0) 481913d7b4cSMark Brown wm8960->rout1 = w; 482913d7b4cSMark Brown if (strcmp(w->name, "OUT3 VMID") == 0) 483913d7b4cSMark Brown wm8960->out3 = w; 484913d7b4cSMark Brown } 485913d7b4cSMark Brown 486f2644a2cSMark Brown return 0; 487f2644a2cSMark Brown } 488f2644a2cSMark Brown 489f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, 490f2644a2cSMark Brown unsigned int fmt) 491f2644a2cSMark Brown { 492f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 493f2644a2cSMark Brown u16 iface = 0; 494f2644a2cSMark Brown 495f2644a2cSMark Brown /* set master/slave audio interface */ 496f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 497f2644a2cSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 498f2644a2cSMark Brown iface |= 0x0040; 499f2644a2cSMark Brown break; 500f2644a2cSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 501f2644a2cSMark Brown break; 502f2644a2cSMark Brown default: 503f2644a2cSMark Brown return -EINVAL; 504f2644a2cSMark Brown } 505f2644a2cSMark Brown 506f2644a2cSMark Brown /* interface format */ 507f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 508f2644a2cSMark Brown case SND_SOC_DAIFMT_I2S: 509f2644a2cSMark Brown iface |= 0x0002; 510f2644a2cSMark Brown break; 511f2644a2cSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 512f2644a2cSMark Brown break; 513f2644a2cSMark Brown case SND_SOC_DAIFMT_LEFT_J: 514f2644a2cSMark Brown iface |= 0x0001; 515f2644a2cSMark Brown break; 516f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_A: 517f2644a2cSMark Brown iface |= 0x0003; 518f2644a2cSMark Brown break; 519f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_B: 520f2644a2cSMark Brown iface |= 0x0013; 521f2644a2cSMark Brown break; 522f2644a2cSMark Brown default: 523f2644a2cSMark Brown return -EINVAL; 524f2644a2cSMark Brown } 525f2644a2cSMark Brown 526f2644a2cSMark Brown /* clock inversion */ 527f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 528f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_NF: 529f2644a2cSMark Brown break; 530f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_IF: 531f2644a2cSMark Brown iface |= 0x0090; 532f2644a2cSMark Brown break; 533f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_NF: 534f2644a2cSMark Brown iface |= 0x0080; 535f2644a2cSMark Brown break; 536f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_IF: 537f2644a2cSMark Brown iface |= 0x0010; 538f2644a2cSMark Brown break; 539f2644a2cSMark Brown default: 540f2644a2cSMark Brown return -EINVAL; 541f2644a2cSMark Brown } 542f2644a2cSMark Brown 543f2644a2cSMark Brown /* set iface */ 54417a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 545f2644a2cSMark Brown return 0; 546f2644a2cSMark Brown } 547f2644a2cSMark Brown 548db059c0fSMark Brown static struct { 549db059c0fSMark Brown int rate; 550db059c0fSMark Brown unsigned int val; 551db059c0fSMark Brown } alc_rates[] = { 552db059c0fSMark Brown { 48000, 0 }, 553db059c0fSMark Brown { 44100, 0 }, 554db059c0fSMark Brown { 32000, 1 }, 555db059c0fSMark Brown { 22050, 2 }, 556db059c0fSMark Brown { 24000, 2 }, 557db059c0fSMark Brown { 16000, 3 }, 558db059c0fSMark Brown { 11250, 4 }, 559db059c0fSMark Brown { 12000, 4 }, 560db059c0fSMark Brown { 8000, 5 }, 561db059c0fSMark Brown }; 562db059c0fSMark Brown 563f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream, 564f2644a2cSMark Brown struct snd_pcm_hw_params *params, 565f2644a2cSMark Brown struct snd_soc_dai *dai) 566f2644a2cSMark Brown { 567e6968a17SMark Brown struct snd_soc_codec *codec = dai->codec; 568afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 56917a52fd6SMark Brown u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; 5704c2474c0STimur Tabi snd_pcm_format_t format = params_format(params); 571db059c0fSMark Brown int i; 572f2644a2cSMark Brown 573f2644a2cSMark Brown /* bit size */ 5744c2474c0STimur Tabi switch (format) { 575f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S16_LE: 5764c2474c0STimur Tabi case SNDRV_PCM_FORMAT_S16_BE: 577f2644a2cSMark Brown break; 578f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S20_3LE: 5794c2474c0STimur Tabi case SNDRV_PCM_FORMAT_S20_3BE: 580f2644a2cSMark Brown iface |= 0x0004; 581f2644a2cSMark Brown break; 582f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S24_LE: 5834c2474c0STimur Tabi case SNDRV_PCM_FORMAT_S24_BE: 584f2644a2cSMark Brown iface |= 0x0008; 585f2644a2cSMark Brown break; 5864c2474c0STimur Tabi default: 5874c2474c0STimur Tabi dev_err(codec->dev, "unsupported format %i\n", format); 5884c2474c0STimur Tabi return -EINVAL; 589f2644a2cSMark Brown } 590f2644a2cSMark Brown 591afd6d36aSMark Brown /* Update filters for the new rate */ 592afd6d36aSMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 593afd6d36aSMark Brown wm8960->playback_fs = params_rate(params); 594afd6d36aSMark Brown wm8960_set_deemph(codec); 595db059c0fSMark Brown } else { 596db059c0fSMark Brown for (i = 0; i < ARRAY_SIZE(alc_rates); i++) 597db059c0fSMark Brown if (alc_rates[i].rate == params_rate(params)) 598db059c0fSMark Brown snd_soc_update_bits(codec, 599db059c0fSMark Brown WM8960_ADDCTL3, 0x7, 600db059c0fSMark Brown alc_rates[i].val); 601afd6d36aSMark Brown } 602afd6d36aSMark Brown 603f2644a2cSMark Brown /* set iface */ 60417a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 605f2644a2cSMark Brown return 0; 606f2644a2cSMark Brown } 607f2644a2cSMark Brown 608f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute) 609f2644a2cSMark Brown { 610f2644a2cSMark Brown struct snd_soc_codec *codec = dai->codec; 611f2644a2cSMark Brown 612f2644a2cSMark Brown if (mute) 61316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); 614f2644a2cSMark Brown else 61516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); 616f2644a2cSMark Brown return 0; 617f2644a2cSMark Brown } 618f2644a2cSMark Brown 619913d7b4cSMark Brown static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, 620f2644a2cSMark Brown enum snd_soc_bias_level level) 621f2644a2cSMark Brown { 6220ebe36c6SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 6230ebe36c6SMark Brown 624f2644a2cSMark Brown switch (level) { 625f2644a2cSMark Brown case SND_SOC_BIAS_ON: 626f2644a2cSMark Brown break; 627f2644a2cSMark Brown 628f2644a2cSMark Brown case SND_SOC_BIAS_PREPARE: 629f2644a2cSMark Brown /* Set VMID to 2x50k */ 63016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); 631f2644a2cSMark Brown break; 632f2644a2cSMark Brown 633f2644a2cSMark Brown case SND_SOC_BIAS_STANDBY: 634ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 6350ebe36c6SMark Brown regcache_sync(wm8960->regmap); 636bc45df2dSAxel Lin 637f2644a2cSMark Brown /* Enable anti-pop features */ 63817a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 639f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 640f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 641f2644a2cSMark Brown 642f2644a2cSMark Brown /* Enable & ramp VMID at 2x50k */ 64316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); 644f2644a2cSMark Brown msleep(100); 645f2644a2cSMark Brown 646f2644a2cSMark Brown /* Enable VREF */ 64716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, 64816b24881SAxel Lin WM8960_VREF); 649f2644a2cSMark Brown 650f2644a2cSMark Brown /* Disable anti-pop features */ 65117a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); 652f2644a2cSMark Brown } 653f2644a2cSMark Brown 654f2644a2cSMark Brown /* Set VMID to 2x250k */ 65516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); 656f2644a2cSMark Brown break; 657f2644a2cSMark Brown 658f2644a2cSMark Brown case SND_SOC_BIAS_OFF: 659f2644a2cSMark Brown /* Enable anti-pop features */ 66017a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 661f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 662f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 663f2644a2cSMark Brown 664f2644a2cSMark Brown /* Disable VMID and VREF, let them discharge */ 66517a52fd6SMark Brown snd_soc_write(codec, WM8960_POWER1, 0); 666f2644a2cSMark Brown msleep(600); 667913d7b4cSMark Brown break; 668913d7b4cSMark Brown } 669f2644a2cSMark Brown 670ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 671913d7b4cSMark Brown 672913d7b4cSMark Brown return 0; 673913d7b4cSMark Brown } 674913d7b4cSMark Brown 675913d7b4cSMark Brown static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, 676913d7b4cSMark Brown enum snd_soc_bias_level level) 677913d7b4cSMark Brown { 678b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 679913d7b4cSMark Brown int reg; 680913d7b4cSMark Brown 681913d7b4cSMark Brown switch (level) { 682913d7b4cSMark Brown case SND_SOC_BIAS_ON: 683913d7b4cSMark Brown break; 684913d7b4cSMark Brown 685913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 686ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 687913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 688913d7b4cSMark Brown /* Enable anti pop mode */ 689913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 690913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 691913d7b4cSMark Brown WM8960_BUFDCOPEN, 692913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 693913d7b4cSMark Brown WM8960_BUFDCOPEN); 694913d7b4cSMark Brown 695913d7b4cSMark Brown /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ 696913d7b4cSMark Brown reg = 0; 697913d7b4cSMark Brown if (wm8960->lout1 && wm8960->lout1->power) 698913d7b4cSMark Brown reg |= WM8960_PWR2_LOUT1; 699913d7b4cSMark Brown if (wm8960->rout1 && wm8960->rout1->power) 700913d7b4cSMark Brown reg |= WM8960_PWR2_ROUT1; 701913d7b4cSMark Brown if (wm8960->out3 && wm8960->out3->power) 702913d7b4cSMark Brown reg |= WM8960_PWR2_OUT3; 703913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER2, 704913d7b4cSMark Brown WM8960_PWR2_LOUT1 | 705913d7b4cSMark Brown WM8960_PWR2_ROUT1 | 706913d7b4cSMark Brown WM8960_PWR2_OUT3, reg); 707913d7b4cSMark Brown 708913d7b4cSMark Brown /* Enable VMID at 2*50k */ 709913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 710913d7b4cSMark Brown WM8960_VMID_MASK, 0x80); 711913d7b4cSMark Brown 712913d7b4cSMark Brown /* Ramp */ 713913d7b4cSMark Brown msleep(100); 714913d7b4cSMark Brown 715913d7b4cSMark Brown /* Enable VREF */ 716913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 717913d7b4cSMark Brown WM8960_VREF, WM8960_VREF); 718913d7b4cSMark Brown 719913d7b4cSMark Brown msleep(100); 720913d7b4cSMark Brown break; 721913d7b4cSMark Brown 722913d7b4cSMark Brown case SND_SOC_BIAS_ON: 723913d7b4cSMark Brown /* Enable anti-pop mode */ 724913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 725913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 726913d7b4cSMark Brown WM8960_BUFDCOPEN, 727913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 728913d7b4cSMark Brown WM8960_BUFDCOPEN); 729913d7b4cSMark Brown 730913d7b4cSMark Brown /* Disable VMID and VREF */ 731913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 732913d7b4cSMark Brown WM8960_VREF | WM8960_VMID_MASK, 0); 733913d7b4cSMark Brown break; 734913d7b4cSMark Brown 735bc45df2dSAxel Lin case SND_SOC_BIAS_OFF: 7360ebe36c6SMark Brown regcache_sync(wm8960->regmap); 737bc45df2dSAxel Lin break; 738913d7b4cSMark Brown default: 739913d7b4cSMark Brown break; 740913d7b4cSMark Brown } 741913d7b4cSMark Brown break; 742913d7b4cSMark Brown 743913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 744ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 745913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 746913d7b4cSMark Brown /* Disable HP discharge */ 747913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP2, 748913d7b4cSMark Brown WM8960_DISOP | WM8960_DRES_MASK, 749913d7b4cSMark Brown 0); 750913d7b4cSMark Brown 751913d7b4cSMark Brown /* Disable anti-pop features */ 752913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 753913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 754913d7b4cSMark Brown WM8960_BUFDCOPEN, 755913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 756913d7b4cSMark Brown WM8960_BUFDCOPEN); 757913d7b4cSMark Brown break; 758913d7b4cSMark Brown 759913d7b4cSMark Brown default: 760913d7b4cSMark Brown break; 761913d7b4cSMark Brown } 762913d7b4cSMark Brown break; 763913d7b4cSMark Brown 764913d7b4cSMark Brown case SND_SOC_BIAS_OFF: 765f2644a2cSMark Brown break; 766f2644a2cSMark Brown } 767f2644a2cSMark Brown 768ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 769f2644a2cSMark Brown 770f2644a2cSMark Brown return 0; 771f2644a2cSMark Brown } 772f2644a2cSMark Brown 773f2644a2cSMark Brown /* PLL divisors */ 774f2644a2cSMark Brown struct _pll_div { 775f2644a2cSMark Brown u32 pre_div:1; 776f2644a2cSMark Brown u32 n:4; 777f2644a2cSMark Brown u32 k:24; 778f2644a2cSMark Brown }; 779f2644a2cSMark Brown 780f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10 781f2644a2cSMark Brown * to allow rounding later */ 782f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10) 783f2644a2cSMark Brown 784f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target, 785f2644a2cSMark Brown struct _pll_div *pll_div) 786f2644a2cSMark Brown { 787f2644a2cSMark Brown unsigned long long Kpart; 788f2644a2cSMark Brown unsigned int K, Ndiv, Nmod; 789f2644a2cSMark Brown 790f2644a2cSMark Brown pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target); 791f2644a2cSMark Brown 792f2644a2cSMark Brown /* Scale up target to PLL operating frequency */ 793f2644a2cSMark Brown target *= 4; 794f2644a2cSMark Brown 795f2644a2cSMark Brown Ndiv = target / source; 796f2644a2cSMark Brown if (Ndiv < 6) { 797f2644a2cSMark Brown source >>= 1; 798f2644a2cSMark Brown pll_div->pre_div = 1; 799f2644a2cSMark Brown Ndiv = target / source; 800f2644a2cSMark Brown } else 801f2644a2cSMark Brown pll_div->pre_div = 0; 802f2644a2cSMark Brown 803f2644a2cSMark Brown if ((Ndiv < 6) || (Ndiv > 12)) { 804f2644a2cSMark Brown pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); 805f2644a2cSMark Brown return -EINVAL; 806f2644a2cSMark Brown } 807f2644a2cSMark Brown 808f2644a2cSMark Brown pll_div->n = Ndiv; 809f2644a2cSMark Brown Nmod = target % source; 810f2644a2cSMark Brown Kpart = FIXED_PLL_SIZE * (long long)Nmod; 811f2644a2cSMark Brown 812f2644a2cSMark Brown do_div(Kpart, source); 813f2644a2cSMark Brown 814f2644a2cSMark Brown K = Kpart & 0xFFFFFFFF; 815f2644a2cSMark Brown 816f2644a2cSMark Brown /* Check if we need to round */ 817f2644a2cSMark Brown if ((K % 10) >= 5) 818f2644a2cSMark Brown K += 5; 819f2644a2cSMark Brown 820f2644a2cSMark Brown /* Move down to proper range now rounding is done */ 821f2644a2cSMark Brown K /= 10; 822f2644a2cSMark Brown 823f2644a2cSMark Brown pll_div->k = K; 824f2644a2cSMark Brown 825f2644a2cSMark Brown pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n", 826f2644a2cSMark Brown pll_div->n, pll_div->k, pll_div->pre_div); 827f2644a2cSMark Brown 828f2644a2cSMark Brown return 0; 829f2644a2cSMark Brown } 830f2644a2cSMark Brown 83185488037SMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 83285488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 833f2644a2cSMark Brown { 834f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 835f2644a2cSMark Brown u16 reg; 836f2644a2cSMark Brown static struct _pll_div pll_div; 837f2644a2cSMark Brown int ret; 838f2644a2cSMark Brown 839f2644a2cSMark Brown if (freq_in && freq_out) { 840f2644a2cSMark Brown ret = pll_factors(freq_in, freq_out, &pll_div); 841f2644a2cSMark Brown if (ret != 0) 842f2644a2cSMark Brown return ret; 843f2644a2cSMark Brown } 844f2644a2cSMark Brown 845f2644a2cSMark Brown /* Disable the PLL: even if we are changing the frequency the 846f2644a2cSMark Brown * PLL needs to be disabled while we do so. */ 84716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); 84816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); 849f2644a2cSMark Brown 850f2644a2cSMark Brown if (!freq_in || !freq_out) 851f2644a2cSMark Brown return 0; 852f2644a2cSMark Brown 85317a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; 854f2644a2cSMark Brown reg |= pll_div.pre_div << 4; 855f2644a2cSMark Brown reg |= pll_div.n; 856f2644a2cSMark Brown 857f2644a2cSMark Brown if (pll_div.k) { 858f2644a2cSMark Brown reg |= 0x20; 859f2644a2cSMark Brown 86085fa532bSMike Dyer snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); 86185fa532bSMike Dyer snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); 86285fa532bSMike Dyer snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); 863f2644a2cSMark Brown } 86417a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg); 865f2644a2cSMark Brown 866f2644a2cSMark Brown /* Turn it on */ 86716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); 868f2644a2cSMark Brown msleep(250); 86916b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); 870f2644a2cSMark Brown 871f2644a2cSMark Brown return 0; 872f2644a2cSMark Brown } 873f2644a2cSMark Brown 874f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 875f2644a2cSMark Brown int div_id, int div) 876f2644a2cSMark Brown { 877f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 878f2644a2cSMark Brown u16 reg; 879f2644a2cSMark Brown 880f2644a2cSMark Brown switch (div_id) { 881f2644a2cSMark Brown case WM8960_SYSCLKDIV: 88217a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; 88317a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 884f2644a2cSMark Brown break; 885f2644a2cSMark Brown case WM8960_DACDIV: 88617a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; 88717a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 888f2644a2cSMark Brown break; 889f2644a2cSMark Brown case WM8960_OPCLKDIV: 89017a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; 89117a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg | div); 892f2644a2cSMark Brown break; 893f2644a2cSMark Brown case WM8960_DCLKDIV: 89417a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; 89517a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK2, reg | div); 896f2644a2cSMark Brown break; 897f2644a2cSMark Brown case WM8960_TOCLKSEL: 89817a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; 89917a52fd6SMark Brown snd_soc_write(codec, WM8960_ADDCTL1, reg | div); 900f2644a2cSMark Brown break; 901f2644a2cSMark Brown default: 902f2644a2cSMark Brown return -EINVAL; 903f2644a2cSMark Brown } 904f2644a2cSMark Brown 905f2644a2cSMark Brown return 0; 906f2644a2cSMark Brown } 907f2644a2cSMark Brown 908f0fba2adSLiam Girdwood static int wm8960_set_bias_level(struct snd_soc_codec *codec, 909f0fba2adSLiam Girdwood enum snd_soc_bias_level level) 910f0fba2adSLiam Girdwood { 911f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 912f0fba2adSLiam Girdwood 913f0fba2adSLiam Girdwood return wm8960->set_bias_level(codec, level); 914f0fba2adSLiam Girdwood } 915f0fba2adSLiam Girdwood 916f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000 917f2644a2cSMark Brown 918f2644a2cSMark Brown #define WM8960_FORMATS \ 919f2644a2cSMark Brown (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 920f2644a2cSMark Brown SNDRV_PCM_FMTBIT_S24_LE) 921f2644a2cSMark Brown 92285e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8960_dai_ops = { 923f2644a2cSMark Brown .hw_params = wm8960_hw_params, 924f2644a2cSMark Brown .digital_mute = wm8960_mute, 925f2644a2cSMark Brown .set_fmt = wm8960_set_dai_fmt, 926f2644a2cSMark Brown .set_clkdiv = wm8960_set_dai_clkdiv, 927f2644a2cSMark Brown .set_pll = wm8960_set_dai_pll, 928f2644a2cSMark Brown }; 929f2644a2cSMark Brown 930f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8960_dai = { 931f0fba2adSLiam Girdwood .name = "wm8960-hifi", 932f2644a2cSMark Brown .playback = { 933f2644a2cSMark Brown .stream_name = "Playback", 934f2644a2cSMark Brown .channels_min = 1, 935f2644a2cSMark Brown .channels_max = 2, 936f2644a2cSMark Brown .rates = WM8960_RATES, 937f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 938f2644a2cSMark Brown .capture = { 939f2644a2cSMark Brown .stream_name = "Capture", 940f2644a2cSMark Brown .channels_min = 1, 941f2644a2cSMark Brown .channels_max = 2, 942f2644a2cSMark Brown .rates = WM8960_RATES, 943f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 944f2644a2cSMark Brown .ops = &wm8960_dai_ops, 945f2644a2cSMark Brown .symmetric_rates = 1, 946f2644a2cSMark Brown }; 947f2644a2cSMark Brown 94884b315eeSLars-Peter Clausen static int wm8960_suspend(struct snd_soc_codec *codec) 949f2644a2cSMark Brown { 950f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 951f2644a2cSMark Brown 952f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); 953f2644a2cSMark Brown return 0; 954f2644a2cSMark Brown } 955f2644a2cSMark Brown 956f0fba2adSLiam Girdwood static int wm8960_resume(struct snd_soc_codec *codec) 957f2644a2cSMark Brown { 958f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 959f2644a2cSMark Brown 960f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); 961f2644a2cSMark Brown return 0; 962f2644a2cSMark Brown } 963f2644a2cSMark Brown 964f0fba2adSLiam Girdwood static int wm8960_probe(struct snd_soc_codec *codec) 965f2644a2cSMark Brown { 966f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 967f0fba2adSLiam Girdwood struct wm8960_data *pdata = dev_get_platdata(codec->dev); 968f2644a2cSMark Brown int ret; 969f2644a2cSMark Brown 970f0fba2adSLiam Girdwood wm8960->set_bias_level = wm8960_set_bias_level_out3; 971913d7b4cSMark Brown 972f2644a2cSMark Brown if (!pdata) { 973f2644a2cSMark Brown dev_warn(codec->dev, "No platform data supplied\n"); 974f2644a2cSMark Brown } else { 975913d7b4cSMark Brown if (pdata->capless) 976f0fba2adSLiam Girdwood wm8960->set_bias_level = wm8960_set_bias_level_capless; 977f2644a2cSMark Brown } 978f2644a2cSMark Brown 9790ebe36c6SMark Brown ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP); 98017a52fd6SMark Brown if (ret < 0) { 98117a52fd6SMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 982f0fba2adSLiam Girdwood return ret; 98317a52fd6SMark Brown } 98417a52fd6SMark Brown 985f2644a2cSMark Brown ret = wm8960_reset(codec); 986f2644a2cSMark Brown if (ret < 0) { 987f2644a2cSMark Brown dev_err(codec->dev, "Failed to issue reset\n"); 988f0fba2adSLiam Girdwood return ret; 989f2644a2cSMark Brown } 990f2644a2cSMark Brown 991f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); 992f2644a2cSMark Brown 993f2644a2cSMark Brown /* Latch the update bits */ 99416b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100); 99516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100); 99616b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100); 99716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100); 99816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100); 99916b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100); 100016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100); 100116b24881SAxel Lin snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100); 100216b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100); 100316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100); 1004f2644a2cSMark Brown 1005022658beSLiam Girdwood snd_soc_add_codec_controls(codec, wm8960_snd_controls, 1006f0fba2adSLiam Girdwood ARRAY_SIZE(wm8960_snd_controls)); 1007f0fba2adSLiam Girdwood wm8960_add_widgets(codec); 1008f2644a2cSMark Brown 1009f2644a2cSMark Brown return 0; 1010f2644a2cSMark Brown } 1011f2644a2cSMark Brown 1012f0fba2adSLiam Girdwood /* power down chip */ 1013f0fba2adSLiam Girdwood static int wm8960_remove(struct snd_soc_codec *codec) 1014f2644a2cSMark Brown { 1015f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 1016f0fba2adSLiam Girdwood 1017f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); 1018f0fba2adSLiam Girdwood return 0; 1019f2644a2cSMark Brown } 1020f2644a2cSMark Brown 1021f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8960 = { 1022f0fba2adSLiam Girdwood .probe = wm8960_probe, 1023f0fba2adSLiam Girdwood .remove = wm8960_remove, 1024f0fba2adSLiam Girdwood .suspend = wm8960_suspend, 1025f0fba2adSLiam Girdwood .resume = wm8960_resume, 1026f0fba2adSLiam Girdwood .set_bias_level = wm8960_set_bias_level, 10270ebe36c6SMark Brown }; 10280ebe36c6SMark Brown 10290ebe36c6SMark Brown static const struct regmap_config wm8960_regmap = { 10300ebe36c6SMark Brown .reg_bits = 7, 10310ebe36c6SMark Brown .val_bits = 9, 10320ebe36c6SMark Brown .max_register = WM8960_PLL4, 10330ebe36c6SMark Brown 10340ebe36c6SMark Brown .reg_defaults = wm8960_reg_defaults, 10350ebe36c6SMark Brown .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), 10360ebe36c6SMark Brown .cache_type = REGCACHE_RBTREE, 10370ebe36c6SMark Brown 10380ebe36c6SMark Brown .volatile_reg = wm8960_volatile, 1039f0fba2adSLiam Girdwood }; 1040f0fba2adSLiam Girdwood 10417a79e94eSBill Pemberton static int wm8960_i2c_probe(struct i2c_client *i2c, 1042f2644a2cSMark Brown const struct i2c_device_id *id) 1043f2644a2cSMark Brown { 104437061631SMark Brown struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); 1045f2644a2cSMark Brown struct wm8960_priv *wm8960; 1046f0fba2adSLiam Girdwood int ret; 1047f2644a2cSMark Brown 1048b9791c01SMark Brown wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), 1049b9791c01SMark Brown GFP_KERNEL); 1050f2644a2cSMark Brown if (wm8960 == NULL) 1051f2644a2cSMark Brown return -ENOMEM; 1052f2644a2cSMark Brown 1053c5e6f5faSSachin Kamat wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); 10540ebe36c6SMark Brown if (IS_ERR(wm8960->regmap)) 10550ebe36c6SMark Brown return PTR_ERR(wm8960->regmap); 10560ebe36c6SMark Brown 105737061631SMark Brown if (pdata && pdata->shared_lrclk) { 105837061631SMark Brown ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 105937061631SMark Brown 0x4, 0x4); 106037061631SMark Brown if (ret != 0) { 106137061631SMark Brown dev_err(&i2c->dev, "Failed to enable LRCM: %d\n", 106237061631SMark Brown ret); 106337061631SMark Brown return ret; 106437061631SMark Brown } 106537061631SMark Brown } 106637061631SMark Brown 1067f2644a2cSMark Brown i2c_set_clientdata(i2c, wm8960); 1068f2644a2cSMark Brown 1069f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 1070f0fba2adSLiam Girdwood &soc_codec_dev_wm8960, &wm8960_dai, 1); 1071b9791c01SMark Brown 1072f0fba2adSLiam Girdwood return ret; 1073f2644a2cSMark Brown } 1074f2644a2cSMark Brown 10757a79e94eSBill Pemberton static int wm8960_i2c_remove(struct i2c_client *client) 1076f2644a2cSMark Brown { 1077f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 1078f2644a2cSMark Brown return 0; 1079f2644a2cSMark Brown } 1080f2644a2cSMark Brown 1081f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = { 1082f2644a2cSMark Brown { "wm8960", 0 }, 1083f2644a2cSMark Brown { } 1084f2644a2cSMark Brown }; 1085f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); 1086f2644a2cSMark Brown 1087f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = { 1088f2644a2cSMark Brown .driver = { 1089091edccfSMark Brown .name = "wm8960", 1090f2644a2cSMark Brown .owner = THIS_MODULE, 1091f2644a2cSMark Brown }, 1092f2644a2cSMark Brown .probe = wm8960_i2c_probe, 10937a79e94eSBill Pemberton .remove = wm8960_i2c_remove, 1094f2644a2cSMark Brown .id_table = wm8960_i2c_id, 1095f2644a2cSMark Brown }; 1096f2644a2cSMark Brown 10973c010e60SSachin Kamat module_i2c_driver(wm8960_i2c_driver); 1098f2644a2cSMark Brown 1099f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver"); 1100f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood"); 1101f2644a2cSMark Brown MODULE_LICENSE("GPL"); 1102