From 18dcd3044e4c4b3ab6341c98e8d0e81e0d58d5e3 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 2 Apr 2012 15:40:27 +0200 Subject: ALSA: hda - Fix internal mic for Lenovo Ideapad U300s The internal mic input is phase inverted on one channel. To avoid people in userspace summing the channels together and get zero result, use a separate mixer control for the inverted channel. BugLink: https://bugs.launchpad.net/bugs/903853 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 88 +++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 13 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 8c6523bbc79..213fb80c11f 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -142,6 +142,7 @@ struct conexant_spec { unsigned int asus:1; unsigned int pin_eapd_ctrls:1; unsigned int single_adc_amp:1; + unsigned int fixup_stereo_dmic:1; unsigned int adc_switching:1; @@ -4107,9 +4108,9 @@ static int cx_auto_init(struct hda_codec *codec) static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, const char *dir, int cidx, - hda_nid_t nid, int hda_dir, int amp_idx) + hda_nid_t nid, int hda_dir, int amp_idx, int chs) { - static char name[32]; + static char name[44]; static struct snd_kcontrol_new knew[] = { HDA_CODEC_VOLUME(name, 0, 0, 0), HDA_CODEC_MUTE(name, 0, 0, 0), @@ -4119,7 +4120,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, for (i = 0; i < 2; i++) { struct snd_kcontrol *kctl; - knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx, + knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx, hda_dir); knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; knew[i].index = cidx; @@ -4138,7 +4139,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, } #define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ - cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0) + cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3) #define cx_auto_add_pb_volume(codec, nid, str, idx) \ cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) @@ -4208,6 +4209,36 @@ static int cx_auto_build_output_controls(struct hda_codec *codec) return 0; } +/* Returns zero if this is a normal stereo channel, and non-zero if it should + be split in two independent channels. + dest_label must be at least 44 characters. */ +static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label, + char *dest_label, int nid) +{ + struct conexant_spec *spec = codec->spec; + int i; + + if (!spec->fixup_stereo_dmic) + return 0; + + for (i = 0; i < AUTO_CFG_MAX_INS; i++) { + int def_conf; + if (spec->autocfg.inputs[i].pin != nid) + continue; + + if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC) + return 0; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) + return 0; + + /* Finally found the inverted internal mic! */ + snprintf(dest_label, 44, "Inverted %s", label); + return 1; + } + return 0; +} + static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, const char *label, const char *pfx, int cidx) @@ -4216,14 +4247,25 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, int i; for (i = 0; i < spec->num_adc_nids; i++) { + char rightch_label[44]; hda_nid_t adc_nid = spec->adc_nids[i]; int idx = get_input_connection(codec, adc_nid, nid); if (idx < 0) continue; if (spec->single_adc_amp) idx = 0; + + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + /* Make two independent kcontrols for left and right */ + int err = cx_auto_add_volume_idx(codec, label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 2); + } return cx_auto_add_volume_idx(codec, label, pfx, - cidx, adc_nid, HDA_INPUT, idx); + cidx, adc_nid, HDA_INPUT, idx, 3); } return 0; } @@ -4236,9 +4278,19 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, int i, con; nid = spec->imux_info[idx].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + char rightch_label[44]; + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + int err = cx_auto_add_volume_idx(codec, label, " Boost", + cidx, nid, HDA_INPUT, 0, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, " Boost", + cidx, nid, HDA_INPUT, 0, 2); + } return cx_auto_add_volume(codec, label, " Boost", cidx, nid, HDA_INPUT); + } con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, &mux, false, 0); if (con < 0) @@ -4405,22 +4457,30 @@ static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg) } -static void apply_pin_fixup(struct hda_codec *codec, +enum { + CXT_PINCFG_LENOVO_X200, + CXT_FIXUP_STEREO_DMIC, +}; + +static void apply_fixup(struct hda_codec *codec, const struct snd_pci_quirk *quirk, const struct cxt_pincfg **table) { + struct conexant_spec *spec = codec->spec; + quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (quirk) { + if (quirk && table[quirk->value]) { snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", quirk->name); apply_pincfg(codec, table[quirk->value]); } + if (quirk->value == CXT_FIXUP_STEREO_DMIC) { + snd_printdd(KERN_INFO "hda_codec: applying internal mic workaround for %s\n", + quirk->name); + spec->fixup_stereo_dmic = 1; + } } -enum { - CXT_PINCFG_LENOVO_X200, -}; - static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ { 0x17, 0x21a11000 }, /* dock-mic */ @@ -4431,10 +4491,12 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { static const struct cxt_pincfg *cxt_pincfg_tbl[] = { [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, + [CXT_FIXUP_STEREO_DMIC] = NULL, }; static const struct snd_pci_quirk cxt_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), + SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), {} }; @@ -4477,7 +4539,7 @@ static int patch_conexant_auto(struct hda_codec *codec) break; } - apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); + apply_fixup(codec, cxt_fixups, cxt_pincfg_tbl); /* Show mute-led control only on HP laptops * This is a sort of white-list: on HP laptops, EAPD corresponds -- cgit v1.2.3 From 83b0c6ba999643ee8ad6329f26e1cdc870e1a920 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 10 Apr 2012 13:05:29 +0200 Subject: ALSA: hda - Fix oops caused by recent commit "Fix internal mic for Lenovo Ideapad U300s" Make sure we don't dereference the "quirk" pointer when it is null. Reported-by: Dan Carpenter Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index cbe115b6c80..abb59f472d4 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -4441,7 +4441,9 @@ static void apply_fixup(struct hda_codec *codec, struct conexant_spec *spec = codec->spec; quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (quirk && table[quirk->value]) { + if (!quirk) + return; + if (table[quirk->value]) { snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", quirk->name); apply_pincfg(codec, table[quirk->value]); -- cgit v1.2.3 From cdd03cedc5b55da017fcdeff7d47cac2639cded8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 20 Apr 2012 12:34:50 +0200 Subject: ALSA: hda - Introduce snd_hda_set_pin_ctl*() helper functions For setting the pin-control values more safely to match with the actual pin capability bits, a copule of new helper functions, snd_hda_set_pin_ctl() and snd_hda_set_pin_ctl_cache(), are introduced. These are simple replacement of the codec verb write with AC_VERB_SET_PIN_WIDGET but do more sanity checks and filter out superfluous pin-control bits if they don't fit with the corresponding pin capabilities. Some codecs are screwed up or ignore the command when such a wrong bit is set. These helpers will avoid such secret errors. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 26 ++++++++++++++++++++++ sound/pci/hda/hda_local.h | 38 ++++++++++++++++++++++++++++++++ sound/pci/hda/patch_analog.c | 8 +++---- sound/pci/hda/patch_ca0110.c | 6 ++---- sound/pci/hda/patch_ca0132.c | 7 ++---- sound/pci/hda/patch_cirrus.c | 21 ++++++------------ sound/pci/hda/patch_conexant.c | 44 +++++++++++++------------------------ sound/pci/hda/patch_realtek.c | 49 ++++++++++++++++-------------------------- sound/pci/hda/patch_sigmatel.c | 20 ++++++----------- sound/pci/hda/patch_via.c | 23 ++++++-------------- 10 files changed, 125 insertions(+), 117 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7a8fcc4c15f..2d9716e7a11 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4795,6 +4795,32 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup); +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 (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); + } + } + if (cached) + return snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + else + return snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); +} +EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl); + /* * Helper for automatic pin configuration */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 0ec9248165b..17d425775c9 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -502,6 +502,44 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) #define PIN_HP_AMP (AC_PINCTL_HP_EN) +int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, + unsigned int val, bool cached); + +/** + * _snd_hda_set_pin_ctl - Set a pin-control value safely + * @codec: the codec instance + * @pin: the pin NID to set the control + * @val: the pin-control value (AC_PINCTL_* bits) + * + * This function sets the pin-control value to the given pin, but + * filters out the invalid pin-control bits when the pin has no such + * capabilities. For example, when PIN_HP is passed but the pin has no + * HP-drive capability, the HP bit is omitted. + * + * The function doesn't check the input VREF capability bits, though. + * Also, this function is only for analog pins, not for HDMI pins. + */ +static inline int +snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val) +{ + return _snd_hda_set_pin_ctl(codec, pin, val, false); +} + +/** + * snd_hda_set_pin_ctl_cache - Set a pin-control value safely + * @codec: the codec instance + * @pin: the pin NID to set the control + * @val: the pin-control value (AC_PINCTL_* bits) + * + * Just like snd_hda_set_pin_ctl() but write to cache as well. + */ +static inline int +snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin, + unsigned int val) +{ + return _snd_hda_set_pin_ctl(codec, pin, val, true); +} + /* * get widget capabilities */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 7143393927d..38163abeea9 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1742,9 +1742,7 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol, if (! ad198x_eapd_put(kcontrol, ucontrol)) return 0; /* change speaker pin appropriately */ - snd_hda_codec_write(codec, 0x05, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->cur_eapd ? PIN_OUT : 0); + snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0); /* toggle HP mute appropriately */ snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, HDA_AMP_MUTE, @@ -3103,7 +3101,7 @@ static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec, int dac_idx) { /* set as output */ - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + snd_hda_set_pin_ctl(codec, nid, pin_type); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); switch (nid) { case 0x11: /* port-A - DAC 03 */ @@ -3165,7 +3163,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0); break; } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, nid, type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN); if (nid != AD1988_PIN_CD_NID) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 09ccfabb4a1..646dc976f4b 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -341,8 +341,7 @@ static int ca0110_build_pcms(struct hda_codec *codec) static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_set_pin_ctl(codec, pin, PIN_HP); if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -356,8 +355,7 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80); + snd_hda_set_pin_ctl(codec, pin, PIN_VREF80); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 21d91d580da..ea63333f41f 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -239,8 +239,7 @@ enum get_set { static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_set_pin_ctl(codec, pin, PIN_HP); if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -254,9 +253,7 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_VREF80); + snd_hda_set_pin_ctl(codec, pin, PIN_VREF80); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index c83ccdba1e5..778e4b9dd88 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -933,8 +933,7 @@ static void cs_automute(struct hda_codec *codec) pin_ctl = 0; nid = cfg->speaker_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl); + snd_hda_set_pin_ctl(codec, nid, pin_ctl); } if (spec->gpio_eapd_hp) { unsigned int gpio = hp_present ? @@ -948,16 +947,14 @@ static void cs_automute(struct hda_codec *codec) /* mute HPs if spdif jack (SENSE_B) is present */ for (i = 0; i < cfg->hp_outs; i++) { nid = cfg->hp_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, nid, (spdif_present && spec->sense_b) ? 0 : PIN_HP); } /* SPDIF TX on/off */ if (cfg->dig_outs) { nid = cfg->dig_out_pins[0]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, nid, spdif_present ? PIN_OUT : 0); } @@ -1024,13 +1021,11 @@ static void init_output(struct hda_codec *codec) /* set appropriate pin controls */ for (i = 0; i < cfg->line_outs; i++) - snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT); /* HP */ for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_set_pin_ctl(codec, nid, PIN_HP); if (!cfg->speaker_outs) continue; if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { @@ -1041,8 +1036,7 @@ static void init_output(struct hda_codec *codec) /* Speaker */ for (i = 0; i < cfg->speaker_outs; i++) - snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT); /* SPDIF is enabled on presence detect for CS421x */ if (spec->hp_detect || spec->spdif_detect) @@ -1069,8 +1063,7 @@ static void init_input(struct hda_codec *codec) if (caps & AC_PINCAP_VREF_80) ctl = PIN_VREF80; } - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); + snd_hda_set_pin_ctl(codec, pin, ctl); snd_hda_codec_write(codec, spec->adc_nid[i], 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(spec->adc_idx[i])); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 6e04c2bf06d..afa510f0b99 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1602,17 +1602,13 @@ static void cxt5051_update_speaker(struct hda_codec *codec) unsigned int pinctl; /* headphone pin */ pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x16, pinctl); /* speaker pin */ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1a, pinctl); /* on ideapad there is an additional speaker (subwoofer) to mute */ if (spec->ideapad) - snd_hda_codec_write(codec, 0x1b, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1b, pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -1997,8 +1993,7 @@ static void cxt5066_update_speaker(struct hda_codec *codec) /* Port A (HP) */ pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0; - snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x19, pinctl); /* Port D (HP/LO) */ pinctl = spec->cur_eapd ? spec->port_d_mode : 0; @@ -2011,13 +2006,11 @@ static void cxt5066_update_speaker(struct hda_codec *codec) if (!hp_port_d_present(spec)) pinctl = 0; } - snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1c, pinctl); /* CLASS_D AMP */ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; - snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pinctl); + snd_hda_set_pin_ctl(codec, 0x1f, pinctl); } /* turn on/off EAPD (+ mute HP) as a master switch */ @@ -2048,8 +2041,7 @@ static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec) /* Even though port F is the DC input, the bias is controlled on port B. * we also leave that port as an active input (but unselected) in DC mode * just in case that is necessary to make the bias setting take effect. */ - return snd_hda_codec_write_cache(codec, 0x1a, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + return snd_hda_set_pin_ctl_cache(codec, 0x1a, cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index); } @@ -2082,14 +2074,14 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec) } /* disable DC (port F) */ - snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, 0x1e, 0); /* external mic, port B */ - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, 0x1a, spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0); /* internal mic, port C */ - snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, 0x1b, spec->ext_mic_present ? 0 : PIN_VREF80); } @@ -3358,9 +3350,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, struct conexant_spec *spec = codec->spec; int i; for (i = 0; i < num_pins; i++) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - on ? PIN_OUT : 0); + snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0); if (spec->pin_eapd_ctrls) cx_auto_turn_eapd(codec, num_pins, pins, on); } @@ -3977,8 +3967,7 @@ static void cx_auto_init_output(struct hda_codec *codec) if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) & AC_PINCAP_HP_DRV) val |= AC_PINCTL_HP_EN; - snd_hda_codec_write(codec, cfg->hp_pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val); } mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); @@ -4036,8 +4025,7 @@ static void cx_auto_init_input(struct hda_codec *codec) type = PIN_VREF80; else type = PIN_IN; - snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, type); + snd_hda_set_pin_ctl(codec, cfg->inputs[i].pin, type); } if (spec->auto_mic) { @@ -4064,11 +4052,9 @@ static void cx_auto_init_digital(struct hda_codec *codec) struct auto_pin_cfg *cfg = &spec->autocfg; if (spec->multiout.dig_out_nid) - snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT); if (spec->dig_in_nid) - snd_hda_codec_write(codec, cfg->dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); + snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN); } static int cx_auto_init(struct hda_codec *codec) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e65e3543305..9560b8e1e85 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -319,13 +319,16 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, /* for shared I/O, change the pin-control accordingly */ if (spec->shared_mic_hp) { + unsigned int val; + hda_nid_t pin = spec->autocfg.inputs[1].pin; /* NOTE: this assumes that there are only two inputs, the * first is the real internal mic and the second is HP jack. */ - snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->cur_mux[adc_idx] ? - PIN_VREF80 : PIN_HP); + if (spec->cur_mux[adc_idx]) + val = PIN_VREF80; + else + val = PIN_HP; + snd_hda_set_pin_ctl(codec, pin, val); spec->automute_speaker = !spec->cur_mux[adc_idx]; call_update_outputs(codec); } @@ -394,7 +397,7 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, else if (pincap & AC_PINCAP_VREF_GRD) val = PIN_VREFGRD; } - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, nid, val); } /* @@ -517,9 +520,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, } else val = 0; val |= pin_bits; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - val); + snd_hda_set_pin_ctl(codec, nid, val); break; case ALC_AUTOMUTE_AMP: snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, @@ -1621,8 +1622,7 @@ static void alc_auto_init_digital(struct hda_codec *codec) pin = spec->autocfg.dig_out_pins[i]; if (!pin) continue; - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_set_pin_ctl(codec, pin, PIN_OUT); if (!i) dac = spec->multiout.dig_out_nid; else @@ -1635,9 +1635,7 @@ static void alc_auto_init_digital(struct hda_codec *codec) } pin = spec->autocfg.dig_in_pin; if (pin) - snd_hda_codec_write(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_IN); + snd_hda_set_pin_ctl(codec, pin, PIN_IN); } /* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ @@ -2856,8 +2854,7 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_type) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); + snd_hda_set_pin_ctl(codec, nid, pin_type); /* unmute pin */ if (nid_has_mute(codec, nid, HDA_OUTPUT)) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -3998,9 +3995,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); if (output) { - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_OUT); + snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); @@ -4009,9 +4004,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - spec->multi_io[idx].ctl_in); + snd_hda_set_pin_ctl_cache(codec, nid, + spec->multi_io[idx].ctl_in); } return 0; } @@ -5171,8 +5165,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, val = snd_hda_codec_read(codec, nids[i], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_80; - snd_hda_codec_write(codec, nids[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, nids[i], val); spec->keep_vref_in_automute = 1; break; } @@ -5193,8 +5186,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, val = snd_hda_codec_read(codec, nids[i], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_50; - snd_hda_codec_write(codec, nids[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, nids[i], val); } spec->keep_vref_in_automute = 1; } @@ -5943,9 +5935,7 @@ static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; unsigned int pinval = enabled ? 0x20 : 0x24; - snd_hda_codec_update_cache(codec, 0x19, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pinval); + snd_hda_set_pin_ctl_cache(codec, 0x19, pinval); } static void alc269_fixup_mic2_mute(struct hda_codec *codec, @@ -6342,8 +6332,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) val |= AC_PINCTL_IN_EN; val |= AC_PINCTL_VREF_50; - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + snd_hda_set_pin_ctl(codec, 0x0f, val); spec->keep_vref_in_automute = 1; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 4742cac26aa..21de62b7c99 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -681,8 +681,7 @@ static int stac_vrefout_set(struct hda_codec *codec, pinctl &= ~AC_PINCTL_VREFEN; pinctl |= (new_vref & AC_PINCTL_VREFEN); - error = snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl); if (error < 0) return error; @@ -706,8 +705,7 @@ static unsigned int stac92xx_vref_set(struct hda_codec *codec, else pincfg |= AC_PINCTL_IN_EN; - error = snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg); + error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg); if (error < 0) return error; else @@ -2524,8 +2522,7 @@ static unsigned int stac92xx_get_default_vref(struct hda_codec *codec, static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) { - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + snd_hda_set_pin_ctl_cache(codec, nid, pin_type); } #define stac92xx_hp_switch_info snd_ctl_boolean_mono_info @@ -4460,8 +4457,7 @@ static void stac92xx_shutup_pins(struct hda_codec *codec) struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) - snd_hda_codec_write(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, pin->nid, 0); } } @@ -4517,9 +4513,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl |= flag; if (old_ctl != pin_ctl) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl); + snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl); } static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, @@ -4528,9 +4522,7 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_ctl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); if (pin_ctl & flag) - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl & ~flag); + snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag); } static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 06214fdc948..8ee531aeda6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -532,8 +532,7 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, { if (!pin) return; - snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_type); + snd_hda_set_pin_ctl(codec, pin, pin_type); if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x02); @@ -666,8 +665,7 @@ static void via_auto_init_analog_input(struct hda_codec *codec) ctl = PIN_VREF50; else ctl = PIN_IN; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); + snd_hda_set_pin_ctl(codec, nid, ctl); } /* init input-src */ @@ -1006,9 +1004,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); parm |= out_in; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - parm); + snd_hda_set_pin_ctl(codec, nid, parm); if (out_in == AC_PINCTL_OUT_EN) { mute_aa_path(codec, 1); notify_aa_path_ctls(codec); @@ -1647,8 +1643,7 @@ static void toggle_output_mutes(struct hda_codec *codec, int num_pins, parm &= ~AC_PINCTL_OUT_EN; else parm |= AC_PINCTL_OUT_EN; - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, parm); + snd_hda_set_pin_ctl(codec, pins[i], parm); } } @@ -1709,8 +1704,7 @@ static void via_gpio_control(struct hda_codec *codec) if (gpio_data == 0x02) { /* unmute line out */ - snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, + snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], PIN_OUT); if (vol_counter & 0x20) { /* decrease volume */ @@ -1728,9 +1722,7 @@ static void via_gpio_control(struct hda_codec *codec) } } else if (!(gpio_data & 0x02)) { /* mute line out */ - snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - 0); + snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], 0); } } @@ -2757,8 +2749,7 @@ static void via_auto_init_dig_in(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (!spec->dig_in_nid) return; - snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); + snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN); } /* initialize the unsolicited events */ -- cgit v1.2.3 From 4740860b534e86d7db3b26cbd980bec8e4c807e8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 20 Apr 2012 13:06:53 +0200 Subject: ALSA: hda - Add snd_hda_get_default_vref() helper function Add a new helper function to guess the default VREF pin control bits for mic in. This can be used to set the pin control value safely matching with the actual pin capabilities. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 27 +++++++++++++++++++++++++++ sound/pci/hda/hda_local.h | 2 ++ sound/pci/hda/patch_analog.c | 7 +++++-- sound/pci/hda/patch_ca0110.c | 3 ++- sound/pci/hda/patch_ca0132.c | 3 ++- sound/pci/hda/patch_cirrus.c | 8 ++------ sound/pci/hda/patch_conexant.c | 9 ++++----- sound/pci/hda/patch_realtek.c | 22 +++------------------- sound/pci/hda/patch_sigmatel.c | 26 +++++--------------------- sound/pci/hda/patch_via.c | 7 ++++--- 10 files changed, 56 insertions(+), 58 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2d9716e7a11..8b8b74a1284 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4795,6 +4795,33 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup); +/** + * snd_hda_get_default_vref - Get the default (mic) VREF pin bits + * + * Guess the suitable VREF pin bits to be set as the pin-control value. + * Note: the function doesn't set the AC_PINCTL_IN_EN bit. + */ +unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int pincap; + unsigned int oldval; + oldval = snd_hda_codec_read(codec, pin, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + pincap = snd_hda_query_pin_caps(codec, pin); + pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + /* Exception: if the default pin setup is vref50, we give it priority */ + if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) + return AC_PINCTL_VREF_80; + else if (pincap & AC_PINCAP_VREF_50) + return AC_PINCTL_VREF_50; + else if (pincap & AC_PINCAP_VREF_100) + return AC_PINCTL_VREF_100; + else if (pincap & AC_PINCAP_VREF_GRD) + return AC_PINCTL_VREF_GRD; + return AC_PINCTL_VREF_HIZ; +} +EXPORT_SYMBOL_HDA(snd_hda_get_default_vref); + int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached) { diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 17d425775c9..a5cee952547 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -502,6 +502,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) #define PIN_HP_AMP (AC_PINCTL_HP_EN) +unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin); int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached); @@ -517,6 +518,7 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, * HP-drive capability, the HP bit is omitted. * * The function doesn't check the input VREF capability bits, though. + * Use snd_hda_get_default_vref() to guess the right value. * Also, this function is only for analog pins, not for HDMI pins. */ static inline int diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 38163abeea9..723bb9cb5f0 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3155,6 +3155,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; int type = cfg->inputs[i].type; + int val; switch (nid) { case 0x15: /* port-C */ snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); @@ -3163,8 +3164,10 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0); break; } - snd_hda_set_pin_ctl(codec, nid, - type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN); + val = PIN_IN; + if (type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); if (nid != AD1988_PIN_CD_NID) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 646dc976f4b..a3b70a8f6df 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -355,7 +355,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_VREF80); + snd_hda_set_pin_ctl(codec, pin, PIN_IN | + snd_hda_get_default_vref(codec, pin)); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index ea63333f41f..d290a8ff010 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -253,7 +253,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc) { if (pin) { - snd_hda_set_pin_ctl(codec, pin, PIN_VREF80); + snd_hda_set_pin_ctl(codec, pin, PIN_IN | + snd_hda_get_default_vref(codec, pin)); if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP) snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 778e4b9dd88..48c6d8186e9 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1057,12 +1057,8 @@ static void init_input(struct hda_codec *codec) continue; /* set appropriate pin control and mute first */ ctl = PIN_IN; - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - unsigned int caps = snd_hda_query_pin_caps(codec, pin); - caps >>= AC_PINCAP_VREF_SHIFT; - if (caps & AC_PINCAP_VREF_80) - ctl = PIN_VREF80; - } + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, pin); snd_hda_set_pin_ctl(codec, pin, ctl); snd_hda_codec_write(codec, spec->adc_nid[i], 0, AC_VERB_SET_AMP_GAIN_MUTE, diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index afa510f0b99..aabdb9e9a48 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -4020,12 +4020,11 @@ static void cx_auto_init_input(struct hda_codec *codec) } for (i = 0; i < cfg->num_inputs; i++) { - unsigned int type; + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int type = PIN_IN; if (cfg->inputs[i].type == AUTO_PIN_MIC) - type = PIN_VREF80; - else - type = PIN_IN; - snd_hda_set_pin_ctl(codec, cfg->inputs[i].pin, type); + type |= snd_hda_get_default_vref(codec, pin); + snd_hda_set_pin_ctl(codec, pin, type); } if (spec->auto_mic) { diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9560b8e1e85..951a090cef4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -325,7 +325,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, * first is the real internal mic and the second is HP jack. */ if (spec->cur_mux[adc_idx]) - val = PIN_VREF80; + val = snd_hda_get_default_vref(codec, pin) | PIN_IN; else val = PIN_HP; snd_hda_set_pin_ctl(codec, pin, val); @@ -379,24 +379,8 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, int auto_pin_type) { unsigned int val = PIN_IN; - - if (auto_pin_type == AUTO_PIN_MIC) { - unsigned int pincap; - unsigned int oldval; - oldval = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - pincap = snd_hda_query_pin_caps(codec, nid); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - /* if the default pin setup is vref50, we give it priority */ - if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50) - val = PIN_VREF80; - else if (pincap & AC_PINCAP_VREF_50) - val = PIN_VREF50; - else if (pincap & AC_PINCAP_VREF_100) - val = PIN_VREF100; - else if (pincap & AC_PINCAP_VREF_GRD) - val = PIN_VREFGRD; - } + if (auto_pin_type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); snd_hda_set_pin_ctl(codec, nid, val); } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 21de62b7c99..884f8ad351f 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2503,22 +2503,6 @@ static int stac92xx_build_pcms(struct hda_codec *codec) return 0; } -static unsigned int stac92xx_get_default_vref(struct hda_codec *codec, - hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; - if (pincap & AC_PINCAP_VREF_100) - return AC_PINCTL_VREF_100; - if (pincap & AC_PINCAP_VREF_80) - return AC_PINCTL_VREF_80; - if (pincap & AC_PINCAP_VREF_50) - return AC_PINCTL_VREF_50; - if (pincap & AC_PINCAP_VREF_GRD) - return AC_PINCTL_VREF_GRD; - return 0; -} - static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) { @@ -2591,7 +2575,7 @@ static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value; unsigned int vref = stac92xx_vref_get(codec, nid); - if (vref == stac92xx_get_default_vref(codec, nid)) + if (vref == snd_hda_get_default_vref(codec, nid)) ucontrol->value.enumerated.item[0] = 0; else if (vref == AC_PINCTL_VREF_GRD) ucontrol->value.enumerated.item[0] = 1; @@ -2610,7 +2594,7 @@ static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value; if (ucontrol->value.enumerated.item[0] == 0) - new_vref = stac92xx_get_default_vref(codec, nid); + new_vref = snd_hda_get_default_vref(codec, nid); else if (ucontrol->value.enumerated.item[0] == 1) new_vref = AC_PINCTL_VREF_GRD; else if (ucontrol->value.enumerated.item[0] == 2) @@ -2676,7 +2660,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ else { unsigned int pinctl = AC_PINCTL_IN_EN; if (io_idx) /* set VREF for mic */ - pinctl |= stac92xx_get_default_vref(codec, nid); + pinctl |= snd_hda_get_default_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } @@ -2844,7 +2828,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec, char name[22]; if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) { - if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD + if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD && nid == spec->line_switch) control = STAC_CTL_WIDGET_IO_SWITCH; else if (snd_hda_query_pin_caps(codec, nid) @@ -4351,7 +4335,7 @@ static int stac92xx_init(struct hda_codec *codec) unsigned int pinctl, conf; if (type == AUTO_PIN_MIC) { /* for mic pins, force to initialize */ - pinctl = stac92xx_get_default_vref(codec, nid); + pinctl = snd_hda_get_default_vref(codec, nid); pinctl |= AC_PINCTL_IN_EN; stac92xx_auto_set_pinctl(codec, nid, pinctl); } else { diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 8ee531aeda6..92e11672b91 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -661,10 +661,11 @@ static void via_auto_init_analog_input(struct hda_codec *codec) hda_nid_t nid = cfg->inputs[i].pin; if (spec->smart51_enabled && is_smart51_pins(codec, nid)) ctl = PIN_OUT; - else if (cfg->inputs[i].type == AUTO_PIN_MIC) - ctl = PIN_VREF50; - else + else { ctl = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, nid); + } snd_hda_set_pin_ctl(codec, nid, ctl); } -- cgit v1.2.3 From 6942c103fb4b0ce91be910a716044ac181a9c2dd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 20 Apr 2012 13:08:40 +0200 Subject: ALSA: hda - Skip pin capability sanity check for bogus values Some old codecs like ALC880 seem to give a bogus pin capability value 0 occasionally. This breaks the new sanity check in snd_hda_set_pin_ctl(). Skip the sanity checks in such a case. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8b8b74a1284..408613c13f4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4827,14 +4827,14 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, { if (val) { unsigned int cap = snd_hda_query_pin_caps(codec, pin); - if (val & AC_PINCTL_OUT_EN) { + 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 (val & AC_PINCTL_IN_EN) { + if (cap && (val & AC_PINCTL_IN_EN)) { if (!(cap & AC_PINCAP_IN)) val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN); } -- cgit v1.2.3 From c914f55f7cdfafe9d7d5b248751902c7ab57691e Mon Sep 17 00:00:00 2001 From: Mark Hills Date: Mon, 30 Apr 2012 19:39:22 +0100 Subject: ALSA: echoaudio: Remove incorrect part of assertion This assertion seems to imply that chip->dsp_code_to_load is a pointer. It's actually an integer handle on the actual firmware, and 0 has no special meaning. The assertion prevents initialisation of a Darla20 card, but would also affect other models. It seems it was introduced in commit dd7b254d. ALSA sound/pci/echoaudio/echoaudio.c:2061 Echoaudio driver starting... ALSA sound/pci/echoaudio/echoaudio.c:1969 chip=ebe4e000 ALSA sound/pci/echoaudio/echoaudio.c:2007 pci=ed568000 irq=19 subdev=0010 Init hardware... ALSA sound/pci/echoaudio/darla20_dsp.c:36 init_hw() - Darla20 ------------[ cut here ]------------ WARNING: at sound/pci/echoaudio/echoaudio_dsp.c:478 init_hw+0x1d1/0x86c [snd_darla20]() Hardware name: Dell DM051 BUG? (!chip->dsp_code_to_load || !chip->comm_page) Signed-off-by: Mark Hills Cc: Signed-off-by: Takashi Iwai --- sound/pci/echoaudio/echoaudio_dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 64417a73322..d8c670c9d62 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -475,7 +475,7 @@ static int load_firmware(struct echoaudio *chip) const struct firmware *fw; int box_type, err; - if (snd_BUG_ON(!chip->dsp_code_to_load || !chip->comm_page)) + if (snd_BUG_ON(!chip->comm_page)) return -EPERM; /* See if the ASIC is present and working - only if the DSP is already loaded */ -- cgit v1.2.3 From f5c53d898cc34079373c63a290528963db31d681 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 May 2012 10:07:33 +0200 Subject: ALSA: hda/realtek - Add a fixup for Acer Aspire 5739G Acer Aspire 5739G requires the same fix-up for 4930G to support the surround / bass speakers. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=43180 Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 818f90bc7d5..27d0f637864 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5405,6 +5405,8 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G", ALC882_FIXUP_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210), + SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G", + ALC882_FIXUP_ACER_ASPIRE_4930G), SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE), SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736), -- cgit v1.2.3 From bca40138558f0b39357fd1ca477868e4f52f4b1e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 May 2012 11:13:14 +0200 Subject: ALSA: hda/realtek - Add missing CD-input pin for MSI-7350 mobo Reported-by: Philipp Matthias Hahn Cc: [v3.3+] Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 27d0f637864..8ea613eb73f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5440,6 +5440,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_IMAC91_VREF), SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3", ALC889_FIXUP_CD), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), -- cgit v1.2.3 From 42eb92380f73f28e3a5a51973af1183fdbac82f2 Mon Sep 17 00:00:00 2001 From: Andre Schramm Date: Mon, 7 May 2012 18:52:51 +0200 Subject: ALSA: hdsp - Provide ioctl_compat snd_hdsp uses its own ioctls to acquire config- and status information. Expose the corresponding ioctl handler via ioctl_compat, so that 32bit applications can use it on 64bit kernels. Signed-off-by: Andre Schramm Reviewed-by: Adrian Knoth Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdsp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index b68cdec03b9..0b2aea2ce17 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -5170,6 +5170,7 @@ static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp) strcpy(hw->name, "HDSP hwdep interface"); hw->ops.ioctl = snd_hdsp_hwdep_ioctl; + hw->ops.ioctl_compat = snd_hdsp_hwdep_ioctl; return 0; } -- cgit v1.2.3 From af741c150f66db8d1da6f82ac75e2571f7f1dd38 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 May 2012 18:09:48 +0200 Subject: ALSA: hda/realtek - Call alc_auto_parse_customize_define() always after fixup The call for alc_auto_parse_customize_define() must be done after the fixup pre-probe initialization. Otherwise SKU_IGNORE fixup won't work properly (e.g. HP RP5800 with ALC662 codec). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8ea613eb73f..7810913d07a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5641,13 +5641,13 @@ static int patch_alc262(struct hda_codec *codec) snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); } #endif - alc_auto_parse_customize_define(codec); - alc_fix_pll_init(codec, 0x20, 0x0a, 10); alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + alc_auto_parse_customize_define(codec); + /* automatic parse from the BIOS config */ err = alc262_parse_auto_config(codec); if (err < 0) @@ -6252,8 +6252,6 @@ static int patch_alc269(struct hda_codec *codec) spec->mixer_nid = 0x0b; - alc_auto_parse_customize_define(codec); - err = alc_codec_rename_from_preset(codec); if (err < 0) goto error; @@ -6286,6 +6284,8 @@ static int patch_alc269(struct hda_codec *codec) alc269_fixup_tbl, alc269_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + alc_auto_parse_customize_define(codec); + /* automatic parse from the BIOS config */ err = alc269_parse_auto_config(codec); if (err < 0) @@ -6862,8 +6862,6 @@ static int patch_alc662(struct hda_codec *codec) /* handle multiple HPs as is */ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; - alc_auto_parse_customize_define(codec); - alc_fix_pll_init(codec, 0x20, 0x04, 15); err = alc_codec_rename_from_preset(codec); @@ -6880,6 +6878,9 @@ static int patch_alc662(struct hda_codec *codec) alc_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + /* automatic parse from the BIOS config */ err = alc662_parse_auto_config(codec); if (err < 0) -- cgit v1.2.3 From 619a341b78f17fb86d92e89c04612676cd05e26f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 May 2012 16:30:59 +0200 Subject: Revert "ALSA: hda - Set codec to D3 forcibly even if not used" This reverts commit 785f857d1cb0856b612b46a0545b74aa2596e44a. The commit causes a problem with the wrong D3 state after suspend because the call of hda_set_power_state() involves with the power-up sequence, which changes the power_count, and this confuses the resume sequence that checks the power_count as well. Originally, this go-to-D3 sequence should be a simple task without the power-up sequence. But, it'd need some proper sanity checks in the case of power-saved state, so it's not too easy to write now in the 3.4-rc cycle. In short, the safest option now is to revert this affecting commit. Of course, we need to clean up and robustify the power-saving code better for 3.5 kernel. Reported-by: Konstantin Khlebnikov Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 4 ---- sound/pci/hda/hda_intel.c | 14 +++++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7a8fcc4c15f..841475cc13b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -5444,10 +5444,6 @@ int snd_hda_suspend(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { if (hda_codec_is_power_on(codec)) hda_call_codec_suspend(codec); - else /* forcibly change the power to D3 even if not used */ - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D3); if (codec->patch_ops.post_suspend) codec->patch_ops.post_suspend(codec); } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c19e71a94e1..6e958bf9419 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2351,6 +2351,17 @@ static void azx_power_notify(struct hda_bus *bus) * power management */ +static int snd_hda_codecs_inuse(struct hda_bus *bus) +{ + struct hda_codec *codec; + + list_for_each_entry(codec, &bus->codec_list, list) { + if (snd_hda_codec_needs_resume(codec)) + return 1; + } + return 0; +} + static int azx_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); @@ -2397,7 +2408,8 @@ static int azx_resume(struct pci_dev *pci) return -EIO; azx_init_pci(chip); - azx_init_chip(chip, 1); + if (snd_hda_codecs_inuse(chip->bus)) + azx_init_chip(chip, 1); snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); -- cgit v1.2.3 From 3de9517356389abc2e6c0d32f5ae1ceb218f6b7d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 May 2012 18:03:15 +0200 Subject: ALSA: hda/realtek - Call a common helper for alc_spec initialization Just a clean up by calling the same helper function. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 136 +++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 75 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6dd1b74e9f6..ae9732ce867 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4405,6 +4405,25 @@ static int alc_parse_auto_config(struct hda_codec *codec, return 1; } +/* common preparation job for alc_spec */ +static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) +{ + struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL); + int err; + + if (!spec) + return -ENOMEM; + codec->spec = spec; + spec->mixer_nid = mixer_nid; + + err = alc_codec_rename_from_preset(codec); + if (err < 0) { + kfree(spec); + return err; + } + return 0; +} + static int alc880_parse_auto_config(struct hda_codec *codec) { static const hda_nid_t alc880_ignore[] = { 0x1d, 0 }; @@ -4786,13 +4805,11 @@ static int patch_alc880(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; spec->need_dac_fix = 1; alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, @@ -4979,13 +4996,11 @@ static int patch_alc260(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x07); + if (err < 0) + return err; - spec->mixer_nid = 0x07; + spec = codec->spec; alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -5452,13 +5467,11 @@ static int patch_alc882(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; switch (codec->vendor_id) { case 0x10ec0882: @@ -5470,10 +5483,6 @@ static int patch_alc882(struct hda_codec *codec) break; } - err = alc_codec_rename_from_preset(codec); - if (err < 0) - goto error; - alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, alc882_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -5597,13 +5606,11 @@ static int patch_alc262(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is @@ -5699,13 +5706,12 @@ static int patch_alc268(struct hda_codec *codec) struct alc_spec *spec; int i, has_beep, err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - /* ALC268 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; + + spec = codec->spec; /* automatic parse from the BIOS config */ err = alc268_parse_auto_config(codec); @@ -6216,19 +6222,13 @@ static void alc269_fill_coef(struct hda_codec *codec) static int patch_alc269(struct hda_codec *codec) { struct alc_spec *spec; - int err = 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - - spec->mixer_nid = 0x0b; + int err; - err = alc_codec_rename_from_preset(codec); + err = alc_alloc_spec(codec, 0x0b); if (err < 0) - goto error; + return err; + + spec = codec->spec; if (codec->vendor_id == 0x10ec0269) { spec->codec_variant = ALC269_TYPE_ALC269VA; @@ -6374,13 +6374,11 @@ static int patch_alc861(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x15); + if (err < 0) + return err; - spec->mixer_nid = 0x15; + spec = codec->spec; alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -6477,13 +6475,11 @@ static int patch_alc861vd(struct hda_codec *codec) struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); @@ -6822,25 +6818,19 @@ static const struct alc_model_fixup alc662_fixup_models[] = { static int patch_alc662(struct hda_codec *codec) { struct alc_spec *spec; - int err = 0; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; + int err; - codec->spec = spec; + err = alc_alloc_spec(codec, 0x0b); + if (err < 0) + return err; - spec->mixer_nid = 0x0b; + spec = codec->spec; /* handle multiple HPs as is */ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; alc_fix_pll_init(codec, 0x20, 0x04, 15); - err = alc_codec_rename_from_preset(codec); - if (err < 0) - goto error; - if ((alc_get_coef0(codec) & (1 << 14)) && codec->bus->pci->subsystem_vendor == 0x1025 && spec->cdefine.platform_type == 1) { @@ -6903,16 +6893,12 @@ static int alc680_parse_auto_config(struct hda_codec *codec) */ static int patch_alc680(struct hda_codec *codec) { - struct alc_spec *spec; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - codec->spec = spec; - /* ALC680 has no aa-loopback mixer */ + err = alc_alloc_spec(codec, 0); + if (err < 0) + return err; /* automatic parse from the BIOS config */ err = alc680_parse_auto_config(codec); -- cgit v1.2.3 From c382a9f009d7332e16919154e01f5554eeb095ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 May 2012 15:01:02 +0200 Subject: ALSA: hda - Fix possible access to uninitialized work struct The work struct must be initialized before the possible call in the destructor. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 393a3043a46..98976c8ed10 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1264,13 +1264,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); - if (codec->bus->modelname) { - codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); - if (!codec->modelname) { - snd_hda_codec_free(codec); - return -ENODEV; - } - } #ifdef CONFIG_SND_HDA_POWER_SAVE INIT_DELAYED_WORK(&codec->power_work, hda_power_work); @@ -1281,6 +1274,14 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, hda_keep_power_on(codec); #endif + if (codec->bus->modelname) { + codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL); + if (!codec->modelname) { + snd_hda_codec_free(codec); + return -ENODEV; + } + } + list_add_tail(&codec->list, &bus->codec_list); bus->caddr_tbl[codec_addr] = codec; -- cgit v1.2.3 From 7f30830b7b82e5225c38a48b387e44f3defe40e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 May 2012 16:52:23 +0200 Subject: ALSA: hda - Always resume the codec immediately This is a fix for the problem in commit 785f857d1c, the pop noise issue on some machines with ALC269. The problem was the uninitialized state after the resume due to the delayed resume of the codec chips. In that commit, we tried to fix by forcibly putting the codec to D3 at suspend. But, this still also leaves the uninitialized state after resume, and it _might_ be still problematic with some BIOS. Since the commit turned out to regress another issues, we reverted it in the end. Now, in this fix, try to fix by turning on the codec immediately at the resume path. We need to take care of the power-saving in this case. When the device is woken up at the power-saved state, it should go power-saving again after the resume. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 9 +++++++-- sound/pci/hda/hda_codec.h | 2 -- sound/pci/hda/hda_intel.c | 14 +------------- 3 files changed, 8 insertions(+), 17 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 98976c8ed10..e134e7212cd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3500,6 +3500,10 @@ static void hda_call_codec_suspend(struct hda_codec *codec) */ static void hda_call_codec_resume(struct hda_codec *codec) { + /* set as if powered on for avoiding re-entering the resume + * in the resume / power-save sequence + */ + hda_keep_power_on(codec); hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); @@ -3515,6 +3519,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); } + snd_hda_power_down(codec); /* flag down before returning */ } #endif /* CONFIG_PM */ @@ -4332,6 +4337,7 @@ void snd_hda_power_up(struct hda_codec *codec) snd_hda_update_power_acct(codec); codec->power_on = 1; codec->power_jiffies = jiffies; + codec->power_transition = 1; /* avoid reentrance */ if (bus->ops.pm_notify) bus->ops.pm_notify(bus); hda_call_codec_resume(codec); @@ -5521,8 +5527,7 @@ int snd_hda_resume(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { if (codec->patch_ops.pre_resume) codec->patch_ops.pre_resume(codec); - if (snd_hda_codec_needs_resume(codec)) - hda_call_codec_resume(codec); + hda_call_codec_resume(codec); } return 0; } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 56b4f74c0b1..0fe64912cef 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -1051,12 +1051,10 @@ const char *snd_hda_get_jack_location(u32 cfg); #ifdef CONFIG_SND_HDA_POWER_SAVE void snd_hda_power_up(struct hda_codec *codec); void snd_hda_power_down(struct hda_codec *codec); -#define snd_hda_codec_needs_resume(codec) codec->power_count void snd_hda_update_power_acct(struct hda_codec *codec); #else static inline void snd_hda_power_up(struct hda_codec *codec) {} static inline void snd_hda_power_down(struct hda_codec *codec) {} -#define snd_hda_codec_needs_resume(codec) 1 #endif #ifdef CONFIG_SND_HDA_PATCH_LOADER diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6e958bf9419..c19e71a94e1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2351,17 +2351,6 @@ static void azx_power_notify(struct hda_bus *bus) * power management */ -static int snd_hda_codecs_inuse(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - if (snd_hda_codec_needs_resume(codec)) - return 1; - } - return 0; -} - static int azx_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); @@ -2408,8 +2397,7 @@ static int azx_resume(struct pci_dev *pci) return -EIO; azx_init_pci(chip); - if (snd_hda_codecs_inuse(chip->bus)) - azx_init_chip(chip, 1); + azx_init_chip(chip, 1); snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); -- cgit v1.2.3 From 339876d70a5794c0d5fe09d37827c63148d5017a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 May 2012 16:57:12 +0200 Subject: ALSA: hda - Clear the power-saving states properly at reset Some power-saving states have been left unchanged in snd_hda_codec_reset(), and this is a potential danger because the function may be called in various situations including the continuous operation after that call. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e134e7212cd..8bd34320ef9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2265,6 +2265,9 @@ int snd_hda_codec_reset(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE cancel_delayed_work(&codec->power_work); + codec->power_on = 0; + codec->power_transition = 0; + codec->power_jiffies = jiffies; flush_workqueue(codec->bus->workq); #endif snd_hda_ctls_clear(codec); -- cgit v1.2.3 From 5536c6d69376273d5210ce05b7d5d462f0dff9d6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 May 2012 17:08:10 +0200 Subject: ALSA: hda - Protect the power-saving count with spinlock To avoid some races. Still not perfect, but now a bit safer. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 23 +++++++++++++++++++++-- sound/pci/hda/hda_codec.h | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8bd34320ef9..30a3d8ff978 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1266,6 +1266,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); #ifdef CONFIG_SND_HDA_POWER_SAVE + spin_lock_init(&codec->power_lock); INIT_DELAYED_WORK(&codec->power_work, hda_power_work); /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. * the caller has to power down appropriatley after initialization @@ -4292,12 +4293,16 @@ static void hda_power_work(struct work_struct *work) container_of(work, struct hda_codec, power_work.work); struct hda_bus *bus = codec->bus; + spin_lock(&codec->power_lock); if (!codec->power_on || codec->power_count) { codec->power_transition = 0; + spin_unlock(&codec->power_lock); return; } trace_hda_power_down(codec); + spin_unlock(&codec->power_lock); + hda_call_codec_suspend(codec); if (bus->ops.pm_notify) bus->ops.pm_notify(bus); @@ -4305,9 +4310,11 @@ static void hda_power_work(struct work_struct *work) static void hda_keep_power_on(struct hda_codec *codec) { + spin_lock(&codec->power_lock); codec->power_count++; codec->power_on = 1; codec->power_jiffies = jiffies; + spin_unlock(&codec->power_lock); } /* update the power on/off account with the current jiffies */ @@ -4332,20 +4339,28 @@ void snd_hda_power_up(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; + spin_lock(&codec->power_lock); codec->power_count++; - if (codec->power_on || codec->power_transition) + if (codec->power_on || codec->power_transition) { + spin_unlock(&codec->power_lock); return; + } trace_hda_power_up(codec); snd_hda_update_power_acct(codec); codec->power_on = 1; codec->power_jiffies = jiffies; codec->power_transition = 1; /* avoid reentrance */ + spin_unlock(&codec->power_lock); + if (bus->ops.pm_notify) bus->ops.pm_notify(bus); hda_call_codec_resume(codec); + + spin_lock(&codec->power_lock); cancel_delayed_work(&codec->power_work); codec->power_transition = 0; + spin_unlock(&codec->power_lock); } EXPORT_SYMBOL_HDA(snd_hda_power_up); @@ -4361,14 +4376,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up); */ void snd_hda_power_down(struct hda_codec *codec) { + spin_lock(&codec->power_lock); --codec->power_count; - if (!codec->power_on || codec->power_count || codec->power_transition) + if (!codec->power_on || codec->power_count || codec->power_transition) { + spin_unlock(&codec->power_lock); return; + } if (power_save(codec)) { codec->power_transition = 1; /* avoid reentrance */ queue_delayed_work(codec->bus->workq, &codec->power_work, msecs_to_jiffies(power_save(codec) * 1000)); } + spin_unlock(&codec->power_lock); } EXPORT_SYMBOL_HDA(snd_hda_power_down); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 0fe64912cef..77cd99531aa 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -867,6 +867,7 @@ struct hda_codec { unsigned long power_on_acct; unsigned long power_off_acct; unsigned long power_jiffies; + spinlock_t power_lock; #endif /* codec-specific additional proc output */ -- cgit v1.2.3 From 23d30f28275ddd7fdb87f265e9a440baf4a4cf09 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 May 2012 17:17:32 +0200 Subject: ALSA: hda - Move up the fixup helper functions to the library module Move the fixup helper functions in patch_realtek.c to hda_auto_parser.c so that they can be used in other codec drivers like patch_conexant.c. Signed-off-by: Takashi Iwai --- sound/pci/hda/Makefile | 2 +- sound/pci/hda/hda_auto_parser.c | 163 +++++++++++++++++++++++++++ sound/pci/hda/hda_auto_parser.h | 80 +++++++++++++ sound/pci/hda/patch_conexant.c | 62 ++++------ sound/pci/hda/patch_realtek.c | 244 +++++++--------------------------------- 5 files changed, 309 insertions(+), 242 deletions(-) create mode 100644 sound/pci/hda/hda_auto_parser.c create mode 100644 sound/pci/hda/hda_auto_parser.h (limited to 'sound/pci') diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index ace157cc3d1..bd4149f1aaf 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,6 +1,6 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-y := hda_codec.o hda_jack.o +snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c new file mode 100644 index 00000000000..7cc3a168824 --- /dev/null +++ b/sound/pci/hda/hda_auto_parser.c @@ -0,0 +1,163 @@ +/* + * BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include "hda_codec.h" +#include "hda_auto_parser.h" + +#define SFX "hda_codec: " + +int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, + const struct hda_verb *list) +{ + const struct hda_verb **v; + snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8); + v = snd_array_new(&spec->verbs); + if (!v) + return -ENOMEM; + *v = list; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs); + +void snd_hda_gen_apply_verbs(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + for (i = 0; i < spec->verbs.used; i++) { + struct hda_verb **v = snd_array_elem(&spec->verbs, i); + snd_hda_sequence_write(codec, *v); + } +} +EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs); + +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg) +{ + for (; cfg->nid; cfg++) + snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); +} +EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs); + +void snd_hda_apply_fixup(struct hda_codec *codec, int action) +{ + struct hda_gen_spec *spec = codec->spec; + int id = spec->fixup_id; +#ifdef CONFIG_SND_DEBUG_VERBOSE + const char *modelname = spec->fixup_name; +#endif + int depth = 0; + + if (!spec->fixup_list) + return; + + while (id >= 0) { + const struct hda_fixup *fix = spec->fixup_list + id; + + switch (fix->type) { + case HDA_FIXUP_PINS: + if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply pincfg for %s\n", + codec->chip_name, modelname); + snd_hda_apply_pincfgs(codec, fix->v.pins); + break; + case HDA_FIXUP_VERBS: + if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply fix-verbs for %s\n", + codec->chip_name, modelname); + snd_hda_gen_add_verbs(codec->spec, fix->v.verbs); + break; + case HDA_FIXUP_FUNC: + if (!fix->v.func) + break; + snd_printdd(KERN_INFO SFX + "%s: Apply fix-func for %s\n", + codec->chip_name, modelname); + fix->v.func(codec, fix, action); + break; + default: + snd_printk(KERN_ERR SFX + "%s: Invalid fixup type %d\n", + codec->chip_name, fix->type); + break; + } + if (!fix->chained) + break; + if (++depth > 10) + break; + id = fix->chain_id; + } +} +EXPORT_SYMBOL_HDA(snd_hda_apply_fixup); + +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct snd_pci_quirk *quirk, + const struct hda_fixup *fixlist) +{ + struct hda_gen_spec *spec = codec->spec; + const struct snd_pci_quirk *q; + int id = -1; + const char *name = NULL; + + /* when model=nofixup is given, don't pick up any fixups */ + if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { + spec->fixup_list = NULL; + spec->fixup_id = -1; + return; + } + + if (codec->modelname && models) { + while (models->name) { + if (!strcmp(codec->modelname, models->name)) { + id = models->id; + name = models->name; + break; + } + models++; + } + } + if (id < 0) { + q = snd_pci_quirk_lookup(codec->bus->pci, quirk); + if (q) { + id = q->value; +#ifdef CONFIG_SND_DEBUG_VERBOSE + name = q->name; +#endif + } + } + if (id < 0) { + for (q = quirk; q->subvendor; q++) { + unsigned int vendorid = + q->subdevice | (q->subvendor << 16); + if (vendorid == codec->subsystem_id) { + id = q->value; +#ifdef CONFIG_SND_DEBUG_VERBOSE + name = q->name; +#endif + break; + } + } + } + + spec->fixup_id = id; + if (id >= 0) { + spec->fixup_list = fixlist; + spec->fixup_name = name; + } +} +EXPORT_SYMBOL_HDA(snd_hda_pick_fixup); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h new file mode 100644 index 00000000000..9fe4f5d245a --- /dev/null +++ b/sound/pci/hda/hda_auto_parser.h @@ -0,0 +1,80 @@ +/* + * BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __SOUND_HDA_AUTO_PARSER_H +#define __SOUND_HDA_AUTO_PARSER_H + +struct hda_gen_spec { + /* fix-up list */ + int fixup_id; + const struct hda_fixup *fixup_list; + const char *fixup_name; + + /* additional init verbs */ + struct snd_array verbs; +}; + + +/* + * Fix-up pin default configurations and add default verbs + */ + +struct hda_pintbl { + hda_nid_t nid; + u32 val; +}; + +struct hda_model_fixup { + const int id; + const char *name; +}; + +struct hda_fixup { + int type; + bool chained; + int chain_id; + union { + const struct hda_pintbl *pins; + const struct hda_verb *verbs; + void (*func)(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); + } v; +}; + +/* fixup types */ +enum { + HDA_FIXUP_INVALID, + HDA_FIXUP_PINS, + HDA_FIXUP_VERBS, + HDA_FIXUP_FUNC, +}; + +/* fixup action definitions */ +enum { + HDA_FIXUP_ACT_PRE_PROBE, + HDA_FIXUP_ACT_PROBE, + HDA_FIXUP_ACT_INIT, + HDA_FIXUP_ACT_BUILD, +}; + +int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, + const struct hda_verb *list); +void snd_hda_gen_apply_verbs(struct hda_codec *codec); +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg); +void snd_hda_apply_fixup(struct hda_codec *codec, int action); +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct snd_pci_quirk *quirk, + const struct hda_fixup *fixlist); + +#endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index aabdb9e9a48..2cfcec049ea 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -30,6 +30,7 @@ #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" @@ -66,6 +67,7 @@ struct imux_info { }; struct conexant_spec { + struct hda_gen_spec gen; const struct snd_kcontrol_new *mixers[5]; int num_mixers; @@ -4407,47 +4409,21 @@ static const struct hda_codec_ops cx_auto_patch_ops = { /* * pin fix-up */ -struct cxt_pincfg { - hda_nid_t nid; - u32 val; -}; - -static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); - -} - enum { CXT_PINCFG_LENOVO_X200, CXT_PINCFG_LENOVO_TP410, CXT_FIXUP_STEREO_DMIC, }; -static void apply_fixup(struct hda_codec *codec, - const struct snd_pci_quirk *quirk, - const struct cxt_pincfg **table) +static void cxt_fixup_stereo_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { struct conexant_spec *spec = codec->spec; - - quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (!quirk) - return; - if (table[quirk->value]) { - snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", - quirk->name); - apply_pincfg(codec, table[quirk->value]); - } - if (quirk->value == CXT_FIXUP_STEREO_DMIC) { - snd_printdd(KERN_INFO "hda_codec: applying internal mic workaround for %s\n", - quirk->name); - spec->fixup_stereo_dmic = 1; - } + spec->fixup_stereo_dmic = 1; } /* ThinkPad X200 & co with cxt5051 */ -static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { +static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ { 0x17, 0x21a11000 }, /* dock-mic */ { 0x19, 0x2121103f }, /* dock-HP */ @@ -4456,17 +4432,26 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { }; /* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ -static const struct cxt_pincfg cxt_pincfg_lenovo_tp410[] = { +static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { { 0x19, 0x042110ff }, /* HP (seq# overridden) */ { 0x1a, 0x21a190f0 }, /* dock-mic */ { 0x1c, 0x212140ff }, /* dock-HP */ {} }; -static const struct cxt_pincfg *cxt_pincfg_tbl[] = { - [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, - [CXT_PINCFG_LENOVO_TP410] = cxt_pincfg_lenovo_tp410, - [CXT_FIXUP_STEREO_DMIC] = NULL, +static const struct hda_fixup cxt_fixups[] = { + [CXT_PINCFG_LENOVO_X200] = { + .type = HDA_FIXUP_PINS, + .v.pins = cxt_pincfg_lenovo_x200, + }, + [CXT_PINCFG_LENOVO_TP410] = { + .type = HDA_FIXUP_PINS, + .v.pins = cxt_pincfg_lenovo_tp410, + }, + [CXT_FIXUP_STEREO_DMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_stereo_dmic, + }, }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -4520,13 +4505,16 @@ static int patch_conexant_auto(struct hda_codec *codec) case 0x14f15051: add_cx5051_fake_mutes(codec); codec->pin_amp_workaround = 1; - apply_fixup(codec, cxt5051_fixups, cxt_pincfg_tbl); + snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups); break; default: codec->pin_amp_workaround = 1; - apply_fixup(codec, cxt5066_fixups, cxt_pincfg_tbl); + snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups); + break; } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* Show mute-led control only on HP laptops * This is a sort of white-list: on HP laptops, EAPD corresponds * only to the mute-LED without actualy amp function. Meanwhile, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ae9732ce867..152f458afd2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -32,6 +32,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" @@ -66,8 +67,6 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; -struct alc_fixup; - struct alc_multi_io { hda_nid_t pin; /* multi-io widget pin NID */ hda_nid_t dac; /* DAC to be connected */ @@ -82,19 +81,33 @@ enum { #define MAX_VOL_NIDS 0x40 +/* make compatible with old code */ +#define alc_apply_pincfgs snd_hda_apply_pincfgs +#define alc_apply_fixup snd_hda_apply_fixup +#define alc_pick_fixup snd_hda_pick_fixup +#define alc_fixup hda_fixup +#define alc_pincfg hda_pintbl +#define alc_model_fixup hda_model_fixup + +#define ALC_FIXUP_PINS HDA_FIXUP_PINS +#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS +#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC + +#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE +#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE +#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT +#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD + + struct alc_spec { + struct hda_gen_spec gen; + /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - const struct hda_verb *init_verbs[10]; /* initialization verbs - * don't forget NULL - * termination! - */ - unsigned int num_init_verbs; - char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; const struct hda_pcm_stream *stream_analog_capture; @@ -210,11 +223,6 @@ struct alc_spec { unsigned int pll_coef_idx, pll_coef_bit; unsigned int coef0; - /* fix-up list */ - int fixup_id; - const struct alc_fixup *fixup_list; - const char *fixup_name; - /* multi-io */ int multi_ios; struct alc_multi_io multi_io[4]; @@ -396,13 +404,6 @@ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix) spec->mixers[spec->num_mixers++] = mix; } -static void add_verb(struct alc_spec *spec, const struct hda_verb *verb) -{ - if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs))) - return; - spec->init_verbs[spec->num_init_verbs++] = verb; -} - /* * GPIO setup tables, used in initialization */ @@ -1185,6 +1186,16 @@ static void alc_auto_check_switches(struct hda_codec *codec) */ #define ALC_FIXUP_SKU_IGNORE (2) +static void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cdefine.fixup = 1; + spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; + } +} + static int alc_auto_parse_customize_define(struct hda_codec *codec) { unsigned int ass, tmp, i; @@ -1387,178 +1398,6 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) } } -/* - * Fix-up pin default configurations and add default verbs - */ - -struct alc_pincfg { - hda_nid_t nid; - u32 val; -}; - -struct alc_model_fixup { - const int id; - const char *name; -}; - -struct alc_fixup { - int type; - bool chained; - int chain_id; - union { - unsigned int sku; - const struct alc_pincfg *pins; - const struct hda_verb *verbs; - void (*func)(struct hda_codec *codec, - const struct alc_fixup *fix, - int action); - } v; -}; - -enum { - ALC_FIXUP_INVALID, - ALC_FIXUP_SKU, - ALC_FIXUP_PINS, - ALC_FIXUP_VERBS, - ALC_FIXUP_FUNC, -}; - -enum { - ALC_FIXUP_ACT_PRE_PROBE, - ALC_FIXUP_ACT_PROBE, - ALC_FIXUP_ACT_INIT, - ALC_FIXUP_ACT_BUILD, -}; - -static void alc_apply_pincfgs(struct hda_codec *codec, - const struct alc_pincfg *cfg) -{ - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); -} - -static void alc_apply_fixup(struct hda_codec *codec, int action) -{ - struct alc_spec *spec = codec->spec; - int id = spec->fixup_id; -#ifdef CONFIG_SND_DEBUG_VERBOSE - const char *modelname = spec->fixup_name; -#endif - int depth = 0; - - if (!spec->fixup_list) - return; - - while (id >= 0) { - const struct alc_fixup *fix = spec->fixup_list + id; - const struct alc_pincfg *cfg; - - switch (fix->type) { - case ALC_FIXUP_SKU: - if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply sku override for %s\n", - codec->chip_name, modelname); - spec->cdefine.sku_cfg = fix->v.sku; - spec->cdefine.fixup = 1; - break; - case ALC_FIXUP_PINS: - cfg = fix->v.pins; - if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply pincfg for %s\n", - codec->chip_name, modelname); - alc_apply_pincfgs(codec, cfg); - break; - case ALC_FIXUP_VERBS: - if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply fix-verbs for %s\n", - codec->chip_name, modelname); - add_verb(codec->spec, fix->v.verbs); - break; - case ALC_FIXUP_FUNC: - if (!fix->v.func) - break; - snd_printdd(KERN_INFO "hda_codec: %s: " - "Apply fix-func for %s\n", - codec->chip_name, modelname); - fix->v.func(codec, fix, action); - break; - default: - snd_printk(KERN_ERR "hda_codec: %s: " - "Invalid fixup type %d\n", - codec->chip_name, fix->type); - break; - } - if (!fix->chained) - break; - if (++depth > 10) - break; - id = fix->chain_id; - } -} - -static void alc_pick_fixup(struct hda_codec *codec, - const struct alc_model_fixup *models, - const struct snd_pci_quirk *quirk, - const struct alc_fixup *fixlist) -{ - struct alc_spec *spec = codec->spec; - const struct snd_pci_quirk *q; - int id = -1; - const char *name = NULL; - - /* when model=nofixup is given, don't pick up any fixups */ - if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { - spec->fixup_list = NULL; - spec->fixup_id = -1; - return; - } - - if (codec->modelname && models) { - while (models->name) { - if (!strcmp(codec->modelname, models->name)) { - id = models->id; - name = models->name; - break; - } - models++; - } - } - if (id < 0) { - q = snd_pci_quirk_lookup(codec->bus->pci, quirk); - if (q) { - id = q->value; -#ifdef CONFIG_SND_DEBUG_VERBOSE - name = q->name; -#endif - } - } - if (id < 0) { - for (q = quirk; q->subvendor; q++) { - unsigned int vendorid = - q->subdevice | (q->subvendor << 16); - if (vendorid == codec->subsystem_id) { - id = q->value; -#ifdef CONFIG_SND_DEBUG_VERBOSE - name = q->name; -#endif - break; - } - } - } - - spec->fixup_id = id; - if (id >= 0) { - spec->fixup_list = fixlist; - spec->fixup_name = name; - } -} - /* * COEF access helper functions */ @@ -2050,7 +1889,6 @@ static void alc_auto_init_std(struct hda_codec *codec); static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int i; if (spec->init_hook) spec->init_hook(codec); @@ -2058,8 +1896,6 @@ static int alc_init(struct hda_codec *codec) alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp); - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); alc_init_special_input_src(codec); alc_auto_init_std(codec); @@ -4885,7 +4721,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT); spec->unsol_event = alc_sku_unsol_event; - add_verb(codec->spec, alc_gpio1_init_verbs); + snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs); } } @@ -5216,8 +5052,8 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, }, [ALC882_FIXUP_ASUS_W90V] = { .type = ALC_FIXUP_PINS, @@ -5693,7 +5529,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (err > 0) { if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); - add_verb(spec, alc268_beep_init_verbs); + snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs); } } return err; @@ -5995,8 +5831,8 @@ static const struct alc_fixup alc269_fixups[] = { } }, [ALC269_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, }, [ALC269_FIXUP_ASUS_G73JW] = { .type = ALC_FIXUP_PINS, @@ -6491,7 +6327,7 @@ static int patch_alc861vd(struct hda_codec *codec) if (codec->vendor_id == 0x10ec0660) { /* always turn on EAPD */ - add_verb(spec, alc660vd_eapd_verbs); + snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs); } if (!spec->no_analog) { @@ -6604,8 +6440,8 @@ static const struct alc_fixup alc662_fixups[] = { } }, [ALC662_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_SKU, - .v.sku = ALC_FIXUP_SKU_IGNORE, + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_sku_ignore, }, [ALC662_FIXUP_HP_RP5800] = { .type = ALC_FIXUP_PINS, -- cgit v1.2.3 From 128bc4ba8c52194eae5a80594ec9b7f6b35ba88f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 May 2012 17:42:31 +0200 Subject: ALSA: hda - Move BIOS pin-parser code to hda_auto_parser.c Just code shuffles. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 597 ++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_auto_parser.h | 80 ++++++ sound/pci/hda/hda_codec.c | 596 --------------------------------------- sound/pci/hda/hda_jack.c | 1 + sound/pci/hda/hda_jack.h | 2 + sound/pci/hda/hda_local.h | 82 +----- sound/pci/hda/patch_analog.c | 1 + sound/pci/hda/patch_ca0110.c | 1 + sound/pci/hda/patch_ca0132.c | 1 + sound/pci/hda/patch_cirrus.c | 1 + sound/pci/hda/patch_cmedia.c | 1 + sound/pci/hda/patch_sigmatel.c | 1 + sound/pci/hda/patch_via.c | 1 + 13 files changed, 690 insertions(+), 675 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7cc3a168824..6e9ef3e2509 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -13,10 +13,607 @@ #include #include #include "hda_codec.h" +#include "hda_local.h" #include "hda_auto_parser.h" #define SFX "hda_codec: " +/* + * Helper for automatic pin configuration + */ + +static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list) +{ + for (; *list; list++) + if (*list == nid) + return 1; + return 0; +} + + +/* + * Sort an associated group of pins according to their sequence numbers. + */ +static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, + int num_pins) +{ + int i, j; + short seq; + hda_nid_t nid; + + for (i = 0; i < num_pins; i++) { + for (j = i + 1; j < num_pins; j++) { + if (sequences[i] > sequences[j]) { + seq = sequences[i]; + sequences[i] = sequences[j]; + sequences[j] = seq; + nid = pins[i]; + pins[i] = pins[j]; + pins[j] = nid; + } + } + } +} + + +/* add the found input-pin to the cfg->inputs[] table */ +static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, + int type) +{ + if (cfg->num_inputs < AUTO_CFG_MAX_INS) { + cfg->inputs[cfg->num_inputs].pin = nid; + cfg->inputs[cfg->num_inputs].type = type; + cfg->num_inputs++; + } +} + +/* sort inputs in the order of AUTO_PIN_* type */ +static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg) +{ + int i, j; + + for (i = 0; i < cfg->num_inputs; i++) { + for (j = i + 1; j < cfg->num_inputs; j++) { + if (cfg->inputs[i].type > cfg->inputs[j].type) { + struct auto_pin_cfg_item tmp; + tmp = cfg->inputs[i]; + cfg->inputs[i] = cfg->inputs[j]; + cfg->inputs[j] = tmp; + } + } + } +} + +/* Reorder the surround channels + * ALSA sequence is front/surr/clfe/side + * HDA sequence is: + * 4-ch: front/surr => OK as it is + * 6-ch: front/clfe/surr + * 8-ch: front/clfe/rear/side|fc + */ +static void reorder_outputs(unsigned int nums, hda_nid_t *pins) +{ + hda_nid_t nid; + + switch (nums) { + case 3: + case 4: + nid = pins[1]; + pins[1] = pins[2]; + pins[2] = nid; + break; + } +} + +/* + * Parse all pin widgets and store the useful pin nids to cfg + * + * The number of line-outs or any primary output is stored in line_outs, + * and the corresponding output pins are assigned to line_out_pins[], + * in the order of front, rear, CLFE, side, ... + * + * If more extra outputs (speaker and headphone) are found, the pins are + * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack + * is detected, one of speaker of HP pins is assigned as the primary + * output, i.e. to line_out_pins[0]. So, line_outs is always positive + * if any analog output exists. + * + * The analog input pins are assigned to inputs array. + * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, + * respectively. + */ +int snd_hda_parse_pin_defcfg(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + const hda_nid_t *ignore_nids, + unsigned int cond_flags) +{ + hda_nid_t nid, end_nid; + short seq, assoc_line_out; + short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; + short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; + short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; + int i; + + memset(cfg, 0, sizeof(*cfg)); + + memset(sequences_line_out, 0, sizeof(sequences_line_out)); + memset(sequences_speaker, 0, sizeof(sequences_speaker)); + memset(sequences_hp, 0, sizeof(sequences_hp)); + assoc_line_out = 0; + + codec->ignore_misc_bit = true; + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wid_caps = get_wcaps(codec, nid); + unsigned int wid_type = get_wcaps_type(wid_caps); + unsigned int def_conf; + short assoc, loc, conn, dev; + + /* read all default configuration for pin complex */ + if (wid_type != AC_WID_PIN) + continue; + /* ignore the given nids (e.g. pc-beep returns error) */ + if (ignore_nids && is_in_nid_list(nid, ignore_nids)) + continue; + + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & + AC_DEFCFG_MISC_NO_PRESENCE)) + codec->ignore_misc_bit = false; + conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) + continue; + loc = get_defcfg_location(def_conf); + dev = get_defcfg_device(def_conf); + + /* workaround for buggy BIOS setups */ + if (dev == AC_JACK_LINE_OUT) { + if (conn == AC_JACK_PORT_FIXED) + dev = AC_JACK_SPEAKER; + } + + switch (dev) { + case AC_JACK_LINE_OUT: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); + + if (!(wid_caps & AC_WCAP_STEREO)) + if (!cfg->mono_out_pin) + cfg->mono_out_pin = nid; + if (!assoc) + continue; + if (!assoc_line_out) + assoc_line_out = assoc; + else if (assoc_line_out != assoc) + continue; + if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) + continue; + cfg->line_out_pins[cfg->line_outs] = nid; + sequences_line_out[cfg->line_outs] = seq; + cfg->line_outs++; + break; + case AC_JACK_SPEAKER: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); + if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) + continue; + cfg->speaker_pins[cfg->speaker_outs] = nid; + sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq; + cfg->speaker_outs++; + break; + case AC_JACK_HP_OUT: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); + if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) + continue; + cfg->hp_pins[cfg->hp_outs] = nid; + sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; + cfg->hp_outs++; + break; + case AC_JACK_MIC_IN: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC); + break; + case AC_JACK_LINE_IN: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN); + break; + case AC_JACK_CD: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); + break; + case AC_JACK_AUX: + add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); + break; + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) + continue; + cfg->dig_out_pins[cfg->dig_outs] = nid; + cfg->dig_out_type[cfg->dig_outs] = + (loc == AC_JACK_LOC_HDMI) ? + HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; + cfg->dig_outs++; + break; + case AC_JACK_SPDIF_IN: + case AC_JACK_DIG_OTHER_IN: + cfg->dig_in_pin = nid; + if (loc == AC_JACK_LOC_HDMI) + cfg->dig_in_type = HDA_PCM_TYPE_HDMI; + else + cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; + break; + } + } + + /* FIX-UP: + * If no line-out is defined but multiple HPs are found, + * some of them might be the real line-outs. + */ + if (!cfg->line_outs && cfg->hp_outs > 1 && + !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { + int i = 0; + while (i < cfg->hp_outs) { + /* The real HPs should have the sequence 0x0f */ + if ((sequences_hp[i] & 0x0f) == 0x0f) { + i++; + continue; + } + /* Move it to the line-out table */ + cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; + sequences_line_out[cfg->line_outs] = sequences_hp[i]; + cfg->line_outs++; + cfg->hp_outs--; + memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, + sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); + memmove(sequences_hp + i, sequences_hp + i + 1, + sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); + } + memset(cfg->hp_pins + cfg->hp_outs, 0, + sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); + if (!cfg->hp_outs) + cfg->line_out_type = AUTO_PIN_HP_OUT; + + } + + /* sort by sequence */ + sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, + cfg->line_outs); + sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, + cfg->speaker_outs); + sort_pins_by_sequence(cfg->hp_pins, sequences_hp, + cfg->hp_outs); + + /* + * FIX-UP: if no line-outs are detected, try to use speaker or HP pin + * as a primary output + */ + if (!cfg->line_outs && + !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) { + if (cfg->speaker_outs) { + cfg->line_outs = cfg->speaker_outs; + memcpy(cfg->line_out_pins, cfg->speaker_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = 0; + memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); + cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; + } else if (cfg->hp_outs) { + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + } + } + + reorder_outputs(cfg->line_outs, cfg->line_out_pins); + reorder_outputs(cfg->hp_outs, cfg->hp_pins); + reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); + + sort_autocfg_input_pins(cfg); + + /* + * debug prints of the parsed results + */ + snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", + cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[3], + cfg->line_out_pins[4], + cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : + (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? + "speaker" : "line")); + snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + cfg->speaker_outs, cfg->speaker_pins[0], + cfg->speaker_pins[1], cfg->speaker_pins[2], + cfg->speaker_pins[3], cfg->speaker_pins[4]); + snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", + cfg->hp_outs, cfg->hp_pins[0], + cfg->hp_pins[1], cfg->hp_pins[2], + cfg->hp_pins[3], cfg->hp_pins[4]); + snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); + if (cfg->dig_outs) + snd_printd(" dig-out=0x%x/0x%x\n", + cfg->dig_out_pins[0], cfg->dig_out_pins[1]); + snd_printd(" inputs:"); + for (i = 0; i < cfg->num_inputs; i++) { + snd_printd(" %s=0x%x", + hda_get_autocfg_input_label(codec, cfg, i), + cfg->inputs[i].pin); + } + snd_printd("\n"); + if (cfg->dig_in_pin) + snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); + + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg); + +int snd_hda_get_input_pin_attr(unsigned int def_conf) +{ + unsigned int loc = get_defcfg_location(def_conf); + unsigned int conn = get_defcfg_connect(def_conf); + if (conn == AC_JACK_PORT_NONE) + return INPUT_PIN_ATTR_UNUSED; + /* Windows may claim the internal mic to be BOTH, too */ + if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) + return INPUT_PIN_ATTR_INT; + if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) + return INPUT_PIN_ATTR_INT; + if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) + return INPUT_PIN_ATTR_DOCK; + if (loc == AC_JACK_LOC_REAR) + return INPUT_PIN_ATTR_REAR; + if (loc == AC_JACK_LOC_FRONT) + return INPUT_PIN_ATTR_FRONT; + return INPUT_PIN_ATTR_NORMAL; +} +EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); + +/** + * hda_get_input_pin_label - Give a label for the given input pin + * + * When check_location is true, the function checks the pin location + * for mic and line-in pins, and set an appropriate prefix like "Front", + * "Rear", "Internal". + */ + +static const char *hda_get_input_pin_label(struct hda_codec *codec, + hda_nid_t pin, bool check_location) +{ + unsigned int def_conf; + static const char * const mic_names[] = { + "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", + }; + int attr; + + def_conf = snd_hda_codec_get_pincfg(codec, pin); + + switch (get_defcfg_device(def_conf)) { + case AC_JACK_MIC_IN: + if (!check_location) + return "Mic"; + attr = snd_hda_get_input_pin_attr(def_conf); + if (!attr) + return "None"; + return mic_names[attr - 1]; + case AC_JACK_LINE_IN: + if (!check_location) + return "Line"; + attr = snd_hda_get_input_pin_attr(def_conf); + if (!attr) + return "None"; + if (attr == INPUT_PIN_ATTR_DOCK) + return "Dock Line"; + return "Line"; + case AC_JACK_AUX: + return "Aux"; + case AC_JACK_CD: + return "CD"; + case AC_JACK_SPDIF_IN: + return "SPDIF In"; + case AC_JACK_DIG_OTHER_IN: + return "Digital In"; + default: + return "Misc"; + } +} + +/* Check whether the location prefix needs to be added to the label. + * If all mic-jacks are in the same location (e.g. rear panel), we don't + * have to put "Front" prefix to each label. In such a case, returns false. + */ +static int check_mic_location_need(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input) +{ + unsigned int defc; + int i, attr, attr2; + + defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); + attr = snd_hda_get_input_pin_attr(defc); + /* for internal or docking mics, we need locations */ + if (attr <= INPUT_PIN_ATTR_NORMAL) + return 1; + + attr = 0; + for (i = 0; i < cfg->num_inputs; i++) { + defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); + attr2 = snd_hda_get_input_pin_attr(defc); + if (attr2 >= INPUT_PIN_ATTR_NORMAL) { + if (attr && attr != attr2) + return 1; /* different locations found */ + attr = attr2; + } + } + return 0; +} + +/** + * hda_get_autocfg_input_label - Get a label for the given input + * + * Get a label for the given input pin defined by the autocfg item. + * Unlike hda_get_input_pin_label(), this function checks all inputs + * defined in autocfg and avoids the redundant mic/line prefix as much as + * possible. + */ +const char *hda_get_autocfg_input_label(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input) +{ + int type = cfg->inputs[input].type; + int has_multiple_pins = 0; + + if ((input > 0 && cfg->inputs[input - 1].type == type) || + (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) + has_multiple_pins = 1; + if (has_multiple_pins && type == AUTO_PIN_MIC) + has_multiple_pins &= check_mic_location_need(codec, cfg, input); + return hda_get_input_pin_label(codec, cfg->inputs[input].pin, + has_multiple_pins); +} +EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); + +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + +/* get a unique suffix or an index number */ +static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, + int num_pins, int *indexp) +{ + static const char * const channel_sfx[] = { + " Front", " Surround", " CLFE", " Side" + }; + int i; + + i = find_idx_in_nid_list(nid, pins, num_pins); + if (i < 0) + return NULL; + if (num_pins == 1) + return ""; + if (num_pins > ARRAY_SIZE(channel_sfx)) { + if (indexp) + *indexp = i; + return ""; + } + return channel_sfx[i]; +} + +static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + const char *name, char *label, int maxlen, + int *indexp) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int attr = snd_hda_get_input_pin_attr(def_conf); + const char *pfx = "", *sfx = ""; + + /* handle as a speaker if it's a fixed line-out */ + if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) + name = "Speaker"; + /* check the location */ + switch (attr) { + case INPUT_PIN_ATTR_DOCK: + pfx = "Dock "; + break; + case INPUT_PIN_ATTR_FRONT: + pfx = "Front "; + break; + } + if (cfg) { + /* try to give a unique suffix if needed */ + sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, + indexp); + if (!sfx) + sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, + indexp); + if (!sfx) { + /* don't add channel suffix for Headphone controls */ + int idx = find_idx_in_nid_list(nid, cfg->hp_pins, + cfg->hp_outs); + if (idx >= 0) + *indexp = idx; + sfx = ""; + } + } + snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); + return 1; +} + +/** + * snd_hda_get_pin_label - Get a label for the given I/O pin + * + * Get a label for the given pin. This function works for both input and + * output pins. When @cfg is given as non-NULL, the function tries to get + * an optimized label using hda_get_autocfg_input_label(). + * + * This function tries to give a unique label string for the pin as much as + * possible. For example, when the multiple line-outs are present, it adds + * the channel suffix like "Front", "Surround", etc (only when @cfg is given). + * If no unique name with a suffix is available and @indexp is non-NULL, the + * index number is stored in the pointer. + */ +int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + char *label, int maxlen, int *indexp) +{ + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + const char *name = NULL; + int i; + + if (indexp) + *indexp = 0; + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) + return 0; + + switch (get_defcfg_device(def_conf)) { + case AC_JACK_LINE_OUT: + return fill_audio_out_name(codec, nid, cfg, "Line Out", + label, maxlen, indexp); + case AC_JACK_SPEAKER: + return fill_audio_out_name(codec, nid, cfg, "Speaker", + label, maxlen, indexp); + case AC_JACK_HP_OUT: + return fill_audio_out_name(codec, nid, cfg, "Headphone", + label, maxlen, indexp); + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) + name = "HDMI"; + else + name = "SPDIF"; + if (cfg && indexp) { + i = find_idx_in_nid_list(nid, cfg->dig_out_pins, + cfg->dig_outs); + if (i >= 0) + *indexp = i; + } + break; + default: + if (cfg) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].pin != nid) + continue; + name = hda_get_autocfg_input_label(codec, cfg, i); + if (name) + break; + } + } + if (!name) + name = hda_get_input_pin_label(codec, nid, true); + break; + } + if (!name) + return 0; + strlcpy(label, name, maxlen); + return 1; +} +EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); + int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, const struct hda_verb *list) { diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index 9fe4f5d245a..2a7889dfbd1 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -12,6 +12,86 @@ #ifndef __SOUND_HDA_AUTO_PARSER_H #define __SOUND_HDA_AUTO_PARSER_H +/* + * Helper for automatic pin configuration + */ + +enum { + AUTO_PIN_MIC, + AUTO_PIN_LINE_IN, + AUTO_PIN_CD, + AUTO_PIN_AUX, + AUTO_PIN_LAST +}; + +enum { + AUTO_PIN_LINE_OUT, + AUTO_PIN_SPEAKER_OUT, + AUTO_PIN_HP_OUT +}; + +#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS +#define AUTO_CFG_MAX_INS 8 + +struct auto_pin_cfg_item { + hda_nid_t pin; + int type; +}; + +struct auto_pin_cfg; +const char *hda_get_autocfg_input_label(struct hda_codec *codec, + const struct auto_pin_cfg *cfg, + int input); +int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, + const struct auto_pin_cfg *cfg, + char *label, int maxlen, int *indexp); + +enum { + INPUT_PIN_ATTR_UNUSED, /* pin not connected */ + INPUT_PIN_ATTR_INT, /* internal mic/line-in */ + INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ + INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ + INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ + INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ +}; + +int snd_hda_get_input_pin_attr(unsigned int def_conf); + +struct auto_pin_cfg { + int line_outs; + /* sorted in the order of Front/Surr/CLFE/Side */ + hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS]; + int speaker_outs; + hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS]; + int hp_outs; + int line_out_type; /* AUTO_PIN_XXX_OUT */ + hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; + int num_inputs; + struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; + int dig_outs; + hda_nid_t dig_out_pins[2]; + hda_nid_t dig_in_pin; + hda_nid_t mono_out_pin; + int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ + int dig_in_type; /* HDA_PCM_TYPE_XXX */ +}; + +/* bit-flags for snd_hda_parse_pin_def_config() behavior */ +#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ +#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ + +int snd_hda_parse_pin_defcfg(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + const hda_nid_t *ignore_nids, + unsigned int cond_flags); + +/* older function */ +#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ + snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) + +/* + */ + struct hda_gen_spec { /* fix-up list */ int fixup_id; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 30a3d8ff978..f2bdf38be39 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4877,602 +4877,6 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, } EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl); -/* - * Helper for automatic pin configuration - */ - -static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list) -{ - for (; *list; list++) - if (*list == nid) - return 1; - return 0; -} - - -/* - * Sort an associated group of pins according to their sequence numbers. - */ -static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences, - int num_pins) -{ - int i, j; - short seq; - hda_nid_t nid; - - for (i = 0; i < num_pins; i++) { - for (j = i + 1; j < num_pins; j++) { - if (sequences[i] > sequences[j]) { - seq = sequences[i]; - sequences[i] = sequences[j]; - sequences[j] = seq; - nid = pins[i]; - pins[i] = pins[j]; - pins[j] = nid; - } - } - } -} - - -/* add the found input-pin to the cfg->inputs[] table */ -static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid, - int type) -{ - if (cfg->num_inputs < AUTO_CFG_MAX_INS) { - cfg->inputs[cfg->num_inputs].pin = nid; - cfg->inputs[cfg->num_inputs].type = type; - cfg->num_inputs++; - } -} - -/* sort inputs in the order of AUTO_PIN_* type */ -static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg) -{ - int i, j; - - for (i = 0; i < cfg->num_inputs; i++) { - for (j = i + 1; j < cfg->num_inputs; j++) { - if (cfg->inputs[i].type > cfg->inputs[j].type) { - struct auto_pin_cfg_item tmp; - tmp = cfg->inputs[i]; - cfg->inputs[i] = cfg->inputs[j]; - cfg->inputs[j] = tmp; - } - } - } -} - -/* Reorder the surround channels - * ALSA sequence is front/surr/clfe/side - * HDA sequence is: - * 4-ch: front/surr => OK as it is - * 6-ch: front/clfe/surr - * 8-ch: front/clfe/rear/side|fc - */ -static void reorder_outputs(unsigned int nums, hda_nid_t *pins) -{ - hda_nid_t nid; - - switch (nums) { - case 3: - case 4: - nid = pins[1]; - pins[1] = pins[2]; - pins[2] = nid; - break; - } -} - -/* - * Parse all pin widgets and store the useful pin nids to cfg - * - * The number of line-outs or any primary output is stored in line_outs, - * and the corresponding output pins are assigned to line_out_pins[], - * in the order of front, rear, CLFE, side, ... - * - * If more extra outputs (speaker and headphone) are found, the pins are - * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack - * is detected, one of speaker of HP pins is assigned as the primary - * output, i.e. to line_out_pins[0]. So, line_outs is always positive - * if any analog output exists. - * - * The analog input pins are assigned to inputs array. - * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, - * respectively. - */ -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags) -{ - hda_nid_t nid, end_nid; - short seq, assoc_line_out; - short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; - short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; - short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; - int i; - - memset(cfg, 0, sizeof(*cfg)); - - memset(sequences_line_out, 0, sizeof(sequences_line_out)); - memset(sequences_speaker, 0, sizeof(sequences_speaker)); - memset(sequences_hp, 0, sizeof(sequences_hp)); - assoc_line_out = 0; - - codec->ignore_misc_bit = true; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { - unsigned int wid_caps = get_wcaps(codec, nid); - unsigned int wid_type = get_wcaps_type(wid_caps); - unsigned int def_conf; - short assoc, loc, conn, dev; - - /* read all default configuration for pin complex */ - if (wid_type != AC_WID_PIN) - continue; - /* ignore the given nids (e.g. pc-beep returns error) */ - if (ignore_nids && is_in_nid_list(nid, ignore_nids)) - continue; - - def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - codec->ignore_misc_bit = false; - conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - continue; - loc = get_defcfg_location(def_conf); - dev = get_defcfg_device(def_conf); - - /* workaround for buggy BIOS setups */ - if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED) - dev = AC_JACK_SPEAKER; - } - - switch (dev) { - case AC_JACK_LINE_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - - if (!(wid_caps & AC_WCAP_STEREO)) - if (!cfg->mono_out_pin) - cfg->mono_out_pin = nid; - if (!assoc) - continue; - if (!assoc_line_out) - assoc_line_out = assoc; - else if (assoc_line_out != assoc) - continue; - if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) - continue; - cfg->line_out_pins[cfg->line_outs] = nid; - sequences_line_out[cfg->line_outs] = seq; - cfg->line_outs++; - break; - case AC_JACK_SPEAKER: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) - continue; - cfg->speaker_pins[cfg->speaker_outs] = nid; - sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq; - cfg->speaker_outs++; - break; - case AC_JACK_HP_OUT: - seq = get_defcfg_sequence(def_conf); - assoc = get_defcfg_association(def_conf); - if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) - continue; - cfg->hp_pins[cfg->hp_outs] = nid; - sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; - cfg->hp_outs++; - break; - case AC_JACK_MIC_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC); - break; - case AC_JACK_LINE_IN: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN); - break; - case AC_JACK_CD: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD); - break; - case AC_JACK_AUX: - add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX); - break; - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) - continue; - cfg->dig_out_pins[cfg->dig_outs] = nid; - cfg->dig_out_type[cfg->dig_outs] = - (loc == AC_JACK_LOC_HDMI) ? - HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF; - cfg->dig_outs++; - break; - case AC_JACK_SPDIF_IN: - case AC_JACK_DIG_OTHER_IN: - cfg->dig_in_pin = nid; - if (loc == AC_JACK_LOC_HDMI) - cfg->dig_in_type = HDA_PCM_TYPE_HDMI; - else - cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; - break; - } - } - - /* FIX-UP: - * If no line-out is defined but multiple HPs are found, - * some of them might be the real line-outs. - */ - if (!cfg->line_outs && cfg->hp_outs > 1 && - !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { - int i = 0; - while (i < cfg->hp_outs) { - /* The real HPs should have the sequence 0x0f */ - if ((sequences_hp[i] & 0x0f) == 0x0f) { - i++; - continue; - } - /* Move it to the line-out table */ - cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i]; - sequences_line_out[cfg->line_outs] = sequences_hp[i]; - cfg->line_outs++; - cfg->hp_outs--; - memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1, - sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i)); - memmove(sequences_hp + i, sequences_hp + i + 1, - sizeof(sequences_hp[0]) * (cfg->hp_outs - i)); - } - memset(cfg->hp_pins + cfg->hp_outs, 0, - sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs)); - if (!cfg->hp_outs) - cfg->line_out_type = AUTO_PIN_HP_OUT; - - } - - /* sort by sequence */ - sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out, - cfg->line_outs); - sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, - cfg->speaker_outs); - sort_pins_by_sequence(cfg->hp_pins, sequences_hp, - cfg->hp_outs); - - /* - * FIX-UP: if no line-outs are detected, try to use speaker or HP pin - * as a primary output - */ - if (!cfg->line_outs && - !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) { - if (cfg->speaker_outs) { - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - } else if (cfg->hp_outs) { - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - } - - reorder_outputs(cfg->line_outs, cfg->line_out_pins); - reorder_outputs(cfg->hp_outs, cfg->hp_pins); - reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); - - sort_autocfg_input_pins(cfg); - - /* - * debug prints of the parsed results - */ - snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[3], - cfg->line_out_pins[4], - cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : - (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ? - "speaker" : "line")); - snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->speaker_outs, cfg->speaker_pins[0], - cfg->speaker_pins[1], cfg->speaker_pins[2], - cfg->speaker_pins[3], cfg->speaker_pins[4]); - snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n", - cfg->hp_outs, cfg->hp_pins[0], - cfg->hp_pins[1], cfg->hp_pins[2], - cfg->hp_pins[3], cfg->hp_pins[4]); - snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); - if (cfg->dig_outs) - snd_printd(" dig-out=0x%x/0x%x\n", - cfg->dig_out_pins[0], cfg->dig_out_pins[1]); - snd_printd(" inputs:"); - for (i = 0; i < cfg->num_inputs; i++) { - snd_printd(" %s=0x%x", - hda_get_autocfg_input_label(codec, cfg, i), - cfg->inputs[i].pin); - } - snd_printd("\n"); - if (cfg->dig_in_pin) - snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin); - - return 0; -} -EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg); - -int snd_hda_get_input_pin_attr(unsigned int def_conf) -{ - unsigned int loc = get_defcfg_location(def_conf); - unsigned int conn = get_defcfg_connect(def_conf); - if (conn == AC_JACK_PORT_NONE) - return INPUT_PIN_ATTR_UNUSED; - /* Windows may claim the internal mic to be BOTH, too */ - if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_INTERNAL) - return INPUT_PIN_ATTR_INT; - if ((loc & 0x30) == AC_JACK_LOC_SEPARATE) - return INPUT_PIN_ATTR_DOCK; - if (loc == AC_JACK_LOC_REAR) - return INPUT_PIN_ATTR_REAR; - if (loc == AC_JACK_LOC_FRONT) - return INPUT_PIN_ATTR_FRONT; - return INPUT_PIN_ATTR_NORMAL; -} -EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr); - -/** - * hda_get_input_pin_label - Give a label for the given input pin - * - * When check_location is true, the function checks the pin location - * for mic and line-in pins, and set an appropriate prefix like "Front", - * "Rear", "Internal". - */ - -static const char *hda_get_input_pin_label(struct hda_codec *codec, - hda_nid_t pin, bool check_location) -{ - unsigned int def_conf; - static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", - }; - int attr; - - def_conf = snd_hda_codec_get_pincfg(codec, pin); - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_MIC_IN: - if (!check_location) - return "Mic"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - return mic_names[attr - 1]; - case AC_JACK_LINE_IN: - if (!check_location) - return "Line"; - attr = snd_hda_get_input_pin_attr(def_conf); - if (!attr) - return "None"; - if (attr == INPUT_PIN_ATTR_DOCK) - return "Dock Line"; - return "Line"; - case AC_JACK_AUX: - return "Aux"; - case AC_JACK_CD: - return "CD"; - case AC_JACK_SPDIF_IN: - return "SPDIF In"; - case AC_JACK_DIG_OTHER_IN: - return "Digital In"; - default: - return "Misc"; - } -} - -/* Check whether the location prefix needs to be added to the label. - * If all mic-jacks are in the same location (e.g. rear panel), we don't - * have to put "Front" prefix to each label. In such a case, returns false. - */ -static int check_mic_location_need(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - unsigned int defc; - int i, attr, attr2; - - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin); - attr = snd_hda_get_input_pin_attr(defc); - /* for internal or docking mics, we need locations */ - if (attr <= INPUT_PIN_ATTR_NORMAL) - return 1; - - attr = 0; - for (i = 0; i < cfg->num_inputs; i++) { - defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin); - attr2 = snd_hda_get_input_pin_attr(defc); - if (attr2 >= INPUT_PIN_ATTR_NORMAL) { - if (attr && attr != attr2) - return 1; /* different locations found */ - attr = attr2; - } - } - return 0; -} - -/** - * hda_get_autocfg_input_label - Get a label for the given input - * - * Get a label for the given input pin defined by the autocfg item. - * Unlike hda_get_input_pin_label(), this function checks all inputs - * defined in autocfg and avoids the redundant mic/line prefix as much as - * possible. - */ -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input) -{ - int type = cfg->inputs[input].type; - int has_multiple_pins = 0; - - if ((input > 0 && cfg->inputs[input - 1].type == type) || - (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type)) - has_multiple_pins = 1; - if (has_multiple_pins && type == AUTO_PIN_MIC) - has_multiple_pins &= check_mic_location_need(codec, cfg, input); - return hda_get_input_pin_label(codec, cfg->inputs[input].pin, - has_multiple_pins); -} -EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label); - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* get a unique suffix or an index number */ -static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, - int num_pins, int *indexp) -{ - static const char * const channel_sfx[] = { - " Front", " Surround", " CLFE", " Side" - }; - int i; - - i = find_idx_in_nid_list(nid, pins, num_pins); - if (i < 0) - return NULL; - if (num_pins == 1) - return ""; - if (num_pins > ARRAY_SIZE(channel_sfx)) { - if (indexp) - *indexp = i; - return ""; - } - return channel_sfx[i]; -} - -static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - const char *name, char *label, int maxlen, - int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - int attr = snd_hda_get_input_pin_attr(def_conf); - const char *pfx = "", *sfx = ""; - - /* handle as a speaker if it's a fixed line-out */ - if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT) - name = "Speaker"; - /* check the location */ - switch (attr) { - case INPUT_PIN_ATTR_DOCK: - pfx = "Dock "; - break; - case INPUT_PIN_ATTR_FRONT: - pfx = "Front "; - break; - } - if (cfg) { - /* try to give a unique suffix if needed */ - sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, - indexp); - if (!sfx) - sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, - indexp); - if (!sfx) { - /* don't add channel suffix for Headphone controls */ - int idx = find_idx_in_nid_list(nid, cfg->hp_pins, - cfg->hp_outs); - if (idx >= 0) - *indexp = idx; - sfx = ""; - } - } - snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); - return 1; -} - -/** - * snd_hda_get_pin_label - Get a label for the given I/O pin - * - * Get a label for the given pin. This function works for both input and - * output pins. When @cfg is given as non-NULL, the function tries to get - * an optimized label using hda_get_autocfg_input_label(). - * - * This function tries to give a unique label string for the pin as much as - * possible. For example, when the multiple line-outs are present, it adds - * the channel suffix like "Front", "Surround", etc (only when @cfg is given). - * If no unique name with a suffix is available and @indexp is non-NULL, the - * index number is stored in the pointer. - */ -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp) -{ - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); - const char *name = NULL; - int i; - - if (indexp) - *indexp = 0; - if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) - return 0; - - switch (get_defcfg_device(def_conf)) { - case AC_JACK_LINE_OUT: - return fill_audio_out_name(codec, nid, cfg, "Line Out", - label, maxlen, indexp); - case AC_JACK_SPEAKER: - return fill_audio_out_name(codec, nid, cfg, "Speaker", - label, maxlen, indexp); - case AC_JACK_HP_OUT: - return fill_audio_out_name(codec, nid, cfg, "Headphone", - label, maxlen, indexp); - case AC_JACK_SPDIF_OUT: - case AC_JACK_DIG_OTHER_OUT: - if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI) - name = "HDMI"; - else - name = "SPDIF"; - if (cfg && indexp) { - i = find_idx_in_nid_list(nid, cfg->dig_out_pins, - cfg->dig_outs); - if (i >= 0) - *indexp = i; - } - break; - default: - if (cfg) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - name = hda_get_autocfg_input_label(codec, cfg, i); - if (name) - break; - } - } - if (!name) - name = hda_get_input_pin_label(codec, nid, true); - break; - } - if (!name) - return 0; - strlcpy(label, name, maxlen); - return 1; -} -EXPORT_SYMBOL_HDA(snd_hda_get_pin_label); - /** * snd_hda_add_imux_item - Add an item to input_mux * diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index d68948499fb..2dd1c113a4c 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -17,6 +17,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_jack.h" bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index c66655cf413..8ae52465ec5 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -12,6 +12,8 @@ #ifndef __SOUND_HDA_JACK_H #define __SOUND_HDA_JACK_H +struct auto_pin_cfg; + struct hda_jack_tbl { hda_nid_t nid; unsigned char action; /* event action (0 = none) */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index a5cee952547..9a096a8e0fc 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -262,6 +262,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, struct snd_ctl_elem_value *ucontrol, hda_nid_t nid, unsigned int *cur_val); +int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, + int index, int *type_index_ret); /* * Channel mode helper @@ -393,72 +395,7 @@ struct hda_bus_unsolicited { struct hda_bus *bus; }; -/* - * Helper for automatic pin configuration - */ - -enum { - AUTO_PIN_MIC, - AUTO_PIN_LINE_IN, - AUTO_PIN_CD, - AUTO_PIN_AUX, - AUTO_PIN_LAST -}; - -enum { - AUTO_PIN_LINE_OUT, - AUTO_PIN_SPEAKER_OUT, - AUTO_PIN_HP_OUT -}; - -#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS -#define AUTO_CFG_MAX_INS 8 - -struct auto_pin_cfg_item { - hda_nid_t pin; - int type; -}; - -struct auto_pin_cfg; -const char *hda_get_autocfg_input_label(struct hda_codec *codec, - const struct auto_pin_cfg *cfg, - int input); -int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, - const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp); -int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label, - int index, int *type_index_ret); - -enum { - INPUT_PIN_ATTR_UNUSED, /* pin not connected */ - INPUT_PIN_ATTR_INT, /* internal mic/line-in */ - INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ - INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ - INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ - INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ -}; - -int snd_hda_get_input_pin_attr(unsigned int def_conf); - -struct auto_pin_cfg { - int line_outs; - /* sorted in the order of Front/Surr/CLFE/Side */ - hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS]; - int speaker_outs; - hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS]; - int hp_outs; - int line_out_type; /* AUTO_PIN_XXX_OUT */ - hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS]; - int num_inputs; - struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS]; - int dig_outs; - hda_nid_t dig_out_pins[2]; - hda_nid_t dig_in_pin; - hda_nid_t mono_out_pin; - int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */ - int dig_in_type; /* HDA_PCM_TYPE_XXX */ -}; - +/* helper macros to retrieve pin default-config values */ #define get_defcfg_connect(cfg) \ ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) #define get_defcfg_association(cfg) \ @@ -472,19 +409,6 @@ struct auto_pin_cfg { #define get_defcfg_misc(cfg) \ ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) -/* bit-flags for snd_hda_parse_pin_def_config() behavior */ -#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */ -#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */ - -int snd_hda_parse_pin_defcfg(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - const hda_nid_t *ignore_nids, - unsigned int cond_flags); - -/* older function */ -#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ - snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0) - /* amp values */ #define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8)) #define AMP_IN_UNMUTE(idx) (0x7000 | ((idx)<<8)) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 723bb9cb5f0..d8b2d6dee98 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -28,6 +28,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index a3b70a8f6df..19ae14f739c 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -26,6 +26,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" /* */ diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index d290a8ff010..d0d3540e39e 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -30,6 +30,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #define WIDGET_CHIP_CTRL 0x15 #define WIDGET_DSP_CTRL 0x16 diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 48c6d8186e9..9647ed4d792 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -26,6 +26,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_jack.h" #include diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index b6767b4ced4..c8fdaaefe70 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -29,6 +29,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #define NUM_PINS 11 diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 884f8ad351f..baf1edde244 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -36,6 +36,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 92e11672b91..db272fb5e57 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -54,6 +54,7 @@ #include #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" #include "hda_jack.h" /* Pin Widget NID */ -- cgit v1.2.3 From 607d4f7f0551eb591fbaca4bf44a8d6251e82f00 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 May 2012 10:31:08 +0200 Subject: ALSA: hda - Remove pre_resume and post_suspend ops Since the recent commit, the resume procedure is always performed at the resume time. This makes the pre_resume hack for VREF mute LED on some HP laptops superfluous. As this is the only user of pre_resume (and there is no user of post_suspend) ops, let's kill them again. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 4 ---- sound/pci/hda/hda_codec.h | 2 -- sound/pci/hda/patch_sigmatel.c | 21 --------------------- 3 files changed, 27 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f2bdf38be39..e0f8667ae22 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4930,8 +4930,6 @@ int snd_hda_suspend(struct hda_bus *bus) list_for_each_entry(codec, &bus->codec_list, list) { if (hda_codec_is_power_on(codec)) hda_call_codec_suspend(codec); - if (codec->patch_ops.post_suspend) - codec->patch_ops.post_suspend(codec); } return 0; } @@ -4951,8 +4949,6 @@ int snd_hda_resume(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { - if (codec->patch_ops.pre_resume) - codec->patch_ops.pre_resume(codec); hda_call_codec_resume(codec); } return 0; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 77cd99531aa..78f89147d62 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -704,8 +704,6 @@ struct hda_codec_ops { unsigned int power_state); #ifdef CONFIG_PM int (*suspend)(struct hda_codec *codec, pm_message_t state); - int (*post_suspend)(struct hda_codec *codec); - int (*pre_resume)(struct hda_codec *codec); int (*resume)(struct hda_codec *codec); #endif #ifdef CONFIG_SND_HDA_POWER_SAVE diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index baf1edde244..b04179be620 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4986,20 +4986,6 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) return 0; } -static int stac92xx_pre_resume(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - /* sync mute LED */ - if (spec->vref_mute_led_nid) - stac_vrefout_set(codec, spec->vref_mute_led_nid, - spec->vref_led); - else if (spec->gpio_led) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - return 0; -} - static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { @@ -5023,7 +5009,6 @@ static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, #else #define stac92xx_suspend NULL #define stac92xx_resume NULL -#define stac92xx_pre_resume NULL #define stac92xx_set_power_state NULL #endif /* CONFIG_PM */ @@ -5569,9 +5554,6 @@ again: codec->patch_ops.set_power_state = stac92xx_set_power_state; } -#ifdef CONFIG_PM - codec->patch_ops.pre_resume = stac92xx_pre_resume; -#endif } err = stac92xx_parse_auto_config(codec); @@ -5878,9 +5860,6 @@ again: codec->patch_ops.set_power_state = stac92xx_set_power_state; } -#ifdef CONFIG_PM - codec->patch_ops.pre_resume = stac92xx_pre_resume; -#endif } spec->multiout.dac_nids = spec->dac_nids; -- cgit v1.2.3 From a2d96e778d1b15d2213f3b7737aa86fd8eda44cb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 May 2012 12:36:22 +0200 Subject: ALSA: hda - More robustify the power-up/down sequence Check the power_transition up/down state instead of boolean bit, so that the power-up sequence can cancel the pending power-down work properly. Also, by moving cancel_delayed_work_sync() before the actual power-up sequence, make sure that the delayed power-down is completed. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 22 +++++++++++++++------- sound/pci/hda/hda_codec.h | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e0f8667ae22..731f8500a23 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2265,7 +2265,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) /* OK, let it free */ #ifdef CONFIG_SND_HDA_POWER_SAVE - cancel_delayed_work(&codec->power_work); + cancel_delayed_work_sync(&codec->power_work); codec->power_on = 0; codec->power_transition = 0; codec->power_jiffies = jiffies; @@ -3491,11 +3491,14 @@ static void hda_call_codec_suspend(struct hda_codec *codec) codec->afg ? codec->afg : codec->mfg, AC_PWRST_D3); #ifdef CONFIG_SND_HDA_POWER_SAVE - snd_hda_update_power_acct(codec); cancel_delayed_work(&codec->power_work); + spin_lock(&codec->power_lock); + snd_hda_update_power_acct(codec); + trace_hda_power_down(codec); codec->power_on = 0; codec->power_transition = 0; codec->power_jiffies = jiffies; + spin_unlock(&codec->power_lock); #endif } @@ -4294,13 +4297,15 @@ static void hda_power_work(struct work_struct *work) struct hda_bus *bus = codec->bus; spin_lock(&codec->power_lock); + if (codec->power_transition > 0) { /* during power-up sequence? */ + spin_unlock(&codec->power_lock); + return; + } if (!codec->power_on || codec->power_count) { codec->power_transition = 0; spin_unlock(&codec->power_lock); return; } - - trace_hda_power_down(codec); spin_unlock(&codec->power_lock); hda_call_codec_suspend(codec); @@ -4341,11 +4346,15 @@ void snd_hda_power_up(struct hda_codec *codec) spin_lock(&codec->power_lock); codec->power_count++; - if (codec->power_on || codec->power_transition) { + if (codec->power_on || codec->power_transition > 0) { spin_unlock(&codec->power_lock); return; } + spin_unlock(&codec->power_lock); + cancel_delayed_work_sync(&codec->power_work); + + spin_lock(&codec->power_lock); trace_hda_power_up(codec); snd_hda_update_power_acct(codec); codec->power_on = 1; @@ -4358,7 +4367,6 @@ void snd_hda_power_up(struct hda_codec *codec) hda_call_codec_resume(codec); spin_lock(&codec->power_lock); - cancel_delayed_work(&codec->power_work); codec->power_transition = 0; spin_unlock(&codec->power_lock); } @@ -4383,7 +4391,7 @@ void snd_hda_power_down(struct hda_codec *codec) return; } if (power_save(codec)) { - codec->power_transition = 1; /* avoid reentrance */ + codec->power_transition = -1; /* avoid reentrance */ queue_delayed_work(codec->bus->workq, &codec->power_work, msecs_to_jiffies(power_save(codec) * 1000)); } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 78f89147d62..fce30b42bc4 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -859,7 +859,7 @@ struct hda_codec { unsigned int no_jack_detect:1; /* Machine has no jack-detection */ #ifdef CONFIG_SND_HDA_POWER_SAVE unsigned int power_on :1; /* current (global) power-state */ - unsigned int power_transition :1; /* power-state in transition */ + int power_transition; /* power-state in transition */ int power_count; /* current (global) power refcount */ struct delayed_work power_work; /* delayed task for powerdown */ unsigned long power_on_acct; -- cgit v1.2.3 From 5ae763b1bc573e7ef5d9a96c71c8b3e3a865ad8c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 8 May 2012 10:34:08 +0200 Subject: ALSA: hda - Add the support for Creative SoundCore3D The controller is compatible with HD-audio 1.0a with some specific restrictions. - The BDLE entries can't be over 4k boundary - No position-buffer and no MSI Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c19e71a94e1..a70d7e5443a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -497,6 +497,7 @@ enum { AZX_DRIVER_NVIDIA, AZX_DRIVER_TERA, AZX_DRIVER_CTX, + AZX_DRIVER_CTHDA, AZX_DRIVER_GENERIC, AZX_NUM_DRIVERS, /* keep this as last entry */ }; @@ -518,6 +519,7 @@ enum { #define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ #define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */ #define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */ +#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -533,6 +535,9 @@ enum { (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\ AZX_DCAPS_ALIGN_BUFSIZE) +#define AZX_DCAPS_PRESET_CTHDA \ + (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY) + static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_PCH] = "HDA Intel PCH", @@ -546,6 +551,7 @@ static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_NVIDIA] = "HDA NVidia", [AZX_DRIVER_TERA] = "HDA Teradici", [AZX_DRIVER_CTX] = "HDA Creative", + [AZX_DRIVER_CTHDA] = "HDA Creative", [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; @@ -1283,7 +1289,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) /* * set up a BDL entry */ -static int setup_bdle(struct snd_pcm_substream *substream, +static int setup_bdle(struct azx *chip, + struct snd_pcm_substream *substream, struct azx_dev *azx_dev, u32 **bdlp, int ofs, int size, int with_ioc) { @@ -1302,6 +1309,12 @@ static int setup_bdle(struct snd_pcm_substream *substream, bdl[1] = cpu_to_le32(upper_32_bits(addr)); /* program the size field of the BDL entry */ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size); + /* one BDLE cannot cross 4K boundary on CTHDA chips */ + if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) { + u32 remain = 0x1000 - (ofs & 0xfff); + if (chunk > remain) + chunk = remain; + } bdl[2] = cpu_to_le32(chunk); /* program the IOC to enable interrupt * only when the whole fragment is processed @@ -1354,7 +1367,7 @@ static int azx_setup_periods(struct azx *chip, bdl_pos_adj[chip->dev_index]); pos_adj = 0; } else { - ofs = setup_bdle(substream, azx_dev, + ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, pos_adj, !substream->runtime->no_period_wakeup); if (ofs < 0) @@ -1364,10 +1377,10 @@ static int azx_setup_periods(struct azx *chip, pos_adj = 0; for (i = 0; i < periods; i++) { if (i == periods - 1 && pos_adj) - ofs = setup_bdle(substream, azx_dev, &bdl, ofs, + ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, period_bytes - pos_adj, 0); else - ofs = setup_bdle(substream, azx_dev, &bdl, ofs, + ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, period_bytes, !substream->runtime->no_period_wakeup); if (ofs < 0) @@ -3116,6 +3129,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB }, #endif + /* CTHDA chips */ + { PCI_DEVICE(0x1102, 0x0010), + .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, + { PCI_DEVICE(0x1102, 0x0012), + .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, /* Vortex86MX */ { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, /* VMware HDAudio */ -- cgit v1.2.3 From 61d648fb4726f8a89c07cd1904f9c2e11bf26df5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 May 2012 08:54:23 +0200 Subject: ALSA: hda - Add Conexant CX20751/2/3/4 codec support These are almost compatible with the older Conexant codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 2cfcec049ea..a03fc333ecf 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -4559,6 +4559,10 @@ static int patch_conexant_auto(struct hda_codec *codec) */ static const struct hda_codec_preset snd_hda_preset_conexant[] = { + { .id = 0x14f11510, .name = "CX20751/2", + .patch = patch_conexant_auto }, + { .id = 0x14f11511, .name = "CX20753/4", + .patch = patch_conexant_auto }, { .id = 0x14f15045, .name = "CX20549 (Venice)", .patch = patch_cxt5045 }, { .id = 0x14f15047, .name = "CX20551 (Waikiki)", @@ -4596,6 +4600,8 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = { {} /* terminator */ }; +MODULE_ALIAS("snd-hda-codec-id:14f11510"); +MODULE_ALIAS("snd-hda-codec-id:14f11511"); MODULE_ALIAS("snd-hda-codec-id:14f15045"); MODULE_ALIAS("snd-hda-codec-id:14f15047"); MODULE_ALIAS("snd-hda-codec-id:14f15051"); -- cgit v1.2.3 From e3245cddcf56ccd810b73d0a2918e02560da93ab Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 May 2012 10:21:29 +0200 Subject: ALSA: hda - Protect SPDIF-related stuff via spdif_mutex Add the missing mutex protection or move into the protected part for SPDIF access codes for codecs. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 35 +++++++++++++++++++++++++---------- sound/pci/hda/patch_hdmi.c | 4 ++-- 2 files changed, 27 insertions(+), 12 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 731f8500a23..5e7c4bf83ab 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2864,12 +2864,15 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; + mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); ucontrol->value.iec958.status[0] = spdif->status & 0xff; ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff; ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff; ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff; + mutex_unlock(&codec->spdif_mutex); return 0; } @@ -2955,12 +2958,14 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - hda_nid_t nid = spdif->nid; + struct hda_spdif_out *spdif; + hda_nid_t nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); + nid = spdif->nid; spdif->status = ucontrol->value.iec958.status[0] | ((unsigned int)ucontrol->value.iec958.status[1] << 8) | ((unsigned int)ucontrol->value.iec958.status[2] << 16) | @@ -2982,9 +2987,12 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; + mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; + mutex_unlock(&codec->spdif_mutex); return 0; } @@ -3004,12 +3012,14 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); int idx = kcontrol->private_value; - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); - hda_nid_t nid = spdif->nid; + struct hda_spdif_out *spdif; + hda_nid_t nid; unsigned short val; int change; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); + nid = spdif->nid; val = spdif->ctls & ~AC_DIG1_ENABLE; if (ucontrol->value.integer.value[0]) val |= AC_DIG1_ENABLE; @@ -3097,6 +3107,9 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls); +/* get the hda_spdif_out entry from the given NID + * call within spdif_mutex lock + */ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, hda_nid_t nid) { @@ -3113,9 +3126,10 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid); void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) { - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); spdif->nid = (u16)-1; mutex_unlock(&codec->spdif_mutex); } @@ -3123,10 +3137,11 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign); void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) { - struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx); + struct hda_spdif_out *spdif; unsigned short val; mutex_lock(&codec->spdif_mutex); + spdif = snd_array_elem(&codec->spdif_out, idx); if (spdif->nid != nid) { spdif->nid = nid; val = spdif->ctls; @@ -4747,11 +4762,11 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, { const hda_nid_t *nids = mout->dac_nids; int chs = substream->runtime->channels; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); + struct hda_spdif_out *spdif; int i; mutex_lock(&codec->spdif_mutex); + spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid); if (mout->dig_out_nid && mout->share_spdif && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { if (chs == 2 && diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 83f345f3c96..ad319d4dc32 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1592,10 +1592,10 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, unsigned int dataDCC2, channel_id; int i; struct hdmi_spec *spec = codec->spec; - struct hda_spdif_out *spdif = - snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); + struct hda_spdif_out *spdif; mutex_lock(&codec->spdif_mutex); + spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid); chs = substream->runtime->channels; -- cgit v1.2.3 From c3b6bcc292da80ea08a979af177538ffdbbae36b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 May 2012 16:11:15 +0200 Subject: ALSA: hda - Fix concurrent hash accesses The amp and caps hashes aren't protected properly for concurrent accesses. Protect them via a new mutex now. But it can't be so simple as originally thought: since the update of a hash table entry itself might trigger the power-up sequence which again accesses the hash table, we can't cover the whole function simply via mutex. Thus the update part has to be split from the mutex and revalidated. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 201 +++++++++++++++++++++++++++------------------- sound/pci/hda/hda_codec.h | 1 + 2 files changed, 121 insertions(+), 81 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5e7c4bf83ab..7a621f5986e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1255,6 +1255,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); + mutex_init(&codec->hash_mutex); init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); @@ -1605,6 +1606,60 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); } +/* overwrite the value with the key in the caps hash */ +static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val) +{ + struct hda_amp_info *info; + + mutex_lock(&codec->hash_mutex); + info = get_alloc_amp_hash(codec, key); + if (!info) { + mutex_unlock(&codec->hash_mutex); + return -EINVAL; + } + info->amp_caps = val; + info->head.val |= INFO_AMP_CAPS; + mutex_unlock(&codec->hash_mutex); + return 0; +} + +/* query the value from the caps hash; if not found, fetch the current + * value from the given function and store in the hash + */ +static unsigned int +query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key, + unsigned int (*func)(struct hda_codec *, hda_nid_t, int)) +{ + struct hda_amp_info *info; + unsigned int val; + + mutex_lock(&codec->hash_mutex); + info = get_alloc_amp_hash(codec, key); + if (!info) { + mutex_unlock(&codec->hash_mutex); + return 0; + } + if (!(info->head.val & INFO_AMP_CAPS)) { + mutex_unlock(&codec->hash_mutex); /* for reentrance */ + val = func(codec, nid, dir); + write_caps_hash(codec, key, val); + } else { + val = info->amp_caps; + mutex_unlock(&codec->hash_mutex); + } + return val; +} + +static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, + int direction) +{ + if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) + nid = codec->afg; + return snd_hda_param_read(codec, nid, + direction == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); +} + /** * query_amp_caps - query AMP capabilities * @codec: the HD-auio codec @@ -1619,22 +1674,9 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); - if (!info) - return 0; - if (!(info->head.val & INFO_AMP_CAPS)) { - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->afg; - info->amp_caps = snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : - AC_PAR_AMP_IN_CAP); - if (info->amp_caps) - info->head.val |= INFO_AMP_CAPS; - } - return info->amp_caps; + return query_caps_hash(codec, nid, direction, + HDA_HASH_KEY(nid, direction, 0), + read_amp_cap); } EXPORT_SYMBOL_HDA(query_amp_caps); @@ -1654,34 +1696,12 @@ EXPORT_SYMBOL_HDA(query_amp_caps); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0)); - if (!info) - return -EINVAL; - info->amp_caps = caps; - info->head.val |= INFO_AMP_CAPS; - return 0; + return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps); } EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps); -static unsigned int -query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key, - unsigned int (*func)(struct hda_codec *, hda_nid_t)) -{ - struct hda_amp_info *info; - - info = get_alloc_amp_hash(codec, key); - if (!info) - return 0; - if (!info->head.val) { - info->head.val |= INFO_AMP_CAPS; - info->amp_caps = func(codec, nid); - } - return info->amp_caps; -} - -static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) +static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid, + int dir) { return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); } @@ -1699,7 +1719,7 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid) */ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) { - return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid), + return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid), read_pin_cap); } EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); @@ -1717,41 +1737,47 @@ EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps); int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, unsigned int caps) { - struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid)); - if (!info) - return -ENOMEM; - info->amp_caps = caps; - info->head.val |= INFO_AMP_CAPS; - return 0; + return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps); } EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); -/* - * read the current volume to info - * if the cache exists, read the cache value. +/* read or sync the hash value with the current value; + * call within hash_mutex */ -static unsigned int get_vol_mute(struct hda_codec *codec, - struct hda_amp_info *info, hda_nid_t nid, - int ch, int direction, int index) +static struct hda_amp_info * +update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int index) { - u32 val, parm; - - if (info->head.val & INFO_AMP_VOL(ch)) - return info->vol[ch]; + struct hda_amp_info *info; + unsigned int parm, val = 0; + bool val_read = false; - parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; - parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; - parm |= index; - val = snd_hda_codec_read(codec, nid, 0, + retry: + info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); + if (!info) + return NULL; + if (!(info->head.val & INFO_AMP_VOL(ch))) { + if (!val_read) { + mutex_unlock(&codec->hash_mutex); + parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; + parm |= direction == HDA_OUTPUT ? + AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; + parm |= index; + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); - info->vol[ch] = val & 0xff; - info->head.val |= INFO_AMP_VOL(ch); - return info->vol[ch]; + val &= 0xff; + val_read = true; + mutex_lock(&codec->hash_mutex); + goto retry; + } + info->vol[ch] = val; + info->head.val |= INFO_AMP_VOL(ch); + } + return info; } /* - * write the current volume in info to the h/w and update the cache + * write the current volume in info to the h/w */ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, hda_nid_t nid, int ch, int direction, int index, @@ -1768,7 +1794,6 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, else parm |= val; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); - info->vol[ch] = val; } /** @@ -1785,10 +1810,14 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) { struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); - if (!info) - return 0; - return get_vol_mute(codec, info, nid, ch, direction, index); + unsigned int val = 0; + + mutex_lock(&codec->hash_mutex); + info = update_amp_hash(codec, nid, ch, direction, index); + if (info) + val = info->vol[ch]; + mutex_unlock(&codec->hash_mutex); + return val; } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read); @@ -1810,15 +1839,23 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, { struct hda_amp_info *info; - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); - if (!info) - return 0; if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; val &= mask; - val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; - if (info->vol[ch] == val) + + mutex_lock(&codec->hash_mutex); + info = update_amp_hash(codec, nid, ch, direction, idx); + if (!info) { + mutex_unlock(&codec->hash_mutex); + return 0; + } + val |= info->vol[ch] & ~mask; + if (info->vol[ch] == val) { + mutex_unlock(&codec->hash_mutex); return 0; + } + info->vol[ch] = val; + mutex_unlock(&codec->hash_mutex); put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } @@ -3693,7 +3730,8 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, } EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format); -static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid) +static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid, + int dir) { unsigned int val = 0; if (nid != codec->afg && @@ -3708,11 +3746,12 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid) static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) { - return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid), + return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid), get_pcm_param); } -static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid) +static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid, + int dir) { unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) @@ -3724,7 +3763,7 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid) static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) { - return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid), + return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid), get_stream_param); } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index fce30b42bc4..29a311b05f2 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -827,6 +827,7 @@ struct hda_codec { struct mutex spdif_mutex; struct mutex control_mutex; + struct mutex hash_mutex; struct snd_array spdif_out; unsigned int spdif_in_enable; /* SPDIF input enable? */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ -- cgit v1.2.3 From 2d825fd82eb765412a558a56e193b77117d56699 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 11 May 2012 08:39:24 +0200 Subject: ALSA: hda/conexant - Correct vendor IDs for new codecs Never trust datasheet... Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index a03fc333ecf..3acb5824ad3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -4559,10 +4559,6 @@ static int patch_conexant_auto(struct hda_codec *codec) */ static const struct hda_codec_preset snd_hda_preset_conexant[] = { - { .id = 0x14f11510, .name = "CX20751/2", - .patch = patch_conexant_auto }, - { .id = 0x14f11511, .name = "CX20753/4", - .patch = patch_conexant_auto }, { .id = 0x14f15045, .name = "CX20549 (Venice)", .patch = patch_cxt5045 }, { .id = 0x14f15047, .name = "CX20551 (Waikiki)", @@ -4597,11 +4593,15 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_conexant_auto }, { .id = 0x14f150b9, .name = "CX20665", .patch = patch_conexant_auto }, + { .id = 0x14f1510f, .name = "CX20751/2", + .patch = patch_conexant_auto }, + { .id = 0x14f15110, .name = "CX20751/2", + .patch = patch_conexant_auto }, + { .id = 0x14f15111, .name = "CX20753/4", + .patch = patch_conexant_auto }, {} /* terminator */ }; -MODULE_ALIAS("snd-hda-codec-id:14f11510"); -MODULE_ALIAS("snd-hda-codec-id:14f11511"); MODULE_ALIAS("snd-hda-codec-id:14f15045"); MODULE_ALIAS("snd-hda-codec-id:14f15047"); MODULE_ALIAS("snd-hda-codec-id:14f15051"); @@ -4619,6 +4619,9 @@ MODULE_ALIAS("snd-hda-codec-id:14f150ab"); MODULE_ALIAS("snd-hda-codec-id:14f150ac"); MODULE_ALIAS("snd-hda-codec-id:14f150b8"); MODULE_ALIAS("snd-hda-codec-id:14f150b9"); +MODULE_ALIAS("snd-hda-codec-id:14f1510f"); +MODULE_ALIAS("snd-hda-codec-id:14f15110"); +MODULE_ALIAS("snd-hda-codec-id:14f15111"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); -- cgit v1.2.3 From 8c7dd8907637d863bdd175f24e895cbcfe03e1e5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sat, 12 May 2012 09:38:05 +0200 Subject: ALSA: hda - Disable FLOAT format support It turned out that the FLOAT format on CS4206 results in simple noises, which implies that this is no right format as is. Since CS4206 is the only codec supporting the float, let's disable it until we find the correct format. Reported-and-tested-by: Adrian Knoth Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7a621f5986e..c556fe1c25e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3842,11 +3842,13 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, bps = 20; } } +#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ if (streams & AC_SUPFMT_FLOAT32) { formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; if (!bps) bps = 32; } +#endif if (streams == AC_SUPFMT_AC3) { /* should be exclusive */ /* temporary hack: we have still no proper support -- cgit v1.2.3