Loading sound/soc/codecs/wm9705.c +7 −7 Original line number Diff line number Diff line Loading @@ -300,6 +300,8 @@ static int wm9705_reset(struct snd_soc_codec *codec) return 0; /* Success */ } dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; } Loading @@ -317,10 +319,8 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9705_reset(codec); if (ret < 0) { printk(KERN_ERR "could not reset AC97 codec\n"); if (ret < 0) return ret; } for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) { soc_ac97_ops->write(codec->ac97, i, cache[i>>1]); Loading @@ -339,7 +339,7 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) { printk(KERN_ERR "wm9705: failed to register AC97 codec\n"); dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } Loading @@ -347,9 +347,6 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) if (ret) goto reset_err; snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls, ARRAY_SIZE(wm9705_snd_ac97_controls)); return 0; reset_err: Loading @@ -374,6 +371,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9705_reg, .controls = wm9705_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls), .dapm_widgets = wm9705_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets), .dapm_routes = wm9705_audio_map, Loading sound/soc/codecs/wm9712.c +110 −70 Original line number Diff line number Diff line Loading @@ -23,6 +23,11 @@ #include <sound/tlv.h> #include "wm9712.h" struct wm9712_priv { unsigned int hp_mixer[2]; struct mutex lock; }; static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg); static int ac97_write(struct snd_soc_codec *codec, Loading @@ -48,12 +53,10 @@ static const u16 wm9712_reg[] = { 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */ 0x0000, 0x0000 /* virtual hp mixers */ }; /* virtual HP mixers regs */ #define HPL_MIXER 0x80 #define HPR_MIXER 0x82 #define HPL_MIXER 0x0 #define HPR_MIXER 0x1 static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; Loading Loading @@ -157,75 +160,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv), }; static const unsigned int wm9712_mixer_mute_regs[] = { AC97_VIDEO, AC97_PCM, AC97_LINE, AC97_PHONE, AC97_CD, AC97_PC_BEEP, }; /* We have to create a fake left and right HP mixers because * the codec only has a single control that is shared by both channels. * This makes it impossible to determine the audio path. */ static int mixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u16 l, r, beep, line, phone, mic, pcm, aux; l = ac97_read(w->codec, HPL_MIXER); r = ac97_read(w->codec, HPR_MIXER); beep = ac97_read(w->codec, AC97_PC_BEEP); mic = ac97_read(w->codec, AC97_VIDEO); phone = ac97_read(w->codec, AC97_PHONE); line = ac97_read(w->codec, AC97_LINE); pcm = ac97_read(w->codec, AC97_PCM); aux = ac97_read(w->codec, AC97_CD); if (l & 0x1 || r & 0x1) ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff); struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); unsigned int val = ucontrol->value.enumerated.item[0]; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; struct snd_soc_dapm_update update; bool change; mixer = mc->shift >> 8; shift = mc->shift & 0xff; mask = 1 << shift; mutex_lock(&wm9712->lock); old = wm9712->hp_mixer[mixer]; if (ucontrol->value.enumerated.item[0]) wm9712->hp_mixer[mixer] |= mask; else ac97_write(w->codec, AC97_VIDEO, mic | 0x8000); if (l & 0x2 || r & 0x2) ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); wm9712->hp_mixer[mixer] &= ~mask; change = old != wm9712->hp_mixer[mixer]; if (change) { update.kcontrol = kcontrol; update.reg = wm9712_mixer_mute_regs[shift]; update.mask = 0x8000; if ((wm9712->hp_mixer[0] & mask) || (wm9712->hp_mixer[1] & mask)) update.val = 0x0; else ac97_write(w->codec, AC97_PCM, pcm | 0x8000); update.val = 0x8000; if (l & 0x4 || r & 0x4) ac97_write(w->codec, AC97_LINE, line & 0x7fff); else ac97_write(w->codec, AC97_LINE, line | 0x8000); snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, &update); } if (l & 0x8 || r & 0x8) ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); else ac97_write(w->codec, AC97_PHONE, phone | 0x8000); mutex_unlock(&wm9712->lock); if (l & 0x10 || r & 0x10) ac97_write(w->codec, AC97_CD, aux & 0x7fff); else ac97_write(w->codec, AC97_CD, aux | 0x8000); return change; } if (l & 0x20 || r & 0x20) ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); else ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int shift, mixer; mixer = mc->shift >> 8; shift = mc->shift & 0xff; ucontrol->value.enumerated.item[0] = (wm9712->hp_mixer[mixer] >> shift) & 1; return 0; } #define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \ .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \ (xmixer << 8) | xshift, 1, 0, 0) \ } /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0), WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5), WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4), WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3), WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2), WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1), WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0), }; /* Right Headphone Mixers */ static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0), WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5), WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4), WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3), WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2), WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1), WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0), }; /* Speaker Mixer */ Loading Loading @@ -299,12 +335,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, &wm9712_diff_sel_controls), SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1, &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1, &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1, &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)), SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1, &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)), SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)), SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, Loading Loading @@ -471,7 +505,6 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, { u16 *cache = codec->reg_cache; if (reg < 0x7c) soc_ac97_ops->write(codec->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9712_reg))) Loading Loading @@ -595,7 +628,7 @@ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) return 0; err: printk(KERN_ERR "WM9712 AC97 reset failed\n"); dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; } Loading @@ -611,10 +644,8 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9712_reset(codec, 1); if (ret < 0) { printk(KERN_ERR "could not reset AC97 codec\n"); if (ret < 0) return ret; } wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading @@ -637,22 +668,18 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) { printk(KERN_ERR "wm9712: failed to register AC97 codec\n"); dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } ret = wm9712_reset(codec, 0); if (ret < 0) { printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n"); if (ret < 0) goto reset_err; } /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls, ARRAY_SIZE(wm9712_snd_ac97_controls)); return 0; Loading @@ -679,6 +706,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9712_reg, .controls = wm9712_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls), .dapm_widgets = wm9712_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), .dapm_routes = wm9712_audio_map, Loading @@ -687,6 +717,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { static int wm9712_probe(struct platform_device *pdev) { struct wm9712_priv *wm9712; wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL); if (wm9712 == NULL) return -ENOMEM; mutex_init(&wm9712->lock); platform_set_drvdata(pdev, wm9712); return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai)); } Loading sound/soc/codecs/wm9713.c +114 −88 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ struct wm9713_priv { u32 pll_in; /* PLL input frequency */ unsigned int hp_mixer[2]; struct mutex lock; }; static unsigned int ac97_read(struct snd_soc_codec *codec, Loading Loading @@ -59,13 +61,10 @@ static const u16 wm9713_reg[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x0001, 0x0000, 0x574d, 0x4c13, 0x0000, 0x0000, 0x0000 }; /* virtual HP mixers regs */ #define HPL_MIXER 0x80 #define HPR_MIXER 0x82 #define MICB_MUX 0x82 #define HPL_MIXER 0 #define HPR_MIXER 1 static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; Loading Loading @@ -110,7 +109,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */ SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */ }; static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0); Loading Loading @@ -234,6 +233,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, return 0; } static const unsigned int wm9713_mixer_mute_regs[] = { AC97_PC_BEEP, AC97_MASTER_TONE, AC97_PHONE, AC97_REC_SEL, AC97_PCM, AC97_AUX, }; /* We have to create a fake left and right HP mixers because * the codec only has a single control that is shared by both channels. Loading @@ -241,73 +248,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, * register map, thus we add a new (virtual) register to help determine the * audio route within the device. */ static int mixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u16 l, r, beep, tone, phone, rec, pcm, aux; l = ac97_read(w->codec, HPL_MIXER); r = ac97_read(w->codec, HPR_MIXER); beep = ac97_read(w->codec, AC97_PC_BEEP); tone = ac97_read(w->codec, AC97_MASTER_TONE); phone = ac97_read(w->codec, AC97_PHONE); rec = ac97_read(w->codec, AC97_REC_SEL); pcm = ac97_read(w->codec, AC97_PCM); aux = ac97_read(w->codec, AC97_AUX); if (event & SND_SOC_DAPM_PRE_REG) return 0; if ((l & 0x1) || (r & 0x1)) ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); unsigned int val = ucontrol->value.enumerated.item[0]; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; struct snd_soc_dapm_update update; bool change; mixer = mc->shift >> 8; shift = mc->shift & 0xff; mask = (1 << shift); mutex_lock(&wm9713->lock); old = wm9713->hp_mixer[mixer]; if (ucontrol->value.enumerated.item[0]) wm9713->hp_mixer[mixer] |= mask; else ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); if ((l & 0x2) || (r & 0x2)) ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff); wm9713->hp_mixer[mixer] &= ~mask; change = old != wm9713->hp_mixer[mixer]; if (change) { update.kcontrol = kcontrol; update.reg = wm9713_mixer_mute_regs[shift]; update.mask = 0x8000; if ((wm9713->hp_mixer[0] & mask) || (wm9713->hp_mixer[1] & mask)) update.val = 0x0; else ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000); update.val = 0x8000; if ((l & 0x4) || (r & 0x4)) ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); else ac97_write(w->codec, AC97_PHONE, phone | 0x8000); snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, &update); } if ((l & 0x8) || (r & 0x8)) ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff); else ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000); mutex_unlock(&wm9713->lock); if ((l & 0x10) || (r & 0x10)) ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); else ac97_write(w->codec, AC97_PCM, pcm | 0x8000); return change; } if ((l & 0x20) || (r & 0x20)) ac97_write(w->codec, AC97_AUX, aux & 0x7fff); else ac97_write(w->codec, AC97_AUX, aux | 0x8000); static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, shift; mixer = mc->shift >> 8; shift = mc->shift & 0xff; ucontrol->value.enumerated.item[0] = (wm9713->hp_mixer[mixer] >> shift) & 1; return 0; } #define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \ .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \ xshift, xmixer, 1, 0, 0) \ } /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0), WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5), WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4), WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3), WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2), WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1), WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0), }; /* Right Headphone Mixers */ static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0), WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5), WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4), WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3), WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2), WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1), WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0), }; /* headphone capture mux */ Loading Loading @@ -429,12 +458,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, &wm9713_mic_sel_mux_controls), SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, &wm9713_micb_sel_mux_controls), SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)), SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)), SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, Loading Loading @@ -667,7 +694,6 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { u16 *cache = codec->reg_cache; if (reg < 0x7c) soc_ac97_ops->write(codec->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9713_reg))) Loading @@ -689,7 +715,8 @@ struct _pll_div { * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 22) * 10) static void pll_factors(struct _pll_div *pll_div, unsigned int source) static void pll_factors(struct snd_soc_codec *codec, struct _pll_div *pll_div, unsigned int source) { u64 Kpart; unsigned int K, Ndiv, Nmod, target; Loading Loading @@ -724,7 +751,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source) Ndiv = target / source; if ((Ndiv < 5) || (Ndiv > 12)) printk(KERN_WARNING dev_warn(codec->dev, "WM9713 PLL N value %u out of recommended range!\n", Ndiv); Loading Loading @@ -768,7 +795,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, return 0; } pll_factors(&pll_div, freq_in); pll_factors(codec, &pll_div, freq_in); if (pll_div.k == 0) { reg = (pll_div.n << 12) | (pll_div.lf << 11) | Loading Loading @@ -1104,8 +1131,11 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm) soc_ac97_ops->reset(codec->ac97); if (soc_ac97_ops->warm_reset) soc_ac97_ops->warm_reset(codec->ac97); if (ac97_read(codec, 0) != wm9713_reg[0]) if (ac97_read(codec, 0) != wm9713_reg[0]) { dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; } return 0; } EXPORT_SYMBOL_GPL(wm9713_reset); Loading Loading @@ -1163,10 +1193,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9713_reset(codec, 1); if (ret < 0) { printk(KERN_ERR "could not reset AC97 codec\n"); if (ret < 0) return ret; } wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading @@ -1189,26 +1217,18 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713; int ret = 0, reg; wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL); if (wm9713 == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, wm9713); ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) goto codec_err; return ret; /* do a cold reset for the controller and then try * a warm reset followed by an optional cold reset for codec */ wm9713_reset(codec, 0); ret = wm9713_reset(codec, 1); if (ret < 0) { printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n"); if (ret < 0) goto reset_err; } wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading @@ -1216,23 +1236,16 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) reg = ac97_read(codec, AC97_CD) & 0x7fff; ac97_write(codec, AC97_CD, reg); snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls, ARRAY_SIZE(wm9713_snd_ac97_controls)); return 0; reset_err: snd_soc_free_ac97_codec(codec); codec_err: kfree(wm9713); return ret; } static int wm9713_soc_remove(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); snd_soc_free_ac97_codec(codec); kfree(wm9713); return 0; } Loading @@ -1248,6 +1261,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9713_reg, .controls = wm9713_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), .dapm_widgets = wm9713_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), .dapm_routes = wm9713_audio_map, Loading @@ -1256,6 +1272,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { static int wm9713_probe(struct platform_device *pdev) { struct wm9713_priv *wm9713; wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL); if (wm9713 == NULL) return -ENOMEM; mutex_init(&wm9713->lock); platform_set_drvdata(pdev, wm9713); return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)); } Loading Loading
sound/soc/codecs/wm9705.c +7 −7 Original line number Diff line number Diff line Loading @@ -300,6 +300,8 @@ static int wm9705_reset(struct snd_soc_codec *codec) return 0; /* Success */ } dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; } Loading @@ -317,10 +319,8 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9705_reset(codec); if (ret < 0) { printk(KERN_ERR "could not reset AC97 codec\n"); if (ret < 0) return ret; } for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) { soc_ac97_ops->write(codec->ac97, i, cache[i>>1]); Loading @@ -339,7 +339,7 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) { printk(KERN_ERR "wm9705: failed to register AC97 codec\n"); dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } Loading @@ -347,9 +347,6 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec) if (ret) goto reset_err; snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls, ARRAY_SIZE(wm9705_snd_ac97_controls)); return 0; reset_err: Loading @@ -374,6 +371,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9705_reg, .controls = wm9705_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls), .dapm_widgets = wm9705_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets), .dapm_routes = wm9705_audio_map, Loading
sound/soc/codecs/wm9712.c +110 −70 Original line number Diff line number Diff line Loading @@ -23,6 +23,11 @@ #include <sound/tlv.h> #include "wm9712.h" struct wm9712_priv { unsigned int hp_mixer[2]; struct mutex lock; }; static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg); static int ac97_write(struct snd_soc_codec *codec, Loading @@ -48,12 +53,10 @@ static const u16 wm9712_reg[] = { 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */ 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */ 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */ 0x0000, 0x0000 /* virtual hp mixers */ }; /* virtual HP mixers regs */ #define HPL_MIXER 0x80 #define HPR_MIXER 0x82 #define HPL_MIXER 0x0 #define HPR_MIXER 0x1 static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; Loading Loading @@ -157,75 +160,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv), }; static const unsigned int wm9712_mixer_mute_regs[] = { AC97_VIDEO, AC97_PCM, AC97_LINE, AC97_PHONE, AC97_CD, AC97_PC_BEEP, }; /* We have to create a fake left and right HP mixers because * the codec only has a single control that is shared by both channels. * This makes it impossible to determine the audio path. */ static int mixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u16 l, r, beep, line, phone, mic, pcm, aux; l = ac97_read(w->codec, HPL_MIXER); r = ac97_read(w->codec, HPR_MIXER); beep = ac97_read(w->codec, AC97_PC_BEEP); mic = ac97_read(w->codec, AC97_VIDEO); phone = ac97_read(w->codec, AC97_PHONE); line = ac97_read(w->codec, AC97_LINE); pcm = ac97_read(w->codec, AC97_PCM); aux = ac97_read(w->codec, AC97_CD); if (l & 0x1 || r & 0x1) ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff); struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); unsigned int val = ucontrol->value.enumerated.item[0]; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; struct snd_soc_dapm_update update; bool change; mixer = mc->shift >> 8; shift = mc->shift & 0xff; mask = 1 << shift; mutex_lock(&wm9712->lock); old = wm9712->hp_mixer[mixer]; if (ucontrol->value.enumerated.item[0]) wm9712->hp_mixer[mixer] |= mask; else ac97_write(w->codec, AC97_VIDEO, mic | 0x8000); if (l & 0x2 || r & 0x2) ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); wm9712->hp_mixer[mixer] &= ~mask; change = old != wm9712->hp_mixer[mixer]; if (change) { update.kcontrol = kcontrol; update.reg = wm9712_mixer_mute_regs[shift]; update.mask = 0x8000; if ((wm9712->hp_mixer[0] & mask) || (wm9712->hp_mixer[1] & mask)) update.val = 0x0; else ac97_write(w->codec, AC97_PCM, pcm | 0x8000); update.val = 0x8000; if (l & 0x4 || r & 0x4) ac97_write(w->codec, AC97_LINE, line & 0x7fff); else ac97_write(w->codec, AC97_LINE, line | 0x8000); snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, &update); } if (l & 0x8 || r & 0x8) ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); else ac97_write(w->codec, AC97_PHONE, phone | 0x8000); mutex_unlock(&wm9712->lock); if (l & 0x10 || r & 0x10) ac97_write(w->codec, AC97_CD, aux & 0x7fff); else ac97_write(w->codec, AC97_CD, aux | 0x8000); return change; } if (l & 0x20 || r & 0x20) ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); else ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int shift, mixer; mixer = mc->shift >> 8; shift = mc->shift & 0xff; ucontrol->value.enumerated.item[0] = (wm9712->hp_mixer[mixer] >> shift) & 1; return 0; } #define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \ .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \ (xmixer << 8) | xshift, 1, 0, 0) \ } /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0), WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5), WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4), WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3), WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2), WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1), WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0), }; /* Right Headphone Mixers */ static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0), WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5), WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4), WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3), WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2), WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1), WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0), }; /* Speaker Mixer */ Loading Loading @@ -299,12 +335,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, &wm9712_diff_sel_controls), SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1, &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1, &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1, &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)), SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1, &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)), SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)), SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, Loading Loading @@ -471,7 +505,6 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, { u16 *cache = codec->reg_cache; if (reg < 0x7c) soc_ac97_ops->write(codec->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9712_reg))) Loading Loading @@ -595,7 +628,7 @@ static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) return 0; err: printk(KERN_ERR "WM9712 AC97 reset failed\n"); dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; } Loading @@ -611,10 +644,8 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9712_reset(codec, 1); if (ret < 0) { printk(KERN_ERR "could not reset AC97 codec\n"); if (ret < 0) return ret; } wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading @@ -637,22 +668,18 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) { printk(KERN_ERR "wm9712: failed to register AC97 codec\n"); dev_err(codec->dev, "Failed to register AC97 codec\n"); return ret; } ret = wm9712_reset(codec, 0); if (ret < 0) { printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n"); if (ret < 0) goto reset_err; } /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls, ARRAY_SIZE(wm9712_snd_ac97_controls)); return 0; Loading @@ -679,6 +706,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9712_reg, .controls = wm9712_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls), .dapm_widgets = wm9712_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), .dapm_routes = wm9712_audio_map, Loading @@ -687,6 +717,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = { static int wm9712_probe(struct platform_device *pdev) { struct wm9712_priv *wm9712; wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL); if (wm9712 == NULL) return -ENOMEM; mutex_init(&wm9712->lock); platform_set_drvdata(pdev, wm9712); return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai)); } Loading
sound/soc/codecs/wm9713.c +114 −88 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ struct wm9713_priv { u32 pll_in; /* PLL input frequency */ unsigned int hp_mixer[2]; struct mutex lock; }; static unsigned int ac97_read(struct snd_soc_codec *codec, Loading Loading @@ -59,13 +61,10 @@ static const u16 wm9713_reg[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x0001, 0x0000, 0x574d, 0x4c13, 0x0000, 0x0000, 0x0000 }; /* virtual HP mixers regs */ #define HPL_MIXER 0x80 #define HPR_MIXER 0x82 #define MICB_MUX 0x82 #define HPL_MIXER 0 #define HPR_MIXER 1 static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; Loading Loading @@ -110,7 +109,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */ SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */ }; static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0); Loading Loading @@ -234,6 +233,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, return 0; } static const unsigned int wm9713_mixer_mute_regs[] = { AC97_PC_BEEP, AC97_MASTER_TONE, AC97_PHONE, AC97_REC_SEL, AC97_PCM, AC97_AUX, }; /* We have to create a fake left and right HP mixers because * the codec only has a single control that is shared by both channels. Loading @@ -241,73 +248,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, * register map, thus we add a new (virtual) register to help determine the * audio route within the device. */ static int mixer_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u16 l, r, beep, tone, phone, rec, pcm, aux; l = ac97_read(w->codec, HPL_MIXER); r = ac97_read(w->codec, HPR_MIXER); beep = ac97_read(w->codec, AC97_PC_BEEP); tone = ac97_read(w->codec, AC97_MASTER_TONE); phone = ac97_read(w->codec, AC97_PHONE); rec = ac97_read(w->codec, AC97_REC_SEL); pcm = ac97_read(w->codec, AC97_PCM); aux = ac97_read(w->codec, AC97_AUX); if (event & SND_SOC_DAPM_PRE_REG) return 0; if ((l & 0x1) || (r & 0x1)) ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff); struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); unsigned int val = ucontrol->value.enumerated.item[0]; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, mask, shift, old; struct snd_soc_dapm_update update; bool change; mixer = mc->shift >> 8; shift = mc->shift & 0xff; mask = (1 << shift); mutex_lock(&wm9713->lock); old = wm9713->hp_mixer[mixer]; if (ucontrol->value.enumerated.item[0]) wm9713->hp_mixer[mixer] |= mask; else ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000); if ((l & 0x2) || (r & 0x2)) ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff); wm9713->hp_mixer[mixer] &= ~mask; change = old != wm9713->hp_mixer[mixer]; if (change) { update.kcontrol = kcontrol; update.reg = wm9713_mixer_mute_regs[shift]; update.mask = 0x8000; if ((wm9713->hp_mixer[0] & mask) || (wm9713->hp_mixer[1] & mask)) update.val = 0x0; else ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000); update.val = 0x8000; if ((l & 0x4) || (r & 0x4)) ac97_write(w->codec, AC97_PHONE, phone & 0x7fff); else ac97_write(w->codec, AC97_PHONE, phone | 0x8000); snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, &update); } if ((l & 0x8) || (r & 0x8)) ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff); else ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000); mutex_unlock(&wm9713->lock); if ((l & 0x10) || (r & 0x10)) ac97_write(w->codec, AC97_PCM, pcm & 0x7fff); else ac97_write(w->codec, AC97_PCM, pcm | 0x8000); return change; } if ((l & 0x20) || (r & 0x20)) ac97_write(w->codec, AC97_AUX, aux & 0x7fff); else ac97_write(w->codec, AC97_AUX, aux | 0x8000); static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int mixer, shift; mixer = mc->shift >> 8; shift = mc->shift & 0xff; ucontrol->value.enumerated.item[0] = (wm9713->hp_mixer[mixer] >> shift) & 1; return 0; } #define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \ .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \ xshift, xmixer, 1, 0, 0) \ } /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0), WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5), WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4), WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3), WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2), WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1), WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0), }; /* Right Headphone Mixers */ static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0), SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0), SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0), SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0), SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0), WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5), WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4), WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3), WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2), WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1), WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0), }; /* headphone capture mux */ Loading Loading @@ -429,12 +458,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, &wm9713_mic_sel_mux_controls), SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, &wm9713_micb_sel_mux_controls), SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls), mixer_event, SND_SOC_DAPM_POST_REG), SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)), SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)), SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, Loading Loading @@ -667,7 +694,6 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) { u16 *cache = codec->reg_cache; if (reg < 0x7c) soc_ac97_ops->write(codec->ac97, reg, val); reg = reg >> 1; if (reg < (ARRAY_SIZE(wm9713_reg))) Loading @@ -689,7 +715,8 @@ struct _pll_div { * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 22) * 10) static void pll_factors(struct _pll_div *pll_div, unsigned int source) static void pll_factors(struct snd_soc_codec *codec, struct _pll_div *pll_div, unsigned int source) { u64 Kpart; unsigned int K, Ndiv, Nmod, target; Loading Loading @@ -724,7 +751,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source) Ndiv = target / source; if ((Ndiv < 5) || (Ndiv > 12)) printk(KERN_WARNING dev_warn(codec->dev, "WM9713 PLL N value %u out of recommended range!\n", Ndiv); Loading Loading @@ -768,7 +795,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, return 0; } pll_factors(&pll_div, freq_in); pll_factors(codec, &pll_div, freq_in); if (pll_div.k == 0) { reg = (pll_div.n << 12) | (pll_div.lf << 11) | Loading Loading @@ -1104,8 +1131,11 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm) soc_ac97_ops->reset(codec->ac97); if (soc_ac97_ops->warm_reset) soc_ac97_ops->warm_reset(codec->ac97); if (ac97_read(codec, 0) != wm9713_reg[0]) if (ac97_read(codec, 0) != wm9713_reg[0]) { dev_err(codec->dev, "Failed to reset: AC97 link error\n"); return -EIO; } return 0; } EXPORT_SYMBOL_GPL(wm9713_reset); Loading Loading @@ -1163,10 +1193,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) u16 *cache = codec->reg_cache; ret = wm9713_reset(codec, 1); if (ret < 0) { printk(KERN_ERR "could not reset AC97 codec\n"); if (ret < 0) return ret; } wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading @@ -1189,26 +1217,18 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713; int ret = 0, reg; wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL); if (wm9713 == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, wm9713); ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0); if (ret < 0) goto codec_err; return ret; /* do a cold reset for the controller and then try * a warm reset followed by an optional cold reset for codec */ wm9713_reset(codec, 0); ret = wm9713_reset(codec, 1); if (ret < 0) { printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n"); if (ret < 0) goto reset_err; } wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); Loading @@ -1216,23 +1236,16 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) reg = ac97_read(codec, AC97_CD) & 0x7fff; ac97_write(codec, AC97_CD, reg); snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls, ARRAY_SIZE(wm9713_snd_ac97_controls)); return 0; reset_err: snd_soc_free_ac97_codec(codec); codec_err: kfree(wm9713); return ret; } static int wm9713_soc_remove(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); snd_soc_free_ac97_codec(codec); kfree(wm9713); return 0; } Loading @@ -1248,6 +1261,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { .reg_word_size = sizeof(u16), .reg_cache_step = 2, .reg_cache_default = wm9713_reg, .controls = wm9713_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), .dapm_widgets = wm9713_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), .dapm_routes = wm9713_audio_map, Loading @@ -1256,6 +1272,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { static int wm9713_probe(struct platform_device *pdev) { struct wm9713_priv *wm9713; wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL); if (wm9713 == NULL) return -ENOMEM; mutex_init(&wm9713->lock); platform_set_drvdata(pdev, wm9713); return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)); } Loading