aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-01-10 08:56:46 +0100
committerTakashi Iwai <tiwai@suse.de>2013-01-12 08:44:27 +0100
commit62f3a2f718131e6f42746ccd26dbf4eb5eab677a (patch)
treec578dd9728ee29fde924c8e957c4c3cef30e1f93 /sound/pci/hda/hda_codec.c
parentd7fdc00ae50b3dc02364301b334a6352c58e9e85 (diff)
ALSA: hda - More strict correction of invalid pinctl bits
Check more strictly about the validity of pinctl values in snd_hda_set_pin_ctl() and correct the wrong bits automatically. Also provide the helper function to correct pinctl bits to codec drivers. This automatically fixes the invalid pinctl writes that are found in a few Realtek fixups for NID 0x0f amp like ASUS A6Rp. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c66
1 files changed, 52 insertions, 14 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 505cb72e4ee..0a531f2f925 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -5275,23 +5275,61 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
}
EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
+/* correct the pin ctl value for matching with the pin cap */
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+ hda_nid_t pin, unsigned int val)
+{
+ static unsigned int cap_lists[][2] = {
+ { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
+ { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
+ { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
+ { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
+ };
+ unsigned int cap;
+
+ if (!val)
+ return 0;
+ cap = snd_hda_query_pin_caps(codec, pin);
+ if (!cap)
+ return val; /* don't know what to do... */
+
+ if (val & AC_PINCTL_OUT_EN) {
+ if (!(cap & AC_PINCAP_OUT))
+ val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV))
+ val &= ~AC_PINCTL_HP_EN;
+ }
+
+ if (val & AC_PINCTL_IN_EN) {
+ if (!(cap & AC_PINCAP_IN))
+ val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+ else {
+ unsigned int vcap, vref;
+ int i;
+ vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ vref = val & AC_PINCTL_VREFEN;
+ for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
+ if (vref == cap_lists[i][0] &&
+ !(vcap & cap_lists[i][1])) {
+ if (i == ARRAY_SIZE(cap_lists) - 1)
+ vref = AC_PINCTL_VREF_HIZ;
+ else
+ vref = cap_lists[i + 1][0];
+ }
+ }
+ val &= ~AC_PINCTL_VREFEN;
+ val |= vref;
+ }
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_HDA(snd_hda_correct_pin_ctl);
+
int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
unsigned int val, bool cached)
{
- if (val) {
- unsigned int cap = snd_hda_query_pin_caps(codec, pin);
- if (cap && (val & AC_PINCTL_OUT_EN)) {
- if (!(cap & AC_PINCAP_OUT))
- val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
- else if ((val & AC_PINCTL_HP_EN) &&
- !(cap & AC_PINCAP_HP_DRV))
- val &= ~AC_PINCTL_HP_EN;
- }
- if (cap && (val & AC_PINCTL_IN_EN)) {
- if (!(cap & AC_PINCAP_IN))
- val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
- }
- }
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
snd_hda_codec_set_pin_target(codec, pin, val);
if (cached)
return snd_hda_codec_update_cache(codec, pin, 0,