1f2644a2cSMark Brown /* 2f2644a2cSMark Brown * wm8960.c -- WM8960 ALSA SoC Audio driver 3f2644a2cSMark Brown * 4f2644a2cSMark Brown * Author: Liam Girdwood 5f2644a2cSMark Brown * 6f2644a2cSMark Brown * This program is free software; you can redistribute it and/or modify 7f2644a2cSMark Brown * it under the terms of the GNU General Public License version 2 as 8f2644a2cSMark Brown * published by the Free Software Foundation. 9f2644a2cSMark Brown */ 10f2644a2cSMark Brown 11f2644a2cSMark Brown #include <linux/module.h> 12f2644a2cSMark Brown #include <linux/moduleparam.h> 13f2644a2cSMark Brown #include <linux/init.h> 14f2644a2cSMark Brown #include <linux/delay.h> 15f2644a2cSMark Brown #include <linux/pm.h> 16f2644a2cSMark Brown #include <linux/i2c.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 18f2644a2cSMark Brown #include <sound/core.h> 19f2644a2cSMark Brown #include <sound/pcm.h> 20f2644a2cSMark Brown #include <sound/pcm_params.h> 21f2644a2cSMark Brown #include <sound/soc.h> 22f2644a2cSMark Brown #include <sound/initval.h> 23f2644a2cSMark Brown #include <sound/tlv.h> 24b6877a47SMark Brown #include <sound/wm8960.h> 25f2644a2cSMark Brown 26f2644a2cSMark Brown #include "wm8960.h" 27f2644a2cSMark Brown 28f2644a2cSMark Brown /* R25 - Power 1 */ 29913d7b4cSMark Brown #define WM8960_VMID_MASK 0x180 30f2644a2cSMark Brown #define WM8960_VREF 0x40 31f2644a2cSMark Brown 32913d7b4cSMark Brown /* R26 - Power 2 */ 33913d7b4cSMark Brown #define WM8960_PWR2_LOUT1 0x40 34913d7b4cSMark Brown #define WM8960_PWR2_ROUT1 0x20 35913d7b4cSMark Brown #define WM8960_PWR2_OUT3 0x02 36913d7b4cSMark Brown 37f2644a2cSMark Brown /* R28 - Anti-pop 1 */ 38f2644a2cSMark Brown #define WM8960_POBCTRL 0x80 39f2644a2cSMark Brown #define WM8960_BUFDCOPEN 0x10 40f2644a2cSMark Brown #define WM8960_BUFIOEN 0x08 41f2644a2cSMark Brown #define WM8960_SOFT_ST 0x04 42f2644a2cSMark Brown #define WM8960_HPSTBY 0x01 43f2644a2cSMark Brown 44f2644a2cSMark Brown /* R29 - Anti-pop 2 */ 45f2644a2cSMark Brown #define WM8960_DISOP 0x40 46913d7b4cSMark Brown #define WM8960_DRES_MASK 0x30 47f2644a2cSMark Brown 48f2644a2cSMark Brown /* 49f2644a2cSMark Brown * wm8960 register cache 50f2644a2cSMark Brown * We can't read the WM8960 register space when we are 51f2644a2cSMark Brown * using 2 wire for device control, so we cache them instead. 52f2644a2cSMark Brown */ 53f2644a2cSMark Brown static const u16 wm8960_reg[WM8960_CACHEREGNUM] = { 54f2644a2cSMark Brown 0x0097, 0x0097, 0x0000, 0x0000, 55f2644a2cSMark Brown 0x0000, 0x0008, 0x0000, 0x000a, 56f2644a2cSMark Brown 0x01c0, 0x0000, 0x00ff, 0x00ff, 57f2644a2cSMark Brown 0x0000, 0x0000, 0x0000, 0x0000, 58f2644a2cSMark Brown 0x0000, 0x007b, 0x0100, 0x0032, 59f2644a2cSMark Brown 0x0000, 0x00c3, 0x00c3, 0x01c0, 60f2644a2cSMark Brown 0x0000, 0x0000, 0x0000, 0x0000, 61f2644a2cSMark Brown 0x0000, 0x0000, 0x0000, 0x0000, 62f2644a2cSMark Brown 0x0100, 0x0100, 0x0050, 0x0050, 63f2644a2cSMark Brown 0x0050, 0x0050, 0x0000, 0x0000, 64f2644a2cSMark Brown 0x0000, 0x0000, 0x0040, 0x0000, 65f2644a2cSMark Brown 0x0000, 0x0050, 0x0050, 0x0000, 66f2644a2cSMark Brown 0x0002, 0x0037, 0x004d, 0x0080, 67f2644a2cSMark Brown 0x0008, 0x0031, 0x0026, 0x00e9, 68f2644a2cSMark Brown }; 69f2644a2cSMark Brown 70f2644a2cSMark Brown struct wm8960_priv { 71f0fba2adSLiam Girdwood enum snd_soc_control_type control_type; 72f0fba2adSLiam Girdwood int (*set_bias_level)(struct snd_soc_codec *, 73f0fba2adSLiam Girdwood enum snd_soc_bias_level level); 74913d7b4cSMark Brown struct snd_soc_dapm_widget *lout1; 75913d7b4cSMark Brown struct snd_soc_dapm_widget *rout1; 76913d7b4cSMark Brown struct snd_soc_dapm_widget *out3; 77afd6d36aSMark Brown bool deemph; 78afd6d36aSMark Brown int playback_fs; 79f2644a2cSMark Brown }; 80f2644a2cSMark Brown 8117a52fd6SMark Brown #define wm8960_reset(c) snd_soc_write(c, WM8960_RESET, 0) 82f2644a2cSMark Brown 83f2644a2cSMark Brown /* enumerated controls */ 84f2644a2cSMark Brown static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", 85f2644a2cSMark Brown "Right Inverted", "Stereo Inversion"}; 86f2644a2cSMark Brown static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; 87f2644a2cSMark Brown static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"}; 88f2644a2cSMark Brown static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; 89f2644a2cSMark Brown static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; 90f2644a2cSMark Brown 91f2644a2cSMark Brown static const struct soc_enum wm8960_enum[] = { 92f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), 93f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), 94f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), 95f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff), 96f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc), 97f2644a2cSMark Brown SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), 98f2644a2cSMark Brown }; 99f2644a2cSMark Brown 100afd6d36aSMark Brown static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; 101afd6d36aSMark Brown 102afd6d36aSMark Brown static int wm8960_set_deemph(struct snd_soc_codec *codec) 103afd6d36aSMark Brown { 104afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 105afd6d36aSMark Brown int val, i, best; 106afd6d36aSMark Brown 107afd6d36aSMark Brown /* If we're using deemphasis select the nearest available sample 108afd6d36aSMark Brown * rate. 109afd6d36aSMark Brown */ 110afd6d36aSMark Brown if (wm8960->deemph) { 111afd6d36aSMark Brown best = 1; 112afd6d36aSMark Brown for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { 113afd6d36aSMark Brown if (abs(deemph_settings[i] - wm8960->playback_fs) < 114afd6d36aSMark Brown abs(deemph_settings[best] - wm8960->playback_fs)) 115afd6d36aSMark Brown best = i; 116afd6d36aSMark Brown } 117afd6d36aSMark Brown 118afd6d36aSMark Brown val = best << 1; 119afd6d36aSMark Brown } else { 120afd6d36aSMark Brown val = 0; 121afd6d36aSMark Brown } 122afd6d36aSMark Brown 123afd6d36aSMark Brown dev_dbg(codec->dev, "Set deemphasis %d\n", val); 124afd6d36aSMark Brown 125afd6d36aSMark Brown return snd_soc_update_bits(codec, WM8960_DACCTL1, 126afd6d36aSMark Brown 0x6, val); 127afd6d36aSMark Brown } 128afd6d36aSMark Brown 129afd6d36aSMark Brown static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, 130afd6d36aSMark Brown struct snd_ctl_elem_value *ucontrol) 131afd6d36aSMark Brown { 132afd6d36aSMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 133afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 134afd6d36aSMark Brown 1353f343f85SDmitry Artamonow ucontrol->value.enumerated.item[0] = wm8960->deemph; 1363f343f85SDmitry Artamonow return 0; 137afd6d36aSMark Brown } 138afd6d36aSMark Brown 139afd6d36aSMark Brown static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, 140afd6d36aSMark Brown struct snd_ctl_elem_value *ucontrol) 141afd6d36aSMark Brown { 142afd6d36aSMark Brown struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); 143afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 144afd6d36aSMark Brown int deemph = ucontrol->value.enumerated.item[0]; 145afd6d36aSMark Brown 146afd6d36aSMark Brown if (deemph > 1) 147afd6d36aSMark Brown return -EINVAL; 148afd6d36aSMark Brown 149afd6d36aSMark Brown wm8960->deemph = deemph; 150afd6d36aSMark Brown 151afd6d36aSMark Brown return wm8960_set_deemph(codec); 152afd6d36aSMark Brown } 153afd6d36aSMark Brown 154f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0); 155f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); 156f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); 157f2644a2cSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 158f2644a2cSMark Brown 159f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_snd_controls[] = { 160f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, 161f2644a2cSMark Brown 0, 63, 0, adc_tlv), 162f2644a2cSMark Brown SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 163f2644a2cSMark Brown 6, 1, 0), 164f2644a2cSMark Brown SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 165f2644a2cSMark Brown 7, 1, 0), 166f2644a2cSMark Brown 167f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 168f2644a2cSMark Brown 0, 255, 0, dac_tlv), 169f2644a2cSMark Brown 170f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, 171f2644a2cSMark Brown 0, 127, 0, out_tlv), 172f2644a2cSMark Brown SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1, 173f2644a2cSMark Brown 7, 1, 0), 174f2644a2cSMark Brown 175f2644a2cSMark Brown SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, 176f2644a2cSMark Brown 0, 127, 0, out_tlv), 177f2644a2cSMark Brown SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2, 178f2644a2cSMark Brown 7, 1, 0), 179f2644a2cSMark Brown SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), 180f2644a2cSMark Brown SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), 181f2644a2cSMark Brown 182f2644a2cSMark Brown SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), 1834faaa8d9SMark Brown SOC_ENUM("ADC Polarity", wm8960_enum[0]), 184f2644a2cSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), 185f2644a2cSMark Brown 186f2644a2cSMark Brown SOC_ENUM("DAC Polarity", wm8960_enum[2]), 187afd6d36aSMark Brown SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, 188afd6d36aSMark Brown wm8960_get_deemph, wm8960_put_deemph), 189f2644a2cSMark Brown 1904faaa8d9SMark Brown SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), 1914faaa8d9SMark Brown SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), 192f2644a2cSMark Brown SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), 193f2644a2cSMark Brown SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), 194f2644a2cSMark Brown 1954faaa8d9SMark Brown SOC_ENUM("ALC Function", wm8960_enum[4]), 196f2644a2cSMark Brown SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), 197f2644a2cSMark Brown SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), 198f2644a2cSMark Brown SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), 199f2644a2cSMark Brown SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), 2004faaa8d9SMark Brown SOC_ENUM("ALC Mode", wm8960_enum[5]), 201f2644a2cSMark Brown SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), 202f2644a2cSMark Brown SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), 203f2644a2cSMark Brown 204f2644a2cSMark Brown SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), 205f2644a2cSMark Brown SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), 206f2644a2cSMark Brown 207f2644a2cSMark Brown SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH, 208f2644a2cSMark Brown 0, 127, 0), 209f2644a2cSMark Brown 210f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", 211f2644a2cSMark Brown WM8960_BYPASS1, 4, 7, 1, bypass_tlv), 212f2644a2cSMark Brown SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume", 213f2644a2cSMark Brown WM8960_LOUTMIX, 4, 7, 1, bypass_tlv), 214f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume", 215f2644a2cSMark Brown WM8960_BYPASS2, 4, 7, 1, bypass_tlv), 216f2644a2cSMark Brown SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", 217f2644a2cSMark Brown WM8960_ROUTMIX, 4, 7, 1, bypass_tlv), 218f2644a2cSMark Brown }; 219f2644a2cSMark Brown 220f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin_boost[] = { 221f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0), 222f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0), 223f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0), 224f2644a2cSMark Brown }; 225f2644a2cSMark Brown 226f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_lin[] = { 227f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0), 228f2644a2cSMark Brown }; 229f2644a2cSMark Brown 230f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin_boost[] = { 231f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0), 232f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0), 233f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0), 234f2644a2cSMark Brown }; 235f2644a2cSMark Brown 236f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_rin[] = { 237f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0), 238f2644a2cSMark Brown }; 239f2644a2cSMark Brown 240f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_loutput_mixer[] = { 241f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0), 242f2644a2cSMark Brown SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0), 243f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0), 244f2644a2cSMark Brown }; 245f2644a2cSMark Brown 246f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_routput_mixer[] = { 247f2644a2cSMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0), 248f2644a2cSMark Brown SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0), 249f2644a2cSMark Brown SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0), 250f2644a2cSMark Brown }; 251f2644a2cSMark Brown 252f2644a2cSMark Brown static const struct snd_kcontrol_new wm8960_mono_out[] = { 253f2644a2cSMark Brown SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0), 254f2644a2cSMark Brown SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0), 255f2644a2cSMark Brown }; 256f2644a2cSMark Brown 257f2644a2cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = { 258f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT1"), 259f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT1"), 260f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT2"), 261f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT2"), 262f2644a2cSMark Brown SND_SOC_DAPM_INPUT("LINPUT3"), 263f2644a2cSMark Brown SND_SOC_DAPM_INPUT("RINPUT3"), 264f2644a2cSMark Brown 265187774cbSMark Brown SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), 266f2644a2cSMark Brown 267f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0, 268f2644a2cSMark Brown wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)), 269f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0, 270f2644a2cSMark Brown wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)), 271f2644a2cSMark Brown 272f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, 273f2644a2cSMark Brown wm8960_lin, ARRAY_SIZE(wm8960_lin)), 274f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0, 275f2644a2cSMark Brown wm8960_rin, ARRAY_SIZE(wm8960_rin)), 276f2644a2cSMark Brown 277f2644a2cSMark Brown SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0), 278f2644a2cSMark Brown SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0), 279f2644a2cSMark Brown 280f2644a2cSMark Brown SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0), 281f2644a2cSMark Brown SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0), 282f2644a2cSMark Brown 283f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0, 284f2644a2cSMark Brown &wm8960_loutput_mixer[0], 285f2644a2cSMark Brown ARRAY_SIZE(wm8960_loutput_mixer)), 286f2644a2cSMark Brown SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0, 287f2644a2cSMark Brown &wm8960_routput_mixer[0], 288f2644a2cSMark Brown ARRAY_SIZE(wm8960_routput_mixer)), 289f2644a2cSMark Brown 290f2644a2cSMark Brown SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), 291f2644a2cSMark Brown SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), 292f2644a2cSMark Brown 293f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0), 294f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0), 295f2644a2cSMark Brown 296f2644a2cSMark Brown SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0), 297f2644a2cSMark Brown SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0), 298f2644a2cSMark Brown 299f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"), 300f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"), 301f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"), 302f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"), 303f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"), 304f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"), 305f2644a2cSMark Brown SND_SOC_DAPM_OUTPUT("OUT3"), 306f2644a2cSMark Brown }; 307f2644a2cSMark Brown 308913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = { 309913d7b4cSMark Brown SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0, 310913d7b4cSMark Brown &wm8960_mono_out[0], 311913d7b4cSMark Brown ARRAY_SIZE(wm8960_mono_out)), 312913d7b4cSMark Brown }; 313913d7b4cSMark Brown 314913d7b4cSMark Brown /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */ 315913d7b4cSMark Brown static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = { 316913d7b4cSMark Brown SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0), 317913d7b4cSMark Brown }; 318913d7b4cSMark Brown 319f2644a2cSMark Brown static const struct snd_soc_dapm_route audio_paths[] = { 320f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, 321f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, 322f2644a2cSMark Brown { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, 323f2644a2cSMark Brown 324f2644a2cSMark Brown { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", }, 325f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT1", }, /* Really Boost Switch */ 326f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT2" }, 327f2644a2cSMark Brown { "Left Input Mixer", NULL, "LINPUT3" }, 328f2644a2cSMark Brown 329f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" }, 330f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" }, 331f2644a2cSMark Brown { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" }, 332f2644a2cSMark Brown 333f2644a2cSMark Brown { "Right Input Mixer", "Boost Switch", "Right Boost Mixer", }, 334f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT1", }, /* Really Boost Switch */ 335f2644a2cSMark Brown { "Right Input Mixer", NULL, "RINPUT2" }, 336f2644a2cSMark Brown { "Right Input Mixer", NULL, "LINPUT3" }, 337f2644a2cSMark Brown 338f2644a2cSMark Brown { "Left ADC", NULL, "Left Input Mixer" }, 339f2644a2cSMark Brown { "Right ADC", NULL, "Right Input Mixer" }, 340f2644a2cSMark Brown 341f2644a2cSMark Brown { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" }, 342f2644a2cSMark Brown { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} , 343f2644a2cSMark Brown { "Left Output Mixer", "PCM Playback Switch", "Left DAC" }, 344f2644a2cSMark Brown 345f2644a2cSMark Brown { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" }, 346f2644a2cSMark Brown { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } , 347f2644a2cSMark Brown { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, 348f2644a2cSMark Brown 349f2644a2cSMark Brown { "LOUT1 PGA", NULL, "Left Output Mixer" }, 350f2644a2cSMark Brown { "ROUT1 PGA", NULL, "Right Output Mixer" }, 351f2644a2cSMark Brown 352f2644a2cSMark Brown { "HP_L", NULL, "LOUT1 PGA" }, 353f2644a2cSMark Brown { "HP_R", NULL, "ROUT1 PGA" }, 354f2644a2cSMark Brown 355f2644a2cSMark Brown { "Left Speaker PGA", NULL, "Left Output Mixer" }, 356f2644a2cSMark Brown { "Right Speaker PGA", NULL, "Right Output Mixer" }, 357f2644a2cSMark Brown 358f2644a2cSMark Brown { "Left Speaker Output", NULL, "Left Speaker PGA" }, 359f2644a2cSMark Brown { "Right Speaker Output", NULL, "Right Speaker PGA" }, 360f2644a2cSMark Brown 361f2644a2cSMark Brown { "SPK_LN", NULL, "Left Speaker Output" }, 362f2644a2cSMark Brown { "SPK_LP", NULL, "Left Speaker Output" }, 363f2644a2cSMark Brown { "SPK_RN", NULL, "Right Speaker Output" }, 364f2644a2cSMark Brown { "SPK_RP", NULL, "Right Speaker Output" }, 365913d7b4cSMark Brown }; 366913d7b4cSMark Brown 367913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_out3[] = { 368913d7b4cSMark Brown { "Mono Output Mixer", "Left Switch", "Left Output Mixer" }, 369913d7b4cSMark Brown { "Mono Output Mixer", "Right Switch", "Right Output Mixer" }, 370f2644a2cSMark Brown 371f2644a2cSMark Brown { "OUT3", NULL, "Mono Output Mixer", } 372f2644a2cSMark Brown }; 373f2644a2cSMark Brown 374913d7b4cSMark Brown static const struct snd_soc_dapm_route audio_paths_capless[] = { 375913d7b4cSMark Brown { "HP_L", NULL, "OUT3 VMID" }, 376913d7b4cSMark Brown { "HP_R", NULL, "OUT3 VMID" }, 377913d7b4cSMark Brown 378913d7b4cSMark Brown { "OUT3 VMID", NULL, "Left Output Mixer" }, 379913d7b4cSMark Brown { "OUT3 VMID", NULL, "Right Output Mixer" }, 380913d7b4cSMark Brown }; 381913d7b4cSMark Brown 382f2644a2cSMark Brown static int wm8960_add_widgets(struct snd_soc_codec *codec) 383f2644a2cSMark Brown { 384913d7b4cSMark Brown struct wm8960_data *pdata = codec->dev->platform_data; 385b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 386ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 387913d7b4cSMark Brown struct snd_soc_dapm_widget *w; 388913d7b4cSMark Brown 389ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, 390f2644a2cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets)); 391f2644a2cSMark Brown 392ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); 393f2644a2cSMark Brown 394913d7b4cSMark Brown /* In capless mode OUT3 is used to provide VMID for the 395913d7b4cSMark Brown * headphone outputs, otherwise it is used as a mono mixer. 396913d7b4cSMark Brown */ 397913d7b4cSMark Brown if (pdata && pdata->capless) { 398ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, 399913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_capless)); 400913d7b4cSMark Brown 401ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_capless, 402913d7b4cSMark Brown ARRAY_SIZE(audio_paths_capless)); 403913d7b4cSMark Brown } else { 404ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, 405913d7b4cSMark Brown ARRAY_SIZE(wm8960_dapm_widgets_out3)); 406913d7b4cSMark Brown 407ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths_out3, 408913d7b4cSMark Brown ARRAY_SIZE(audio_paths_out3)); 409913d7b4cSMark Brown } 410913d7b4cSMark Brown 411913d7b4cSMark Brown /* We need to power up the headphone output stage out of 412913d7b4cSMark Brown * sequence for capless mode. To save scanning the widget 413913d7b4cSMark Brown * list each time to find the desired power state do so now 414913d7b4cSMark Brown * and save the result. 415913d7b4cSMark Brown */ 41697c866deSJarkko Nikula list_for_each_entry(w, &codec->card->widgets, list) { 41797c866deSJarkko Nikula if (w->dapm != &codec->dapm) 41897c866deSJarkko Nikula continue; 419913d7b4cSMark Brown if (strcmp(w->name, "LOUT1 PGA") == 0) 420913d7b4cSMark Brown wm8960->lout1 = w; 421913d7b4cSMark Brown if (strcmp(w->name, "ROUT1 PGA") == 0) 422913d7b4cSMark Brown wm8960->rout1 = w; 423913d7b4cSMark Brown if (strcmp(w->name, "OUT3 VMID") == 0) 424913d7b4cSMark Brown wm8960->out3 = w; 425913d7b4cSMark Brown } 426913d7b4cSMark Brown 427f2644a2cSMark Brown return 0; 428f2644a2cSMark Brown } 429f2644a2cSMark Brown 430f2644a2cSMark Brown static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, 431f2644a2cSMark Brown unsigned int fmt) 432f2644a2cSMark Brown { 433f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 434f2644a2cSMark Brown u16 iface = 0; 435f2644a2cSMark Brown 436f2644a2cSMark Brown /* set master/slave audio interface */ 437f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 438f2644a2cSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 439f2644a2cSMark Brown iface |= 0x0040; 440f2644a2cSMark Brown break; 441f2644a2cSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 442f2644a2cSMark Brown break; 443f2644a2cSMark Brown default: 444f2644a2cSMark Brown return -EINVAL; 445f2644a2cSMark Brown } 446f2644a2cSMark Brown 447f2644a2cSMark Brown /* interface format */ 448f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 449f2644a2cSMark Brown case SND_SOC_DAIFMT_I2S: 450f2644a2cSMark Brown iface |= 0x0002; 451f2644a2cSMark Brown break; 452f2644a2cSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 453f2644a2cSMark Brown break; 454f2644a2cSMark Brown case SND_SOC_DAIFMT_LEFT_J: 455f2644a2cSMark Brown iface |= 0x0001; 456f2644a2cSMark Brown break; 457f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_A: 458f2644a2cSMark Brown iface |= 0x0003; 459f2644a2cSMark Brown break; 460f2644a2cSMark Brown case SND_SOC_DAIFMT_DSP_B: 461f2644a2cSMark Brown iface |= 0x0013; 462f2644a2cSMark Brown break; 463f2644a2cSMark Brown default: 464f2644a2cSMark Brown return -EINVAL; 465f2644a2cSMark Brown } 466f2644a2cSMark Brown 467f2644a2cSMark Brown /* clock inversion */ 468f2644a2cSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 469f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_NF: 470f2644a2cSMark Brown break; 471f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_IF: 472f2644a2cSMark Brown iface |= 0x0090; 473f2644a2cSMark Brown break; 474f2644a2cSMark Brown case SND_SOC_DAIFMT_IB_NF: 475f2644a2cSMark Brown iface |= 0x0080; 476f2644a2cSMark Brown break; 477f2644a2cSMark Brown case SND_SOC_DAIFMT_NB_IF: 478f2644a2cSMark Brown iface |= 0x0010; 479f2644a2cSMark Brown break; 480f2644a2cSMark Brown default: 481f2644a2cSMark Brown return -EINVAL; 482f2644a2cSMark Brown } 483f2644a2cSMark Brown 484f2644a2cSMark Brown /* set iface */ 48517a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 486f2644a2cSMark Brown return 0; 487f2644a2cSMark Brown } 488f2644a2cSMark Brown 489db059c0fSMark Brown static struct { 490db059c0fSMark Brown int rate; 491db059c0fSMark Brown unsigned int val; 492db059c0fSMark Brown } alc_rates[] = { 493db059c0fSMark Brown { 48000, 0 }, 494db059c0fSMark Brown { 44100, 0 }, 495db059c0fSMark Brown { 32000, 1 }, 496db059c0fSMark Brown { 22050, 2 }, 497db059c0fSMark Brown { 24000, 2 }, 498db059c0fSMark Brown { 16000, 3 }, 499db059c0fSMark Brown { 11250, 4 }, 500db059c0fSMark Brown { 12000, 4 }, 501db059c0fSMark Brown { 8000, 5 }, 502db059c0fSMark Brown }; 503db059c0fSMark Brown 504f2644a2cSMark Brown static int wm8960_hw_params(struct snd_pcm_substream *substream, 505f2644a2cSMark Brown struct snd_pcm_hw_params *params, 506f2644a2cSMark Brown struct snd_soc_dai *dai) 507f2644a2cSMark Brown { 508f2644a2cSMark Brown struct snd_soc_pcm_runtime *rtd = substream->private_data; 509f0fba2adSLiam Girdwood struct snd_soc_codec *codec = rtd->codec; 510afd6d36aSMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 51117a52fd6SMark Brown u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; 512db059c0fSMark Brown int i; 513f2644a2cSMark Brown 514f2644a2cSMark Brown /* bit size */ 515f2644a2cSMark Brown switch (params_format(params)) { 516f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S16_LE: 517f2644a2cSMark Brown break; 518f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S20_3LE: 519f2644a2cSMark Brown iface |= 0x0004; 520f2644a2cSMark Brown break; 521f2644a2cSMark Brown case SNDRV_PCM_FORMAT_S24_LE: 522f2644a2cSMark Brown iface |= 0x0008; 523f2644a2cSMark Brown break; 524f2644a2cSMark Brown } 525f2644a2cSMark Brown 526afd6d36aSMark Brown /* Update filters for the new rate */ 527afd6d36aSMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 528afd6d36aSMark Brown wm8960->playback_fs = params_rate(params); 529afd6d36aSMark Brown wm8960_set_deemph(codec); 530db059c0fSMark Brown } else { 531db059c0fSMark Brown for (i = 0; i < ARRAY_SIZE(alc_rates); i++) 532db059c0fSMark Brown if (alc_rates[i].rate == params_rate(params)) 533db059c0fSMark Brown snd_soc_update_bits(codec, 534db059c0fSMark Brown WM8960_ADDCTL3, 0x7, 535db059c0fSMark Brown alc_rates[i].val); 536afd6d36aSMark Brown } 537afd6d36aSMark Brown 538f2644a2cSMark Brown /* set iface */ 53917a52fd6SMark Brown snd_soc_write(codec, WM8960_IFACE1, iface); 540f2644a2cSMark Brown return 0; 541f2644a2cSMark Brown } 542f2644a2cSMark Brown 543f2644a2cSMark Brown static int wm8960_mute(struct snd_soc_dai *dai, int mute) 544f2644a2cSMark Brown { 545f2644a2cSMark Brown struct snd_soc_codec *codec = dai->codec; 546f2644a2cSMark Brown 547f2644a2cSMark Brown if (mute) 54816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); 549f2644a2cSMark Brown else 55016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); 551f2644a2cSMark Brown return 0; 552f2644a2cSMark Brown } 553f2644a2cSMark Brown 554913d7b4cSMark Brown static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, 555f2644a2cSMark Brown enum snd_soc_bias_level level) 556f2644a2cSMark Brown { 557f2644a2cSMark Brown switch (level) { 558f2644a2cSMark Brown case SND_SOC_BIAS_ON: 559f2644a2cSMark Brown break; 560f2644a2cSMark Brown 561f2644a2cSMark Brown case SND_SOC_BIAS_PREPARE: 562f2644a2cSMark Brown /* Set VMID to 2x50k */ 56316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); 564f2644a2cSMark Brown break; 565f2644a2cSMark Brown 566f2644a2cSMark Brown case SND_SOC_BIAS_STANDBY: 567ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 568bc45df2dSAxel Lin snd_soc_cache_sync(codec); 569bc45df2dSAxel Lin 570f2644a2cSMark Brown /* Enable anti-pop features */ 57117a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 572f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 573f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 574f2644a2cSMark Brown 575f2644a2cSMark Brown /* Enable & ramp VMID at 2x50k */ 57616b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); 577f2644a2cSMark Brown msleep(100); 578f2644a2cSMark Brown 579f2644a2cSMark Brown /* Enable VREF */ 58016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, 58116b24881SAxel Lin WM8960_VREF); 582f2644a2cSMark Brown 583f2644a2cSMark Brown /* Disable anti-pop features */ 58417a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); 585f2644a2cSMark Brown } 586f2644a2cSMark Brown 587f2644a2cSMark Brown /* Set VMID to 2x250k */ 58816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); 589f2644a2cSMark Brown break; 590f2644a2cSMark Brown 591f2644a2cSMark Brown case SND_SOC_BIAS_OFF: 592f2644a2cSMark Brown /* Enable anti-pop features */ 59317a52fd6SMark Brown snd_soc_write(codec, WM8960_APOP1, 594f2644a2cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 595f2644a2cSMark Brown WM8960_BUFDCOPEN | WM8960_BUFIOEN); 596f2644a2cSMark Brown 597f2644a2cSMark Brown /* Disable VMID and VREF, let them discharge */ 59817a52fd6SMark Brown snd_soc_write(codec, WM8960_POWER1, 0); 599f2644a2cSMark Brown msleep(600); 600913d7b4cSMark Brown break; 601913d7b4cSMark Brown } 602f2644a2cSMark Brown 603ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 604913d7b4cSMark Brown 605913d7b4cSMark Brown return 0; 606913d7b4cSMark Brown } 607913d7b4cSMark Brown 608913d7b4cSMark Brown static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, 609913d7b4cSMark Brown enum snd_soc_bias_level level) 610913d7b4cSMark Brown { 611b2c812e2SMark Brown struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 612913d7b4cSMark Brown int reg; 613913d7b4cSMark Brown 614913d7b4cSMark Brown switch (level) { 615913d7b4cSMark Brown case SND_SOC_BIAS_ON: 616913d7b4cSMark Brown break; 617913d7b4cSMark Brown 618913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 619ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 620913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 621913d7b4cSMark Brown /* Enable anti pop mode */ 622913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 623913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 624913d7b4cSMark Brown WM8960_BUFDCOPEN, 625913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 626913d7b4cSMark Brown WM8960_BUFDCOPEN); 627913d7b4cSMark Brown 628913d7b4cSMark Brown /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ 629913d7b4cSMark Brown reg = 0; 630913d7b4cSMark Brown if (wm8960->lout1 && wm8960->lout1->power) 631913d7b4cSMark Brown reg |= WM8960_PWR2_LOUT1; 632913d7b4cSMark Brown if (wm8960->rout1 && wm8960->rout1->power) 633913d7b4cSMark Brown reg |= WM8960_PWR2_ROUT1; 634913d7b4cSMark Brown if (wm8960->out3 && wm8960->out3->power) 635913d7b4cSMark Brown reg |= WM8960_PWR2_OUT3; 636913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER2, 637913d7b4cSMark Brown WM8960_PWR2_LOUT1 | 638913d7b4cSMark Brown WM8960_PWR2_ROUT1 | 639913d7b4cSMark Brown WM8960_PWR2_OUT3, reg); 640913d7b4cSMark Brown 641913d7b4cSMark Brown /* Enable VMID at 2*50k */ 642913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 643913d7b4cSMark Brown WM8960_VMID_MASK, 0x80); 644913d7b4cSMark Brown 645913d7b4cSMark Brown /* Ramp */ 646913d7b4cSMark Brown msleep(100); 647913d7b4cSMark Brown 648913d7b4cSMark Brown /* Enable VREF */ 649913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 650913d7b4cSMark Brown WM8960_VREF, WM8960_VREF); 651913d7b4cSMark Brown 652913d7b4cSMark Brown msleep(100); 653913d7b4cSMark Brown break; 654913d7b4cSMark Brown 655913d7b4cSMark Brown case SND_SOC_BIAS_ON: 656913d7b4cSMark Brown /* Enable anti-pop mode */ 657913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 658913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 659913d7b4cSMark Brown WM8960_BUFDCOPEN, 660913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 661913d7b4cSMark Brown WM8960_BUFDCOPEN); 662913d7b4cSMark Brown 663913d7b4cSMark Brown /* Disable VMID and VREF */ 664913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_POWER1, 665913d7b4cSMark Brown WM8960_VREF | WM8960_VMID_MASK, 0); 666913d7b4cSMark Brown break; 667913d7b4cSMark Brown 668bc45df2dSAxel Lin case SND_SOC_BIAS_OFF: 669bc45df2dSAxel Lin snd_soc_cache_sync(codec); 670bc45df2dSAxel Lin break; 671913d7b4cSMark Brown default: 672913d7b4cSMark Brown break; 673913d7b4cSMark Brown } 674913d7b4cSMark Brown break; 675913d7b4cSMark Brown 676913d7b4cSMark Brown case SND_SOC_BIAS_STANDBY: 677ce6120ccSLiam Girdwood switch (codec->dapm.bias_level) { 678913d7b4cSMark Brown case SND_SOC_BIAS_PREPARE: 679913d7b4cSMark Brown /* Disable HP discharge */ 680913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP2, 681913d7b4cSMark Brown WM8960_DISOP | WM8960_DRES_MASK, 682913d7b4cSMark Brown 0); 683913d7b4cSMark Brown 684913d7b4cSMark Brown /* Disable anti-pop features */ 685913d7b4cSMark Brown snd_soc_update_bits(codec, WM8960_APOP1, 686913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 687913d7b4cSMark Brown WM8960_BUFDCOPEN, 688913d7b4cSMark Brown WM8960_POBCTRL | WM8960_SOFT_ST | 689913d7b4cSMark Brown WM8960_BUFDCOPEN); 690913d7b4cSMark Brown break; 691913d7b4cSMark Brown 692913d7b4cSMark Brown default: 693913d7b4cSMark Brown break; 694913d7b4cSMark Brown } 695913d7b4cSMark Brown break; 696913d7b4cSMark Brown 697913d7b4cSMark Brown case SND_SOC_BIAS_OFF: 698f2644a2cSMark Brown break; 699f2644a2cSMark Brown } 700f2644a2cSMark Brown 701ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 702f2644a2cSMark Brown 703f2644a2cSMark Brown return 0; 704f2644a2cSMark Brown } 705f2644a2cSMark Brown 706f2644a2cSMark Brown /* PLL divisors */ 707f2644a2cSMark Brown struct _pll_div { 708f2644a2cSMark Brown u32 pre_div:1; 709f2644a2cSMark Brown u32 n:4; 710f2644a2cSMark Brown u32 k:24; 711f2644a2cSMark Brown }; 712f2644a2cSMark Brown 713f2644a2cSMark Brown /* The size in bits of the pll divide multiplied by 10 714f2644a2cSMark Brown * to allow rounding later */ 715f2644a2cSMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10) 716f2644a2cSMark Brown 717f2644a2cSMark Brown static int pll_factors(unsigned int source, unsigned int target, 718f2644a2cSMark Brown struct _pll_div *pll_div) 719f2644a2cSMark Brown { 720f2644a2cSMark Brown unsigned long long Kpart; 721f2644a2cSMark Brown unsigned int K, Ndiv, Nmod; 722f2644a2cSMark Brown 723f2644a2cSMark Brown pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target); 724f2644a2cSMark Brown 725f2644a2cSMark Brown /* Scale up target to PLL operating frequency */ 726f2644a2cSMark Brown target *= 4; 727f2644a2cSMark Brown 728f2644a2cSMark Brown Ndiv = target / source; 729f2644a2cSMark Brown if (Ndiv < 6) { 730f2644a2cSMark Brown source >>= 1; 731f2644a2cSMark Brown pll_div->pre_div = 1; 732f2644a2cSMark Brown Ndiv = target / source; 733f2644a2cSMark Brown } else 734f2644a2cSMark Brown pll_div->pre_div = 0; 735f2644a2cSMark Brown 736f2644a2cSMark Brown if ((Ndiv < 6) || (Ndiv > 12)) { 737f2644a2cSMark Brown pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); 738f2644a2cSMark Brown return -EINVAL; 739f2644a2cSMark Brown } 740f2644a2cSMark Brown 741f2644a2cSMark Brown pll_div->n = Ndiv; 742f2644a2cSMark Brown Nmod = target % source; 743f2644a2cSMark Brown Kpart = FIXED_PLL_SIZE * (long long)Nmod; 744f2644a2cSMark Brown 745f2644a2cSMark Brown do_div(Kpart, source); 746f2644a2cSMark Brown 747f2644a2cSMark Brown K = Kpart & 0xFFFFFFFF; 748f2644a2cSMark Brown 749f2644a2cSMark Brown /* Check if we need to round */ 750f2644a2cSMark Brown if ((K % 10) >= 5) 751f2644a2cSMark Brown K += 5; 752f2644a2cSMark Brown 753f2644a2cSMark Brown /* Move down to proper range now rounding is done */ 754f2644a2cSMark Brown K /= 10; 755f2644a2cSMark Brown 756f2644a2cSMark Brown pll_div->k = K; 757f2644a2cSMark Brown 758f2644a2cSMark Brown pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n", 759f2644a2cSMark Brown pll_div->n, pll_div->k, pll_div->pre_div); 760f2644a2cSMark Brown 761f2644a2cSMark Brown return 0; 762f2644a2cSMark Brown } 763f2644a2cSMark Brown 76485488037SMark Brown static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 76585488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out) 766f2644a2cSMark Brown { 767f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 768f2644a2cSMark Brown u16 reg; 769f2644a2cSMark Brown static struct _pll_div pll_div; 770f2644a2cSMark Brown int ret; 771f2644a2cSMark Brown 772f2644a2cSMark Brown if (freq_in && freq_out) { 773f2644a2cSMark Brown ret = pll_factors(freq_in, freq_out, &pll_div); 774f2644a2cSMark Brown if (ret != 0) 775f2644a2cSMark Brown return ret; 776f2644a2cSMark Brown } 777f2644a2cSMark Brown 778f2644a2cSMark Brown /* Disable the PLL: even if we are changing the frequency the 779f2644a2cSMark Brown * PLL needs to be disabled while we do so. */ 78016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); 78116b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); 782f2644a2cSMark Brown 783f2644a2cSMark Brown if (!freq_in || !freq_out) 784f2644a2cSMark Brown return 0; 785f2644a2cSMark Brown 78617a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; 787f2644a2cSMark Brown reg |= pll_div.pre_div << 4; 788f2644a2cSMark Brown reg |= pll_div.n; 789f2644a2cSMark Brown 790f2644a2cSMark Brown if (pll_div.k) { 791f2644a2cSMark Brown reg |= 0x20; 792f2644a2cSMark Brown 79317a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f); 79417a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff); 79517a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff); 796f2644a2cSMark Brown } 79717a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg); 798f2644a2cSMark Brown 799f2644a2cSMark Brown /* Turn it on */ 80016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); 801f2644a2cSMark Brown msleep(250); 80216b24881SAxel Lin snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); 803f2644a2cSMark Brown 804f2644a2cSMark Brown return 0; 805f2644a2cSMark Brown } 806f2644a2cSMark Brown 807f2644a2cSMark Brown static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 808f2644a2cSMark Brown int div_id, int div) 809f2644a2cSMark Brown { 810f2644a2cSMark Brown struct snd_soc_codec *codec = codec_dai->codec; 811f2644a2cSMark Brown u16 reg; 812f2644a2cSMark Brown 813f2644a2cSMark Brown switch (div_id) { 814f2644a2cSMark Brown case WM8960_SYSCLKDIV: 81517a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; 81617a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 817f2644a2cSMark Brown break; 818f2644a2cSMark Brown case WM8960_DACDIV: 81917a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; 82017a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK1, reg | div); 821f2644a2cSMark Brown break; 822f2644a2cSMark Brown case WM8960_OPCLKDIV: 82317a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; 82417a52fd6SMark Brown snd_soc_write(codec, WM8960_PLL1, reg | div); 825f2644a2cSMark Brown break; 826f2644a2cSMark Brown case WM8960_DCLKDIV: 82717a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; 82817a52fd6SMark Brown snd_soc_write(codec, WM8960_CLOCK2, reg | div); 829f2644a2cSMark Brown break; 830f2644a2cSMark Brown case WM8960_TOCLKSEL: 83117a52fd6SMark Brown reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; 83217a52fd6SMark Brown snd_soc_write(codec, WM8960_ADDCTL1, reg | div); 833f2644a2cSMark Brown break; 834f2644a2cSMark Brown default: 835f2644a2cSMark Brown return -EINVAL; 836f2644a2cSMark Brown } 837f2644a2cSMark Brown 838f2644a2cSMark Brown return 0; 839f2644a2cSMark Brown } 840f2644a2cSMark Brown 841f0fba2adSLiam Girdwood static int wm8960_set_bias_level(struct snd_soc_codec *codec, 842f0fba2adSLiam Girdwood enum snd_soc_bias_level level) 843f0fba2adSLiam Girdwood { 844f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 845f0fba2adSLiam Girdwood 846f0fba2adSLiam Girdwood return wm8960->set_bias_level(codec, level); 847f0fba2adSLiam Girdwood } 848f0fba2adSLiam Girdwood 849f2644a2cSMark Brown #define WM8960_RATES SNDRV_PCM_RATE_8000_48000 850f2644a2cSMark Brown 851f2644a2cSMark Brown #define WM8960_FORMATS \ 852f2644a2cSMark Brown (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 853f2644a2cSMark Brown SNDRV_PCM_FMTBIT_S24_LE) 854f2644a2cSMark Brown 85585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8960_dai_ops = { 856f2644a2cSMark Brown .hw_params = wm8960_hw_params, 857f2644a2cSMark Brown .digital_mute = wm8960_mute, 858f2644a2cSMark Brown .set_fmt = wm8960_set_dai_fmt, 859f2644a2cSMark Brown .set_clkdiv = wm8960_set_dai_clkdiv, 860f2644a2cSMark Brown .set_pll = wm8960_set_dai_pll, 861f2644a2cSMark Brown }; 862f2644a2cSMark Brown 863f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8960_dai = { 864f0fba2adSLiam Girdwood .name = "wm8960-hifi", 865f2644a2cSMark Brown .playback = { 866f2644a2cSMark Brown .stream_name = "Playback", 867f2644a2cSMark Brown .channels_min = 1, 868f2644a2cSMark Brown .channels_max = 2, 869f2644a2cSMark Brown .rates = WM8960_RATES, 870f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 871f2644a2cSMark Brown .capture = { 872f2644a2cSMark Brown .stream_name = "Capture", 873f2644a2cSMark Brown .channels_min = 1, 874f2644a2cSMark Brown .channels_max = 2, 875f2644a2cSMark Brown .rates = WM8960_RATES, 876f2644a2cSMark Brown .formats = WM8960_FORMATS,}, 877f2644a2cSMark Brown .ops = &wm8960_dai_ops, 878f2644a2cSMark Brown .symmetric_rates = 1, 879f2644a2cSMark Brown }; 880f2644a2cSMark Brown 88184b315eeSLars-Peter Clausen static int wm8960_suspend(struct snd_soc_codec *codec) 882f2644a2cSMark Brown { 883f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 884f2644a2cSMark Brown 885f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); 886f2644a2cSMark Brown return 0; 887f2644a2cSMark Brown } 888f2644a2cSMark Brown 889f0fba2adSLiam Girdwood static int wm8960_resume(struct snd_soc_codec *codec) 890f2644a2cSMark Brown { 891f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 892f2644a2cSMark Brown 893f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); 894f2644a2cSMark Brown return 0; 895f2644a2cSMark Brown } 896f2644a2cSMark Brown 897f0fba2adSLiam Girdwood static int wm8960_probe(struct snd_soc_codec *codec) 898f2644a2cSMark Brown { 899f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 900f0fba2adSLiam Girdwood struct wm8960_data *pdata = dev_get_platdata(codec->dev); 901f2644a2cSMark Brown int ret; 902f2644a2cSMark Brown 903f0fba2adSLiam Girdwood wm8960->set_bias_level = wm8960_set_bias_level_out3; 904913d7b4cSMark Brown 905f2644a2cSMark Brown if (!pdata) { 906f2644a2cSMark Brown dev_warn(codec->dev, "No platform data supplied\n"); 907f2644a2cSMark Brown } else { 908f2644a2cSMark Brown if (pdata->dres > WM8960_DRES_MAX) { 909f2644a2cSMark Brown dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres); 910f2644a2cSMark Brown pdata->dres = 0; 911f2644a2cSMark Brown } 912913d7b4cSMark Brown 913913d7b4cSMark Brown if (pdata->capless) 914f0fba2adSLiam Girdwood wm8960->set_bias_level = wm8960_set_bias_level_capless; 915f2644a2cSMark Brown } 916f2644a2cSMark Brown 917f0fba2adSLiam Girdwood ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8960->control_type); 91817a52fd6SMark Brown if (ret < 0) { 91917a52fd6SMark Brown dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); 920f0fba2adSLiam Girdwood return ret; 92117a52fd6SMark Brown } 92217a52fd6SMark Brown 923f2644a2cSMark Brown ret = wm8960_reset(codec); 924f2644a2cSMark Brown if (ret < 0) { 925f2644a2cSMark Brown dev_err(codec->dev, "Failed to issue reset\n"); 926f0fba2adSLiam Girdwood return ret; 927f2644a2cSMark Brown } 928f2644a2cSMark Brown 929f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY); 930f2644a2cSMark Brown 931f2644a2cSMark Brown /* Latch the update bits */ 93216b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100); 93316b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100); 93416b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100); 93516b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100); 93616b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100); 93716b24881SAxel Lin snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100); 93816b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100); 93916b24881SAxel Lin snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100); 94016b24881SAxel Lin snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100); 94116b24881SAxel Lin snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100); 942f2644a2cSMark Brown 943f0fba2adSLiam Girdwood snd_soc_add_controls(codec, wm8960_snd_controls, 944f0fba2adSLiam Girdwood ARRAY_SIZE(wm8960_snd_controls)); 945f0fba2adSLiam Girdwood wm8960_add_widgets(codec); 946f2644a2cSMark Brown 947f2644a2cSMark Brown return 0; 948f2644a2cSMark Brown } 949f2644a2cSMark Brown 950f0fba2adSLiam Girdwood /* power down chip */ 951f0fba2adSLiam Girdwood static int wm8960_remove(struct snd_soc_codec *codec) 952f2644a2cSMark Brown { 953f0fba2adSLiam Girdwood struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 954f0fba2adSLiam Girdwood 955f0fba2adSLiam Girdwood wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF); 956f0fba2adSLiam Girdwood return 0; 957f2644a2cSMark Brown } 958f2644a2cSMark Brown 959f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8960 = { 960f0fba2adSLiam Girdwood .probe = wm8960_probe, 961f0fba2adSLiam Girdwood .remove = wm8960_remove, 962f0fba2adSLiam Girdwood .suspend = wm8960_suspend, 963f0fba2adSLiam Girdwood .resume = wm8960_resume, 964f0fba2adSLiam Girdwood .set_bias_level = wm8960_set_bias_level, 965f0fba2adSLiam Girdwood .reg_cache_size = ARRAY_SIZE(wm8960_reg), 966f0fba2adSLiam Girdwood .reg_word_size = sizeof(u16), 967f0fba2adSLiam Girdwood .reg_cache_default = wm8960_reg, 968f0fba2adSLiam Girdwood }; 969f0fba2adSLiam Girdwood 970f2644a2cSMark Brown static __devinit int wm8960_i2c_probe(struct i2c_client *i2c, 971f2644a2cSMark Brown const struct i2c_device_id *id) 972f2644a2cSMark Brown { 973f2644a2cSMark Brown struct wm8960_priv *wm8960; 974f0fba2adSLiam Girdwood int ret; 975f2644a2cSMark Brown 976b9791c01SMark Brown wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), 977b9791c01SMark Brown GFP_KERNEL); 978f2644a2cSMark Brown if (wm8960 == NULL) 979f2644a2cSMark Brown return -ENOMEM; 980f2644a2cSMark Brown 981f2644a2cSMark Brown i2c_set_clientdata(i2c, wm8960); 9827f984b55SLars-Peter Clausen wm8960->control_type = SND_SOC_I2C; 983f2644a2cSMark Brown 984f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 985f0fba2adSLiam Girdwood &soc_codec_dev_wm8960, &wm8960_dai, 1); 986b9791c01SMark Brown 987f0fba2adSLiam Girdwood return ret; 988f2644a2cSMark Brown } 989f2644a2cSMark Brown 990f2644a2cSMark Brown static __devexit int wm8960_i2c_remove(struct i2c_client *client) 991f2644a2cSMark Brown { 992f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 993f2644a2cSMark Brown return 0; 994f2644a2cSMark Brown } 995f2644a2cSMark Brown 996f2644a2cSMark Brown static const struct i2c_device_id wm8960_i2c_id[] = { 997f2644a2cSMark Brown { "wm8960", 0 }, 998f2644a2cSMark Brown { } 999f2644a2cSMark Brown }; 1000f2644a2cSMark Brown MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); 1001f2644a2cSMark Brown 1002f2644a2cSMark Brown static struct i2c_driver wm8960_i2c_driver = { 1003f2644a2cSMark Brown .driver = { 1004091edccfSMark Brown .name = "wm8960", 1005f2644a2cSMark Brown .owner = THIS_MODULE, 1006f2644a2cSMark Brown }, 1007f2644a2cSMark Brown .probe = wm8960_i2c_probe, 1008f2644a2cSMark Brown .remove = __devexit_p(wm8960_i2c_remove), 1009f2644a2cSMark Brown .id_table = wm8960_i2c_id, 1010f2644a2cSMark Brown }; 1011f2644a2cSMark Brown 1012f2644a2cSMark Brown static int __init wm8960_modinit(void) 1013f2644a2cSMark Brown { 1014f0fba2adSLiam Girdwood int ret = 0; 1015f2644a2cSMark Brown ret = i2c_add_driver(&wm8960_i2c_driver); 1016f2644a2cSMark Brown if (ret != 0) { 1017f2644a2cSMark Brown printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n", 1018f2644a2cSMark Brown ret); 1019f2644a2cSMark Brown } 1020f2644a2cSMark Brown return ret; 1021f2644a2cSMark Brown } 1022f2644a2cSMark Brown module_init(wm8960_modinit); 1023f2644a2cSMark Brown 1024f2644a2cSMark Brown static void __exit wm8960_exit(void) 1025f2644a2cSMark Brown { 1026f2644a2cSMark Brown i2c_del_driver(&wm8960_i2c_driver); 1027f2644a2cSMark Brown } 1028f2644a2cSMark Brown module_exit(wm8960_exit); 1029f2644a2cSMark Brown 1030f2644a2cSMark Brown MODULE_DESCRIPTION("ASoC WM8960 driver"); 1031f2644a2cSMark Brown MODULE_AUTHOR("Liam Girdwood"); 1032f2644a2cSMark Brown MODULE_LICENSE("GPL"); 1033