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> 1875aa8868SZidan Wang #include <linux/clk.h> 19f2644a2cSMark Brown #include <linux/i2c.h> 205a0e3ad6STejun Heo #include <linux/slab.h> 21f2644a2cSMark Brown #include <sound/core.h> 22f2644a2cSMark Brown #include <sound/pcm.h> 23f2644a2cSMark Brown #include <sound/pcm_params.h> 24f2644a2cSMark Brown #include <sound/soc.h> 25f2644a2cSMark Brown #include <sound/initval.h> 26f2644a2cSMark Brown #include <sound/tlv.h> 27b6877a47SMark Brown #include <sound/wm8960.h> 28f2644a2cSMark Brown 29f2644a2cSMark Brown #include "wm8960.h" 30f2644a2cSMark Brown 31f2644a2cSMark Brown /* R25 - Power 1 */ 32913d7b4cSMark Brown #define WM8960_VMID_MASK 0x180 33f2644a2cSMark Brown #define WM8960_VREF 0x40 34f2644a2cSMark Brown 35913d7b4cSMark Brown /* R26 - Power 2 */ 36913d7b4cSMark Brown #define WM8960_PWR2_LOUT1 0x40 37913d7b4cSMark Brown #define WM8960_PWR2_ROUT1 0x20 38913d7b4cSMark Brown #define WM8960_PWR2_OUT3 0x02 39913d7b4cSMark Brown 40f2644a2cSMark Brown /* R28 - Anti-pop 1 */ 41f2644a2cSMark Brown #define WM8960_POBCTRL 0x80 42f2644a2cSMark Brown #define WM8960_BUFDCOPEN 0x10 43f2644a2cSMark Brown #define WM8960_BUFIOEN 0x08 44f2644a2cSMark Brown #define WM8960_SOFT_ST 0x04 45f2644a2cSMark Brown #define WM8960_HPSTBY 0x01 46f2644a2cSMark Brown 47f2644a2cSMark Brown /* R29 - Anti-pop 2 */ 48f2644a2cSMark Brown #define WM8960_DISOP 0x40 49913d7b4cSMark Brown #define WM8960_DRES_MASK 0x30 50f2644a2cSMark Brown 51f2644a2cSMark Brown /* 52f2644a2cSMark Brown * wm8960 register cache 53f2644a2cSMark Brown * We can't read the WM8960 register space when we are 54f2644a2cSMark Brown * using 2 wire for device control, so we cache them instead. 55f2644a2cSMark Brown */ 560ebe36c6SMark Brown static const struct reg_default wm8960_reg_defaults[] = { 57b3df026eSMark Brown { 0x0, 0x00a7 }, 58b3df026eSMark Brown { 0x1, 0x00a7 }, 590ebe36c6SMark Brown { 0x2, 0x0000 }, 600ebe36c6SMark Brown { 0x3, 0x0000 }, 610ebe36c6SMark Brown { 0x4, 0x0000 }, 620ebe36c6SMark Brown { 0x5, 0x0008 }, 630ebe36c6SMark Brown { 0x6, 0x0000 }, 640ebe36c6SMark Brown { 0x7, 0x000a }, 650ebe36c6SMark Brown { 0x8, 0x01c0 }, 660ebe36c6SMark Brown { 0x9, 0x0000 }, 670ebe36c6SMark Brown { 0xa, 0x00ff }, 680ebe36c6SMark Brown { 0xb, 0x00ff }, 690ebe36c6SMark Brown 700ebe36c6SMark Brown { 0x10, 0x0000 }, 710ebe36c6SMark Brown { 0x11, 0x007b }, 720ebe36c6SMark Brown { 0x12, 0x0100 }, 730ebe36c6SMark Brown { 0x13, 0x0032 }, 740ebe36c6SMark Brown { 0x14, 0x0000 }, 750ebe36c6SMark Brown { 0x15, 0x00c3 }, 760ebe36c6SMark Brown { 0x16, 0x00c3 }, 770ebe36c6SMark Brown { 0x17, 0x01c0 }, 780ebe36c6SMark Brown { 0x18, 0x0000 }, 790ebe36c6SMark Brown { 0x19, 0x0000 }, 800ebe36c6SMark Brown { 0x1a, 0x0000 }, 810ebe36c6SMark Brown { 0x1b, 0x0000 }, 820ebe36c6SMark Brown { 0x1c, 0x0000 }, 830ebe36c6SMark Brown { 0x1d, 0x0000 }, 840ebe36c6SMark Brown 850ebe36c6SMark Brown { 0x20, 0x0100 }, 860ebe36c6SMark Brown { 0x21, 0x0100 }, 870ebe36c6SMark Brown { 0x22, 0x0050 }, 880ebe36c6SMark Brown 890ebe36c6SMark Brown { 0x25, 0x0050 }, 900ebe36c6SMark Brown { 0x26, 0x0000 }, 910ebe36c6SMark Brown { 0x27, 0x0000 }, 920ebe36c6SMark Brown { 0x28, 0x0000 }, 930ebe36c6SMark Brown { 0x29, 0x0000 }, 940ebe36c6SMark Brown { 0x2a, 0x0040 }, 950ebe36c6SMark Brown { 0x2b, 0x0000 }, 960ebe36c6SMark Brown { 0x2c, 0x0000 }, 970ebe36c6SMark Brown { 0x2d, 0x0050 }, 980ebe36c6SMark Brown { 0x2e, 0x0050 }, 990ebe36c6SMark Brown { 0x2f, 0x0000 }, 1000ebe36c6SMark Brown { 0x30, 0x0002 }, 1010ebe36c6SMark Brown { 0x31, 0x0037 }, 1020ebe36c6SMark Brown 1030ebe36c6SMark Brown { 0x33, 0x0080 }, 1040ebe36c6SMark Brown { 0x34, 0x0008 }, 1050ebe36c6SMark Brown { 0x35, 0x0031 }, 1060ebe36c6SMark Brown { 0x36, 0x0026 }, 1070ebe36c6SMark Brown { 0x37, 0x00e9 }, 108f2644a2cSMark Brown }; 109f2644a2cSMark Brown 1100ebe36c6SMark Brown static bool wm8960_volatile(struct device *dev, unsigned int reg) 1110ebe36c6SMark Brown { 1120ebe36c6SMark Brown switch (reg) { 1130ebe36c6SMark Brown case WM8960_RESET: 1140ebe36c6SMark Brown return true; 1150ebe36c6SMark Brown default: 1160ebe36c6SMark Brown return false; 1170ebe36c6SMark Brown } 1180ebe36c6SMark Brown } 1190ebe36c6SMark Brown 120f2644a2cSMark Brown struct wm8960_priv { 12175aa8868SZidan Wang struct clk *mclk; 1220ebe36c6SMark Brown struct regmap *regmap; 123f0fba2adSLiam Girdwood int (*set_bias_level)(struct snd_soc_codec *, 124f0fba2adSLiam Girdwood enum snd_soc_bias_level level); 125913d7b4cSMark Brown struct snd_soc_dapm_widget *lout1; 126913d7b4cSMark Brown struct snd_soc_dapm_widget *rout1; 127913d7b4cSMark Brown struct snd_soc_dapm_widget *out3; 128afd6d36aSMark Brown bool deemph; 129afd6d36aSMark Brown int playback_fs; 1300e50b51aSZidan Wang int bclk; 1310e50b51aSZidan Wang int sysclk; 132e2280c90SZidan Wang struct wm8960_data pdata; 133f2644a2cSMark Brown }; 134f2644a2cSMark Brown 1353ad5e861SZidan Wang #define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) 136f2644a2cSMark Brown 137f2644a2cSMark Brown /* enumerated controls */ 138f2644a2cSMark Brown static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", 139f2644a2cSMark Brown "Right Inverted", "Stereo Inversion"}; 140f2644a2cSMark Brown static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; 141f2644a2cSMark Brown static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"}; 142f2644a2cSMark Brown static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; 143f2644a2cSMark Brown static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; 144f2644a2cSMark Brown 145f2644a2cSMark Brown static const struct soc_enum wm8960_enum[] = { 146f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), 147f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), 148f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), 149f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff), 150f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc), 151f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), 152f2644a2cSMark Brown }; 153f2644a2cSMark Brown 154afd6d36aSMark Brown static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; 155afd6d36aSMark Brown 156afd6d36aSMark Brown static int wm8960_set_deemph(struct snd_soc_codec *codec) 157afd6d36aSMark Brown { 158afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 159afd6d36aSMark Brown int val, i, best; 160afd6d36aSMark Brown 161afd6d36aSMark Brown /* If we're using deemphasis select the nearest available sample 162afd6d36aSMark Brown * rate. 163afd6d36aSMark Brown */ 164afd6d36aSMark Brown if (wm8960->deemph) { 165afd6d36aSMark Brown best = 1; 166afd6d36aSMark Brown for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { 167afd6d36aSMark Brown if (abs(deemph_settings[i] - wm8960->playback_fs) < 168afd6d36aSMark Brown abs(deemph_settings[best] - wm8960->playback_fs)) 169afd6d36aSMark Brown best = i; 170afd6d36aSMark Brown } 171afd6d36aSMark Brown 172afd6d36aSMark Brown val = best << 1; 173afd6d36aSMark Brown } else { 174afd6d36aSMark Brown val = 0; 175afd6d36aSMark Brown } 176afd6d36aSMark Brown 177afd6d36aSMark Brown dev_dbg(codec->dev, "Set deemphasis %d\n", val); 178afd6d36aSMark Brown 179afd6d36aSMark Brown return snd_soc_update_bits(codec, WM8960_DACCTL1, 180afd6d36aSMark Brown 0x6, val); 181afd6d36aSMark Brown } 182afd6d36aSMark Brown 183afd6d36aSMark Brown static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, 184afd6d36aSMark Brown struct snd_ctl_elem_value *ucontrol) 185afd6d36aSMark Brown { 186ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 187afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 188afd6d36aSMark Brown 189b4a18c8bSTakashi Iwai ucontrol->value.integer.value[0] = wm8960->deemph; 1903f343f85SDmitry Artamonow return 0; 191afd6d36aSMark Brown } 192afd6d36aSMark Brown 193afd6d36aSMark Brown static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, 194afd6d36aSMark Brown struct snd_ctl_elem_value *ucontrol) 195afd6d36aSMark Brown { 196ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 197afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 198b4a18c8bSTakashi Iwai int deemph = ucontrol->value.integer.value[0]; 199afd6d36aSMark Brown 200afd6d36aSMark Brown if (deemph > 1) 201afd6d36aSMark Brown return -EINVAL; 202afd6d36aSMark Brown 203afd6d36aSMark Brown wm8960->deemph = deemph; 204afd6d36aSMark Brown 205afd6d36aSMark Brown return wm8960_set_deemph(codec); 206afd6d36aSMark Brown } 207afd6d36aSMark Brown 208f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); 209f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); 210f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); 211f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 21221eb2693SMark Brown static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1); 213f2644a2cSMark Brown 214f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = { 215f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, 216f2644a2cSMark Brown 0, 63, 0, adc_tlv), 217f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 218f2644a2cSMark Brown 6, 1, 0), 219f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 220f2644a2cSMark Brown 7, 1, 0), 221f2644a2cSMark Brown 22221eb2693SMark Brown SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", 22321eb2693SMark Brown WM8960_INBMIX1, 4, 7, 0, boost_tlv), 22421eb2693SMark Brown SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", 22521eb2693SMark Brown WM8960_INBMIX1, 1, 7, 0, boost_tlv), 22621eb2693SMark Brown SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", 22721eb2693SMark Brown WM8960_INBMIX2, 4, 7, 0, boost_tlv), 22821eb2693SMark Brown SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", 22921eb2693SMark Brown WM8960_INBMIX2, 1, 7, 0, boost_tlv), 23021eb2693SMark Brown 231f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 232f2644a2cSMark Brown 0, 255, 0, dac_tlv), 233f2644a2cSMark Brown 234f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, 235f2644a2cSMark Brown 0, 127, 0, out_tlv), 236f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1, 237f2644a2cSMark Brown 7, 1, 0), 238f2644a2cSMark Brown 239f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, 240f2644a2cSMark Brown 0, 127, 0, out_tlv), 241f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2, 242f2644a2cSMark Brown 7, 1, 0), 243f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), 244f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), 245f2644a2cSMark Brown 246f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), 2474faaa8d9SMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[0]), 248f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), 249f2644a2cSMark Brown 250f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]), 251afd6d36aSMark Brown SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, 252afd6d36aSMark Brown wm8960_get_deemph, wm8960_put_deemph), 253f2644a2cSMark Brown 2544faaa8d9SMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), 2554faaa8d9SMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), 256f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), 257f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), 258f2644a2cSMark Brown 2594faaa8d9SMark Brown SOC_ENUM("ALC Function", wm8960_enum[4]), 260f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), 261f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), 262f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), 263f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), 2644faaa8d9SMark Brown SOC_ENUM("ALC Mode", wm8960_enum[5]), 265f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), 266f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), 267f2644a2cSMark Brown 268f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), 269f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), 270f2644a2cSMark Brown 271c324aac0SMa Haijun SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, 272c324aac0SMa Haijun 0, 255, 0, adc_tlv), 273f2644a2cSMark Brown 274f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", 275f2644a2cSMark Brown WM8960_BYPASS1, 4, 7, 1, bypass_tlv), 276f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume", 277f2644a2cSMark Brown WM8960_LOUTMIX, 4, 7, 1, bypass_tlv), 278f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume", 279f2644a2cSMark Brown WM8960_BYPASS2, 4, 7, 1, bypass_tlv), 280f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", 281f2644a2cSMark Brown WM8960_ROUTMIX, 4, 7, 1, bypass_tlv), 282f2644a2cSMark Brown }; 283f2644a2cSMark Brown 284f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = { 285f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0), 286f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0), 287f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0), 288f2644a2cSMark Brown }; 289f2644a2cSMark Brown 290f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = { 291f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0), 292f2644a2cSMark Brown }; 293f2644a2cSMark Brown 294f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = { 295f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0), 296f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0), 297f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0), 298f2644a2cSMark Brown }; 299f2644a2cSMark Brown 300f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = { 301f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0), 302f2644a2cSMark Brown }; 303f2644a2cSMark Brown 304f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = { 305f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0), 306f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0), 307f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0), 308f2644a2cSMark Brown }; 309f2644a2cSMark Brown 310f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = { 311f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0), 312f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0), 313f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0), 314f2644a2cSMark Brown }; 315f2644a2cSMark Brown 316f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = { 317f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0), 318f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0), 319f2644a2cSMark Brown }; 320f2644a2cSMark Brown 321f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = { 322f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"), 323f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"), 324f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"), 325f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"), 326f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"), 327f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"), 328f2644a2cSMark Brown 329187774cbSMark Brown SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), 330f2644a2cSMark Brown 331f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0, 332f2644a2cSMark Brown wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)), 333f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0, 334f2644a2cSMark Brown wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)), 335f2644a2cSMark Brown 336f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, 337f2644a2cSMark Brown wm8960_lin, ARRAY_SIZE(wm8960_lin)), 338f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0, 339f2644a2cSMark Brown wm8960_rin, ARRAY_SIZE(wm8960_rin)), 340f2644a2cSMark Brown 34144426de4SMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER1, 3, 0), 34244426de4SMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER1, 2, 0), 343f2644a2cSMark Brown 344f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0), 345f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0), 346f2644a2cSMark Brown 347f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0, 348f2644a2cSMark Brown &wm8960_loutput_mixer[0], 349f2644a2cSMark Brown ARRAY_SIZE(wm8960_loutput_mixer)), 350f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0, 351f2644a2cSMark Brown &wm8960_routput_mixer[0], 352f2644a2cSMark Brown ARRAY_SIZE(wm8960_routput_mixer)), 353f2644a2cSMark Brown 354f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), 355f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), 356f2644a2cSMark Brown 357f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0), 358f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0), 359f2644a2cSMark Brown 360f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0), 361f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0), 362f2644a2cSMark Brown 363f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"), 364f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"), 365f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"), 366f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"), 367f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"), 368f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"), 369f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"), 370f2644a2cSMark Brown }; 371f2644a2cSMark Brown 372913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = { 373913d7b4cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0, 374913d7b4cSMark Brown &wm8960_mono_out[0], 375913d7b4cSMark Brown ARRAY_SIZE(wm8960_mono_out)), 376913d7b4cSMark Brown }; 377913d7b4cSMark Brown 378913d7b4cSMark Brown /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */ 379913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = { 380913d7b4cSMark Brown SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0), 381913d7b4cSMark Brown }; 382913d7b4cSMark Brown 383f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = { 384f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, 385f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, 386f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, 387f2644a2cSMark Brown 388f2644a2cSMark Brown { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", }, 389f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */ 390f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT2" }, 391f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT3" }, 392f2644a2cSMark Brown 393f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" }, 394f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" }, 395f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" }, 396f2644a2cSMark Brown 397f2644a2cSMark Brown { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", }, 398f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */ 399f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT2" }, 400f2644a2cSMark Brown { "Right Input Mixer", NULL, "LINPUT3" }, 401f2644a2cSMark Brown 402f2644a2cSMark Brown { "Left ADC", NULL, "Left Input Mixer" }, 403f2644a2cSMark Brown { "Right ADC", NULL, "Right Input Mixer" }, 404f2644a2cSMark Brown 405f2644a2cSMark Brown { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" }, 406f2644a2cSMark Brown { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} , 407f2644a2cSMark Brown { "Left Output Mixer", "PCM Playback Switch", "Left DAC" }, 408f2644a2cSMark Brown 409f2644a2cSMark Brown { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" }, 410f2644a2cSMark Brown { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } , 411f2644a2cSMark Brown { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, 412f2644a2cSMark Brown 413f2644a2cSMark Brown { "LOUT1 PGA", NULL, "Left Output Mixer" }, 414f2644a2cSMark Brown { "ROUT1 PGA", NULL, "Right Output Mixer" }, 415f2644a2cSMark Brown 416f2644a2cSMark Brown { "HP_L", NULL, "LOUT1 PGA" }, 417f2644a2cSMark Brown { "HP_R", NULL, "ROUT1 PGA" }, 418f2644a2cSMark Brown 419f2644a2cSMark Brown { "Left Speaker PGA", NULL, "Left Output Mixer" }, 420f2644a2cSMark Brown { "Right Speaker PGA", NULL, "Right Output Mixer" }, 421f2644a2cSMark Brown 422f2644a2cSMark Brown { "Left Speaker Output", NULL, "Left Speaker PGA" }, 423f2644a2cSMark Brown { "Right Speaker Output", NULL, "Right Speaker PGA" }, 424f2644a2cSMark Brown 425f2644a2cSMark Brown { "SPK_LN", NULL, "Left Speaker Output" }, 426f2644a2cSMark Brown { "SPK_LP", NULL, "Left Speaker Output" }, 427f2644a2cSMark Brown { "SPK_RN", NULL, "Right Speaker Output" }, 428f2644a2cSMark Brown { "SPK_RP", NULL, "Right Speaker Output" }, 429913d7b4cSMark Brown }; 430913d7b4cSMark Brown 431913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_out3[] = { 432913d7b4cSMark Brown { "Mono Output Mixer", "Left Switch", "Left Output Mixer" }, 433913d7b4cSMark Brown { "Mono Output Mixer", "Right Switch", "Right Output Mixer" }, 434f2644a2cSMark Brown 435f2644a2cSMark Brown { "OUT3", NULL, "Mono Output Mixer", } 436f2644a2cSMark Brown }; 437f2644a2cSMark Brown 438913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_capless[] = { 439913d7b4cSMark Brown { "HP_L", NULL, "OUT3 VMID" }, 440913d7b4cSMark Brown { "HP_R", NULL, "OUT3 VMID" }, 441913d7b4cSMark Brown 442913d7b4cSMark Brown { "OUT3 VMID", NULL, "Left Output Mixer" }, 443913d7b4cSMark Brown { "OUT3 VMID", NULL, "Right Output Mixer" }, 444913d7b4cSMark Brown }; 445913d7b4cSMark Brown 446f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec) 447f2644a2cSMark Brown { 448b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 449e2280c90SZidan Wang struct wm8960_data *pdata = &wm8960->pdata; 450ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 451913d7b4cSMark Brown struct snd_soc_dapm_widget *w; 452913d7b4cSMark Brown 453ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, 454f2644a2cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets)); 455f2644a2cSMark Brown 456ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); 457f2644a2cSMark Brown 458913d7b4cSMark Brown /* In capless mode OUT3 is used to provide VMID for the 459913d7b4cSMark Brown * headphone outputs, otherwise it is used as a mono mixer. 460913d7b4cSMark Brown */ 461913d7b4cSMark Brown if (pdata && pdata->capless) { 462ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, 463913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_capless)); 464913d7b4cSMark Brown 465ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_capless, 466913d7b4cSMark Brown ARRAY_SIZE(audio_paths_capless)); 467913d7b4cSMark Brown } else { 468ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, 469913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_out3)); 470913d7b4cSMark Brown 471ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_out3, 472913d7b4cSMark Brown ARRAY_SIZE(audio_paths_out3)); 473913d7b4cSMark Brown } 474913d7b4cSMark Brown 475913d7b4cSMark Brown /* We need to power up the headphone output stage out of 476913d7b4cSMark Brown * sequence for capless mode. To save scanning the widget 477913d7b4cSMark Brown * list each time to find the desired power state do so now 478913d7b4cSMark Brown * and save the result. 479913d7b4cSMark Brown */ 48000200107SLars-Peter Clausen list_for_each_entry(w, &codec->component.card->widgets, list) { 48197c866deSJarkko Nikula if (w->dapm != &codec->dapm) 48297c866deSJarkko Nikula continue; 483913d7b4cSMark Brown if (strcmp(w->name, "LOUT1 PGA") == 0) 484913d7b4cSMark Brown wm8960->lout1 = w; 485913d7b4cSMark Brown if (strcmp(w->name, "ROUT1 PGA") == 0) 486913d7b4cSMark Brown wm8960->rout1 = w; 487913d7b4cSMark Brown if (strcmp(w->name, "OUT3 VMID") == 0) 488913d7b4cSMark Brown wm8960->out3 = w; 489913d7b4cSMark Brown } 490913d7b4cSMark Brown 491f2644a2cSMark Brown return 0; 492f2644a2cSMark Brown } 493f2644a2cSMark Brown 494f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, 495f2644a2cSMark Brown unsigned int fmt) 496f2644a2cSMark Brown { 497f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 498f2644a2cSMark Brown u16 iface = 0; 499f2644a2cSMark Brown 500f2644a2cSMark Brown /* set master/slave audio interface */ 501f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 502f2644a2cSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 503f2644a2cSMark Brown iface |= 0x0040; 504f2644a2cSMark Brown break; 505f2644a2cSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 506f2644a2cSMark Brown break; 507f2644a2cSMark Brown default: 508f2644a2cSMark Brown return -EINVAL; 509f2644a2cSMark Brown } 510f2644a2cSMark Brown 511f2644a2cSMark Brown /* interface format */ 512f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 513f2644a2cSMark Brown case SND_SOC_DAIFMT_I2S: 514f2644a2cSMark Brown iface |= 0x0002; 515f2644a2cSMark Brown break; 516f2644a2cSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 517f2644a2cSMark Brown break; 518f2644a2cSMark Brown case SND_SOC_DAIFMT_LEFT_J: 519f2644a2cSMark Brown iface |= 0x0001; 520f2644a2cSMark Brown break; 521f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_A: 522f2644a2cSMark Brown iface |= 0x0003; 523f2644a2cSMark Brown break; 524f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_B: 525f2644a2cSMark Brown iface |= 0x0013; 526f2644a2cSMark Brown break; 527f2644a2cSMark Brown default: 528f2644a2cSMark Brown return -EINVAL; 529f2644a2cSMark Brown } 530f2644a2cSMark Brown 531f2644a2cSMark Brown /* clock inversion */ 532f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 533f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_NF: 534f2644a2cSMark Brown break; 535f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_IF: 536f2644a2cSMark Brown iface |= 0x0090; 537f2644a2cSMark Brown break; 538f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_NF: 539f2644a2cSMark Brown iface |= 0x0080; 540f2644a2cSMark Brown break; 541f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_IF: 542f2644a2cSMark Brown iface |= 0x0010; 543f2644a2cSMark Brown break; 544f2644a2cSMark Brown default: 545f2644a2cSMark Brown return -EINVAL; 546f2644a2cSMark Brown } 547f2644a2cSMark Brown 548f2644a2cSMark Brown /* set iface */ 54917a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 550f2644a2cSMark Brown return 0; 551f2644a2cSMark Brown } 552f2644a2cSMark Brown 553db059c0fSMark Brown static struct { 554db059c0fSMark Brown int rate; 555db059c0fSMark Brown unsigned int val; 556db059c0fSMark Brown } alc_rates[] = { 557db059c0fSMark Brown { 48000, 0 }, 558db059c0fSMark Brown { 44100, 0 }, 559db059c0fSMark Brown { 32000, 1 }, 560db059c0fSMark Brown { 22050, 2 }, 561db059c0fSMark Brown { 24000, 2 }, 562db059c0fSMark Brown { 16000, 3 }, 56322ee76daSZidan Wang { 11025, 4 }, 564db059c0fSMark Brown { 12000, 4 }, 565db059c0fSMark Brown { 8000, 5 }, 566db059c0fSMark Brown }; 567db059c0fSMark Brown 5680e50b51aSZidan Wang /* Multiply 256 for internal 256 div */ 5690e50b51aSZidan Wang static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; 5700e50b51aSZidan Wang 5710e50b51aSZidan Wang /* Multiply 10 to eliminate decimials */ 5720e50b51aSZidan Wang static const int bclk_divs[] = { 5730e50b51aSZidan Wang 10, 15, 20, 30, 40, 55, 60, 80, 110, 5740e50b51aSZidan Wang 120, 160, 220, 240, 320, 320, 320 5750e50b51aSZidan Wang }; 5760e50b51aSZidan Wang 5770e50b51aSZidan Wang static void wm8960_configure_clocking(struct snd_soc_codec *codec, 5780e50b51aSZidan Wang bool tx, int lrclk) 5790e50b51aSZidan Wang { 5800e50b51aSZidan Wang struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 5810e50b51aSZidan Wang u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); 5820e50b51aSZidan Wang u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); 5830e50b51aSZidan Wang u32 sysclk; 5840e50b51aSZidan Wang int i, j; 5850e50b51aSZidan Wang 5860e50b51aSZidan Wang if (!(iface1 & (1<<6))) { 5870e50b51aSZidan Wang dev_dbg(codec->dev, 5880e50b51aSZidan Wang "Codec is slave mode, no need to configure clock\n"); 5890e50b51aSZidan Wang return; 5900e50b51aSZidan Wang } 5910e50b51aSZidan Wang 5920e50b51aSZidan Wang if (!wm8960->sysclk) { 5930e50b51aSZidan Wang dev_dbg(codec->dev, "No SYSCLK configured\n"); 5940e50b51aSZidan Wang return; 5950e50b51aSZidan Wang } 5960e50b51aSZidan Wang 5970e50b51aSZidan Wang if (!wm8960->bclk || !lrclk) { 5980e50b51aSZidan Wang dev_dbg(codec->dev, "No audio clocks configured\n"); 5990e50b51aSZidan Wang return; 6000e50b51aSZidan Wang } 6010e50b51aSZidan Wang 6020e50b51aSZidan Wang for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) { 6030e50b51aSZidan Wang if (wm8960->sysclk == lrclk * dac_divs[i]) { 6040e50b51aSZidan Wang for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) { 6050e50b51aSZidan Wang sysclk = wm8960->bclk * bclk_divs[j] / 10; 6060e50b51aSZidan Wang if (wm8960->sysclk == sysclk) 6070e50b51aSZidan Wang break; 6080e50b51aSZidan Wang } 6090e50b51aSZidan Wang if(j != ARRAY_SIZE(bclk_divs)) 6100e50b51aSZidan Wang break; 6110e50b51aSZidan Wang } 6120e50b51aSZidan Wang } 6130e50b51aSZidan Wang 6140e50b51aSZidan Wang if (i == ARRAY_SIZE(dac_divs)) { 6150e50b51aSZidan Wang dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); 6160e50b51aSZidan Wang return; 6170e50b51aSZidan Wang } 6180e50b51aSZidan Wang 6190e50b51aSZidan Wang /* 6200e50b51aSZidan Wang * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC 6210e50b51aSZidan Wang * pin is used as a frame clock for ADCs and DACs. 6220e50b51aSZidan Wang */ 6230e50b51aSZidan Wang if (iface2 & (1<<6)) 6240e50b51aSZidan Wang snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); 6250e50b51aSZidan Wang else if (tx) 6260e50b51aSZidan Wang snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); 6270e50b51aSZidan Wang else if (!tx) 6280e50b51aSZidan Wang snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); 6290e50b51aSZidan Wang 6300e50b51aSZidan Wang /* configure bit clock */ 6310e50b51aSZidan Wang snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); 6320e50b51aSZidan Wang } 6330e50b51aSZidan Wang 634f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream, 635f2644a2cSMark Brown struct snd_pcm_hw_params *params, 636f2644a2cSMark Brown struct snd_soc_dai *dai) 637f2644a2cSMark Brown { 638e6968a17SMark Brown struct snd_soc_codec *codec = dai->codec; 639afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 64017a52fd6SMark Brown u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; 6410e50b51aSZidan Wang bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 642db059c0fSMark Brown int i; 643f2644a2cSMark Brown 6440e50b51aSZidan Wang wm8960->bclk = snd_soc_params_to_bclk(params); 6450e50b51aSZidan Wang if (params_channels(params) == 1) 6460e50b51aSZidan Wang wm8960->bclk *= 2; 6470e50b51aSZidan Wang 648f2644a2cSMark Brown /* bit size */ 64939e9cc46SMark Brown switch (params_width(params)) { 65039e9cc46SMark Brown case 16: 651f2644a2cSMark Brown break; 65239e9cc46SMark Brown case 20: 653f2644a2cSMark Brown iface |= 0x0004; 654f2644a2cSMark Brown break; 65539e9cc46SMark Brown case 24: 656f2644a2cSMark Brown iface |= 0x0008; 657f2644a2cSMark Brown break; 6587a8c7867SZidan Wang case 32: 6597a8c7867SZidan Wang /* right justify mode does not support 32 word length */ 6607a8c7867SZidan Wang if ((iface & 0x3) != 0) { 6617a8c7867SZidan Wang iface |= 0x000c; 6627a8c7867SZidan Wang break; 6637a8c7867SZidan Wang } 6644c2474c0STimur Tabi default: 66539e9cc46SMark Brown dev_err(codec->dev, "unsupported width %d\n", 66639e9cc46SMark Brown params_width(params)); 6674c2474c0STimur Tabi return -EINVAL; 668f2644a2cSMark Brown } 669f2644a2cSMark Brown 670afd6d36aSMark Brown /* Update filters for the new rate */ 671afd6d36aSMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 672afd6d36aSMark Brown wm8960->playback_fs = params_rate(params); 673afd6d36aSMark Brown wm8960_set_deemph(codec); 674db059c0fSMark Brown } else { 675db059c0fSMark Brown for (i = 0; i < ARRAY_SIZE(alc_rates); i++) 676db059c0fSMark Brown if (alc_rates[i].rate == params_rate(params)) 677db059c0fSMark Brown snd_soc_update_bits(codec, 678db059c0fSMark Brown WM8960_ADDCTL3, 0x7, 679db059c0fSMark Brown alc_rates[i].val); 680afd6d36aSMark Brown } 681afd6d36aSMark Brown 682f2644a2cSMark Brown /* set iface */ 68317a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 6840e50b51aSZidan Wang 6850e50b51aSZidan Wang wm8960_configure_clocking(codec, tx, params_rate(params)); 6860e50b51aSZidan Wang 687f2644a2cSMark Brown return 0; 688f2644a2cSMark Brown } 689f2644a2cSMark Brown 690f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute) 691f2644a2cSMark Brown { 692f2644a2cSMark Brown struct snd_soc_codec *codec = dai->codec; 693f2644a2cSMark Brown 694f2644a2cSMark Brown if (mute) 69516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); 696f2644a2cSMark Brown else 69716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); 698f2644a2cSMark Brown return 0; 699f2644a2cSMark Brown } 700f2644a2cSMark Brown 701913d7b4cSMark Brown static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, 702f2644a2cSMark Brown enum snd_soc_bias_level level) 703f2644a2cSMark Brown { 7040ebe36c6SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 70575aa8868SZidan Wang int ret; 7060ebe36c6SMark Brown 707f2644a2cSMark Brown switch (level) { 708f2644a2cSMark Brown case SND_SOC_BIAS_ON: 709f2644a2cSMark Brown break; 710f2644a2cSMark Brown 711f2644a2cSMark Brown case SND_SOC_BIAS_PREPARE: 71275aa8868SZidan Wang switch (codec->dapm.bias_level) { 71375aa8868SZidan Wang case SND_SOC_BIAS_STANDBY: 71475aa8868SZidan Wang if (!IS_ERR(wm8960->mclk)) { 71575aa8868SZidan Wang ret = clk_prepare_enable(wm8960->mclk); 71675aa8868SZidan Wang if (ret) { 71775aa8868SZidan Wang dev_err(codec->dev, 71875aa8868SZidan Wang "Failed to enable MCLK: %d\n", 71975aa8868SZidan Wang ret); 72075aa8868SZidan Wang return ret; 72175aa8868SZidan Wang } 72275aa8868SZidan Wang } 72375aa8868SZidan Wang 724f2644a2cSMark Brown /* Set VMID to 2x50k */ 72516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); 726f2644a2cSMark Brown break; 727f2644a2cSMark Brown 72875aa8868SZidan Wang case SND_SOC_BIAS_ON: 72975aa8868SZidan Wang if (!IS_ERR(wm8960->mclk)) 73075aa8868SZidan Wang clk_disable_unprepare(wm8960->mclk); 73175aa8868SZidan Wang break; 73275aa8868SZidan Wang 73375aa8868SZidan Wang default: 73475aa8868SZidan Wang break; 73575aa8868SZidan Wang } 73675aa8868SZidan Wang 73775aa8868SZidan Wang break; 73875aa8868SZidan Wang 739f2644a2cSMark Brown case SND_SOC_BIAS_STANDBY: 740ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 7410ebe36c6SMark Brown regcache_sync(wm8960->regmap); 742bc45df2dSAxel Lin 743f2644a2cSMark Brown /* Enable anti-pop features */ 74417a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 745f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 746f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 747f2644a2cSMark Brown 748f2644a2cSMark Brown /* Enable & ramp VMID at 2x50k */ 74916b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); 750f2644a2cSMark Brown msleep(100); 751f2644a2cSMark Brown 752f2644a2cSMark Brown /* Enable VREF */ 75316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, 75416b24881SAxel Lin WM8960_VREF); 755f2644a2cSMark Brown 756f2644a2cSMark Brown /* Disable anti-pop features */ 75717a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); 758f2644a2cSMark Brown } 759f2644a2cSMark Brown 760f2644a2cSMark Brown /* Set VMID to 2x250k */ 76116b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); 762f2644a2cSMark Brown break; 763f2644a2cSMark Brown 764f2644a2cSMark Brown case SND_SOC_BIAS_OFF: 765f2644a2cSMark Brown /* Enable anti-pop features */ 76617a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 767f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 768f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 769f2644a2cSMark Brown 770f2644a2cSMark Brown /* Disable VMID and VREF, let them discharge */ 77117a52fd6SMark Brown snd_soc_write(codec, WM8960_POWER1, 0); 772f2644a2cSMark Brown msleep(600); 773913d7b4cSMark Brown break; 774913d7b4cSMark Brown } 775f2644a2cSMark Brown 776ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 777913d7b4cSMark Brown 778913d7b4cSMark Brown return 0; 779913d7b4cSMark Brown } 780913d7b4cSMark Brown 781913d7b4cSMark Brown static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, 782913d7b4cSMark Brown enum snd_soc_bias_level level) 783913d7b4cSMark Brown { 784b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 78575aa8868SZidan Wang int reg, ret; 786913d7b4cSMark Brown 787913d7b4cSMark Brown switch (level) { 788913d7b4cSMark Brown case SND_SOC_BIAS_ON: 789913d7b4cSMark Brown break; 790913d7b4cSMark Brown 791913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 792ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 793913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 794913d7b4cSMark Brown /* Enable anti pop mode */ 795913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 796913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 797913d7b4cSMark Brown WM8960_BUFDCOPEN, 798913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 799913d7b4cSMark Brown WM8960_BUFDCOPEN); 800913d7b4cSMark Brown 801913d7b4cSMark Brown /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ 802913d7b4cSMark Brown reg = 0; 803913d7b4cSMark Brown if (wm8960->lout1 && wm8960->lout1->power) 804913d7b4cSMark Brown reg |= WM8960_PWR2_LOUT1; 805913d7b4cSMark Brown if (wm8960->rout1 && wm8960->rout1->power) 806913d7b4cSMark Brown reg |= WM8960_PWR2_ROUT1; 807913d7b4cSMark Brown if (wm8960->out3 && wm8960->out3->power) 808913d7b4cSMark Brown reg |= WM8960_PWR2_OUT3; 809913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER2, 810913d7b4cSMark Brown WM8960_PWR2_LOUT1 | 811913d7b4cSMark Brown WM8960_PWR2_ROUT1 | 812913d7b4cSMark Brown WM8960_PWR2_OUT3, reg); 813913d7b4cSMark Brown 814913d7b4cSMark Brown /* Enable VMID at 2*50k */ 815913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 816913d7b4cSMark Brown WM8960_VMID_MASK, 0x80); 817913d7b4cSMark Brown 818913d7b4cSMark Brown /* Ramp */ 819913d7b4cSMark Brown msleep(100); 820913d7b4cSMark Brown 821913d7b4cSMark Brown /* Enable VREF */ 822913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 823913d7b4cSMark Brown WM8960_VREF, WM8960_VREF); 824913d7b4cSMark Brown 825913d7b4cSMark Brown msleep(100); 82675aa8868SZidan Wang 82775aa8868SZidan Wang if (!IS_ERR(wm8960->mclk)) { 82875aa8868SZidan Wang ret = clk_prepare_enable(wm8960->mclk); 82975aa8868SZidan Wang if (ret) { 83075aa8868SZidan Wang dev_err(codec->dev, 83175aa8868SZidan Wang "Failed to enable MCLK: %d\n", 83275aa8868SZidan Wang ret); 83375aa8868SZidan Wang return ret; 83475aa8868SZidan Wang } 83575aa8868SZidan Wang } 836913d7b4cSMark Brown break; 837913d7b4cSMark Brown 838913d7b4cSMark Brown case SND_SOC_BIAS_ON: 83975aa8868SZidan Wang if (!IS_ERR(wm8960->mclk)) 84075aa8868SZidan Wang clk_disable_unprepare(wm8960->mclk); 84175aa8868SZidan Wang 842913d7b4cSMark Brown /* Enable anti-pop mode */ 843913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 844913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 845913d7b4cSMark Brown WM8960_BUFDCOPEN, 846913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 847913d7b4cSMark Brown WM8960_BUFDCOPEN); 848913d7b4cSMark Brown 849913d7b4cSMark Brown /* Disable VMID and VREF */ 850913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 851913d7b4cSMark Brown WM8960_VREF | WM8960_VMID_MASK, 0); 852913d7b4cSMark Brown break; 853913d7b4cSMark Brown 854bc45df2dSAxel Lin case SND_SOC_BIAS_OFF: 8550ebe36c6SMark Brown regcache_sync(wm8960->regmap); 856bc45df2dSAxel Lin break; 857913d7b4cSMark Brown default: 858913d7b4cSMark Brown break; 859913d7b4cSMark Brown } 860913d7b4cSMark Brown break; 861913d7b4cSMark Brown 862913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 863ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 864913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 865913d7b4cSMark Brown /* Disable HP discharge */ 866913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP2, 867913d7b4cSMark Brown WM8960_DISOP | WM8960_DRES_MASK, 868913d7b4cSMark Brown 0); 869913d7b4cSMark Brown 870913d7b4cSMark Brown /* Disable anti-pop features */ 871913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 872913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 873913d7b4cSMark Brown WM8960_BUFDCOPEN, 874913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 875913d7b4cSMark Brown WM8960_BUFDCOPEN); 876913d7b4cSMark Brown break; 877913d7b4cSMark Brown 878913d7b4cSMark Brown default: 879913d7b4cSMark Brown break; 880913d7b4cSMark Brown } 881913d7b4cSMark Brown break; 882913d7b4cSMark Brown 883913d7b4cSMark Brown case SND_SOC_BIAS_OFF: 884f2644a2cSMark Brown break; 885f2644a2cSMark Brown } 886f2644a2cSMark Brown 887ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 888f2644a2cSMark Brown 889f2644a2cSMark Brown return 0; 890f2644a2cSMark Brown } 891f2644a2cSMark Brown 892f2644a2cSMark Brown /* PLL divisors */ 893f2644a2cSMark Brown struct _pll_div { 894f2644a2cSMark Brown u32 pre_div:1; 895f2644a2cSMark Brown u32 n:4; 896f2644a2cSMark Brown u32 k:24; 897f2644a2cSMark Brown }; 898f2644a2cSMark Brown 899f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10 900f2644a2cSMark Brown * to allow rounding later */ 901f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10) 902f2644a2cSMark Brown 903f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target, 904f2644a2cSMark Brown struct _pll_div *pll_div) 905f2644a2cSMark Brown { 906f2644a2cSMark Brown unsigned long long Kpart; 907f2644a2cSMark Brown unsigned int K, Ndiv, Nmod; 908f2644a2cSMark Brown 909f2644a2cSMark Brown pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target); 910f2644a2cSMark Brown 911f2644a2cSMark Brown /* Scale up target to PLL operating frequency */ 912f2644a2cSMark Brown target *= 4; 913f2644a2cSMark Brown 914f2644a2cSMark Brown Ndiv = target / source; 915f2644a2cSMark Brown if (Ndiv < 6) { 916f2644a2cSMark Brown source >>= 1; 917f2644a2cSMark Brown pll_div->pre_div = 1; 918f2644a2cSMark Brown Ndiv = target / source; 919f2644a2cSMark Brown } else 920f2644a2cSMark Brown pll_div->pre_div = 0; 921f2644a2cSMark Brown 922f2644a2cSMark Brown if ((Ndiv < 6) || (Ndiv > 12)) { 923f2644a2cSMark Brown pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); 924f2644a2cSMark Brown return -EINVAL; 925f2644a2cSMark Brown } 926f2644a2cSMark Brown 927f2644a2cSMark Brown pll_div->n = Ndiv; 928f2644a2cSMark Brown Nmod = target % source; 929f2644a2cSMark Brown Kpart = FIXED_PLL_SIZE * (long long)Nmod; 930f2644a2cSMark Brown 931f2644a2cSMark Brown do_div(Kpart, source); 932f2644a2cSMark Brown 933f2644a2cSMark Brown K = Kpart & 0xFFFFFFFF; 934f2644a2cSMark Brown 935f2644a2cSMark Brown /* Check if we need to round */ 936f2644a2cSMark Brown if ((K % 10) >= 5) 937f2644a2cSMark Brown K += 5; 938f2644a2cSMark Brown 939f2644a2cSMark Brown /* Move down to proper range now rounding is done */ 940f2644a2cSMark Brown K /= 10; 941f2644a2cSMark Brown 942f2644a2cSMark Brown pll_div->k = K; 943f2644a2cSMark Brown 944f2644a2cSMark Brown pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n", 945f2644a2cSMark Brown pll_div->n, pll_div->k, pll_div->pre_div); 946f2644a2cSMark Brown 947f2644a2cSMark Brown return 0; 948f2644a2cSMark Brown } 949f2644a2cSMark Brown 95085488037SMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 95185488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 952f2644a2cSMark Brown { 953f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 954f2644a2cSMark Brown u16 reg; 955f2644a2cSMark Brown static struct _pll_div pll_div; 956f2644a2cSMark Brown int ret; 957f2644a2cSMark Brown 958f2644a2cSMark Brown if (freq_in && freq_out) { 959f2644a2cSMark Brown ret = pll_factors(freq_in, freq_out, &pll_div); 960f2644a2cSMark Brown if (ret != 0) 961f2644a2cSMark Brown return ret; 962f2644a2cSMark Brown } 963f2644a2cSMark Brown 964f2644a2cSMark Brown /* Disable the PLL: even if we are changing the frequency the 965f2644a2cSMark Brown * PLL needs to be disabled while we do so. */ 96616b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); 96716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); 968f2644a2cSMark Brown 969f2644a2cSMark Brown if (!freq_in || !freq_out) 970f2644a2cSMark Brown return 0; 971f2644a2cSMark Brown 97217a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; 973f2644a2cSMark Brown reg |= pll_div.pre_div << 4; 974f2644a2cSMark Brown reg |= pll_div.n; 975f2644a2cSMark Brown 976f2644a2cSMark Brown if (pll_div.k) { 977f2644a2cSMark Brown reg |= 0x20; 978f2644a2cSMark Brown 97985fa532bSMike Dyer snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); 98085fa532bSMike Dyer snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); 98185fa532bSMike Dyer snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); 982f2644a2cSMark Brown } 98317a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg); 984f2644a2cSMark Brown 985f2644a2cSMark Brown /* Turn it on */ 98616b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); 987f2644a2cSMark Brown msleep(250); 98816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); 989f2644a2cSMark Brown 990f2644a2cSMark Brown return 0; 991f2644a2cSMark Brown } 992f2644a2cSMark Brown 993f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 994f2644a2cSMark Brown int div_id, int div) 995f2644a2cSMark Brown { 996f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 997f2644a2cSMark Brown u16 reg; 998f2644a2cSMark Brown 999f2644a2cSMark Brown switch (div_id) { 1000f2644a2cSMark Brown case WM8960_SYSCLKDIV: 100117a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; 100217a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 1003f2644a2cSMark Brown break; 1004f2644a2cSMark Brown case WM8960_DACDIV: 100517a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; 100617a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 1007f2644a2cSMark Brown break; 1008f2644a2cSMark Brown case WM8960_OPCLKDIV: 100917a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; 101017a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg | div); 1011f2644a2cSMark Brown break; 1012f2644a2cSMark Brown case WM8960_DCLKDIV: 101317a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; 101417a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK2, reg | div); 1015f2644a2cSMark Brown break; 1016f2644a2cSMark Brown case WM8960_TOCLKSEL: 101717a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; 101817a52fd6SMark Brown snd_soc_write(codec, WM8960_ADDCTL1, reg | div); 1019f2644a2cSMark Brown break; 1020f2644a2cSMark Brown default: 1021f2644a2cSMark Brown return -EINVAL; 1022f2644a2cSMark Brown } 1023f2644a2cSMark Brown 1024f2644a2cSMark Brown return 0; 1025f2644a2cSMark Brown } 1026f2644a2cSMark Brown 1027f0fba2adSLiam Girdwood static int wm8960_set_bias_level(struct snd_soc_codec *codec, 1028f0fba2adSLiam Girdwood enum snd_soc_bias_level level) 1029f0fba2adSLiam Girdwood { 1030f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 1031f0fba2adSLiam Girdwood 1032f0fba2adSLiam Girdwood return wm8960->set_bias_level(codec, level); 1033f0fba2adSLiam Girdwood } 1034f0fba2adSLiam Girdwood 10350e50b51aSZidan Wang static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, 10360e50b51aSZidan Wang unsigned int freq, int dir) 10370e50b51aSZidan Wang { 10380e50b51aSZidan Wang struct snd_soc_codec *codec = dai->codec; 10390e50b51aSZidan Wang struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 10400e50b51aSZidan Wang 10410e50b51aSZidan Wang switch (clk_id) { 10420e50b51aSZidan Wang case WM8960_SYSCLK_MCLK: 10430e50b51aSZidan Wang snd_soc_update_bits(codec, WM8960_CLOCK1, 10440e50b51aSZidan Wang 0x1, WM8960_SYSCLK_MCLK); 10450e50b51aSZidan Wang break; 10460e50b51aSZidan Wang case WM8960_SYSCLK_PLL: 10470e50b51aSZidan Wang snd_soc_update_bits(codec, WM8960_CLOCK1, 10480e50b51aSZidan Wang 0x1, WM8960_SYSCLK_PLL); 10490e50b51aSZidan Wang break; 10500e50b51aSZidan Wang default: 10510e50b51aSZidan Wang return -EINVAL; 10520e50b51aSZidan Wang } 10530e50b51aSZidan Wang 10540e50b51aSZidan Wang wm8960->sysclk = freq; 10550e50b51aSZidan Wang 10560e50b51aSZidan Wang return 0; 10570e50b51aSZidan Wang } 10580e50b51aSZidan Wang 1059f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000 1060f2644a2cSMark Brown 1061f2644a2cSMark Brown #define WM8960_FORMATS \ 1062f2644a2cSMark Brown (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 10637a8c7867SZidan Wang SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 1064f2644a2cSMark Brown 106585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8960_dai_ops = { 1066f2644a2cSMark Brown .hw_params = wm8960_hw_params, 1067f2644a2cSMark Brown .digital_mute = wm8960_mute, 1068f2644a2cSMark Brown .set_fmt = wm8960_set_dai_fmt, 1069f2644a2cSMark Brown .set_clkdiv = wm8960_set_dai_clkdiv, 1070f2644a2cSMark Brown .set_pll = wm8960_set_dai_pll, 10710e50b51aSZidan Wang .set_sysclk = wm8960_set_dai_sysclk, 1072f2644a2cSMark Brown }; 1073f2644a2cSMark Brown 1074f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8960_dai = { 1075f0fba2adSLiam Girdwood .name = "wm8960-hifi", 1076f2644a2cSMark Brown .playback = { 1077f2644a2cSMark Brown .stream_name = "Playback", 1078f2644a2cSMark Brown .channels_min = 1, 1079f2644a2cSMark Brown .channels_max = 2, 1080f2644a2cSMark Brown .rates = WM8960_RATES, 1081f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 1082f2644a2cSMark Brown .capture = { 1083f2644a2cSMark Brown .stream_name = "Capture", 1084f2644a2cSMark Brown .channels_min = 1, 1085f2644a2cSMark Brown .channels_max = 2, 1086f2644a2cSMark Brown .rates = WM8960_RATES, 1087f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 1088f2644a2cSMark Brown .ops = &wm8960_dai_ops, 1089f2644a2cSMark Brown .symmetric_rates = 1, 1090f2644a2cSMark Brown }; 1091f2644a2cSMark Brown 1092f0fba2adSLiam Girdwood static int wm8960_probe(struct snd_soc_codec *codec) 1093f2644a2cSMark Brown { 1094f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 1095e2280c90SZidan Wang struct wm8960_data *pdata = &wm8960->pdata; 1096f2644a2cSMark Brown 1097913d7b4cSMark Brown if (pdata->capless) 1098f0fba2adSLiam Girdwood wm8960->set_bias_level = wm8960_set_bias_level_capless; 1099e2280c90SZidan Wang else 1100e2280c90SZidan Wang wm8960->set_bias_level = wm8960_set_bias_level_out3; 1101f2644a2cSMark Brown 1102022658beSLiam Girdwood snd_soc_add_codec_controls(codec, wm8960_snd_controls, 1103f0fba2adSLiam Girdwood ARRAY_SIZE(wm8960_snd_controls)); 1104f0fba2adSLiam Girdwood wm8960_add_widgets(codec); 1105f2644a2cSMark Brown 1106f2644a2cSMark Brown return 0; 1107f2644a2cSMark Brown } 1108f2644a2cSMark Brown 1109f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8960 = { 1110f0fba2adSLiam Girdwood .probe = wm8960_probe, 1111f0fba2adSLiam Girdwood .set_bias_level = wm8960_set_bias_level, 11120a87a6e1SLars-Peter Clausen .suspend_bias_off = true, 11130ebe36c6SMark Brown }; 11140ebe36c6SMark Brown 11150ebe36c6SMark Brown static const struct regmap_config wm8960_regmap = { 11160ebe36c6SMark Brown .reg_bits = 7, 11170ebe36c6SMark Brown .val_bits = 9, 11180ebe36c6SMark Brown .max_register = WM8960_PLL4, 11190ebe36c6SMark Brown 11200ebe36c6SMark Brown .reg_defaults = wm8960_reg_defaults, 11210ebe36c6SMark Brown .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), 11220ebe36c6SMark Brown .cache_type = REGCACHE_RBTREE, 11230ebe36c6SMark Brown 11240ebe36c6SMark Brown .volatile_reg = wm8960_volatile, 1125f0fba2adSLiam Girdwood }; 1126f0fba2adSLiam Girdwood 1127e2280c90SZidan Wang static void wm8960_set_pdata_from_of(struct i2c_client *i2c, 1128e2280c90SZidan Wang struct wm8960_data *pdata) 1129e2280c90SZidan Wang { 1130e2280c90SZidan Wang const struct device_node *np = i2c->dev.of_node; 1131e2280c90SZidan Wang 1132e2280c90SZidan Wang if (of_property_read_bool(np, "wlf,capless")) 1133e2280c90SZidan Wang pdata->capless = true; 1134e2280c90SZidan Wang 1135e2280c90SZidan Wang if (of_property_read_bool(np, "wlf,shared-lrclk")) 1136e2280c90SZidan Wang pdata->shared_lrclk = true; 1137e2280c90SZidan Wang } 1138e2280c90SZidan Wang 11397a79e94eSBill Pemberton static int wm8960_i2c_probe(struct i2c_client *i2c, 1140f2644a2cSMark Brown const struct i2c_device_id *id) 1141f2644a2cSMark Brown { 114237061631SMark Brown struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); 1143f2644a2cSMark Brown struct wm8960_priv *wm8960; 1144f0fba2adSLiam Girdwood int ret; 1145f2644a2cSMark Brown 1146b9791c01SMark Brown wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), 1147b9791c01SMark Brown GFP_KERNEL); 1148f2644a2cSMark Brown if (wm8960 == NULL) 1149f2644a2cSMark Brown return -ENOMEM; 1150f2644a2cSMark Brown 115175aa8868SZidan Wang wm8960->mclk = devm_clk_get(&i2c->dev, "mclk"); 115275aa8868SZidan Wang if (IS_ERR(wm8960->mclk)) { 115375aa8868SZidan Wang if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) 115475aa8868SZidan Wang return -EPROBE_DEFER; 115575aa8868SZidan Wang } 115675aa8868SZidan Wang 1157c5e6f5faSSachin Kamat wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); 11580ebe36c6SMark Brown if (IS_ERR(wm8960->regmap)) 11590ebe36c6SMark Brown return PTR_ERR(wm8960->regmap); 11600ebe36c6SMark Brown 1161e2280c90SZidan Wang if (pdata) 1162e2280c90SZidan Wang memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data)); 1163e2280c90SZidan Wang else if (i2c->dev.of_node) 1164e2280c90SZidan Wang wm8960_set_pdata_from_of(i2c, &wm8960->pdata); 1165e2280c90SZidan Wang 11663ad5e861SZidan Wang ret = wm8960_reset(wm8960->regmap); 11673ad5e861SZidan Wang if (ret != 0) { 11683ad5e861SZidan Wang dev_err(&i2c->dev, "Failed to issue reset\n"); 11693ad5e861SZidan Wang return ret; 11703ad5e861SZidan Wang } 11713ad5e861SZidan Wang 11723ad5e861SZidan Wang if (wm8960->pdata.shared_lrclk) { 117337061631SMark Brown ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 117437061631SMark Brown 0x4, 0x4); 117537061631SMark Brown if (ret != 0) { 117637061631SMark Brown dev_err(&i2c->dev, "Failed to enable LRCM: %d\n", 117737061631SMark Brown ret); 117837061631SMark Brown return ret; 117937061631SMark Brown } 118037061631SMark Brown } 118137061631SMark Brown 11823ad5e861SZidan Wang /* Latch the update bits */ 11833ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100); 11843ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100); 11853ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100); 11863ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100); 11873ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100); 11883ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100); 11893ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100); 11903ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100); 11913ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100); 11923ad5e861SZidan Wang regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100); 11933ad5e861SZidan Wang 1194f2644a2cSMark Brown i2c_set_clientdata(i2c, wm8960); 1195f2644a2cSMark Brown 1196f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 1197f0fba2adSLiam Girdwood &soc_codec_dev_wm8960, &wm8960_dai, 1); 1198b9791c01SMark Brown 1199f0fba2adSLiam Girdwood return ret; 1200f2644a2cSMark Brown } 1201f2644a2cSMark Brown 12027a79e94eSBill Pemberton static int wm8960_i2c_remove(struct i2c_client *client) 1203f2644a2cSMark Brown { 1204f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 1205f2644a2cSMark Brown return 0; 1206f2644a2cSMark Brown } 1207f2644a2cSMark Brown 1208f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = { 1209f2644a2cSMark Brown { "wm8960", 0 }, 1210f2644a2cSMark Brown { } 1211f2644a2cSMark Brown }; 1212f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); 1213f2644a2cSMark Brown 1214e2280c90SZidan Wang static const struct of_device_id wm8960_of_match[] = { 1215e2280c90SZidan Wang { .compatible = "wlf,wm8960", }, 1216e2280c90SZidan Wang { } 1217e2280c90SZidan Wang }; 1218e2280c90SZidan Wang MODULE_DEVICE_TABLE(of, wm8960_of_match); 1219e2280c90SZidan Wang 1220f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = { 1221f2644a2cSMark Brown .driver = { 1222091edccfSMark Brown .name = "wm8960", 1223f2644a2cSMark Brown .owner = THIS_MODULE, 1224e2280c90SZidan Wang .of_match_table = wm8960_of_match, 1225f2644a2cSMark Brown }, 1226f2644a2cSMark Brown .probe = wm8960_i2c_probe, 12277a79e94eSBill Pemberton .remove = wm8960_i2c_remove, 1228f2644a2cSMark Brown .id_table = wm8960_i2c_id, 1229f2644a2cSMark Brown }; 1230f2644a2cSMark Brown 12313c010e60SSachin Kamat module_i2c_driver(wm8960_i2c_driver); 1232f2644a2cSMark Brown 1233f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver"); 1234f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood"); 1235f2644a2cSMark Brown MODULE_LICENSE("GPL"); 1236