diff options
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/dell_wmi_helper.c | 116 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 97 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 6 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.c | 144 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.h | 16 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 18 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_ca0132.c | 279 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 29 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 104 | ||||
-rw-r--r-- | sound/pci/hda/patch_hdmi.c | 128 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 853 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 31 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 294 | ||||
-rw-r--r-- | sound/pci/hda/thinkpad_helper.c | 27 |
15 files changed, 1147 insertions, 999 deletions
diff --git a/sound/pci/hda/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c index 1b48a8c19d28..bbd6c87a4ed6 100644 --- a/sound/pci/hda/dell_wmi_helper.c +++ b/sound/pci/hda/dell_wmi_helper.c @@ -6,111 +6,18 @@ #if IS_ENABLED(CONFIG_DELL_LAPTOP) #include <linux/dell-led.h> -enum { - MICMUTE_LED_ON, - MICMUTE_LED_OFF, - MICMUTE_LED_FOLLOW_CAPTURE, - MICMUTE_LED_FOLLOW_MUTE, -}; - -static int dell_led_mode = MICMUTE_LED_FOLLOW_MUTE; -static int dell_capture; -static int dell_led_value; static int (*dell_micmute_led_set_func)(int); -static void (*dell_old_cap_hook)(struct hda_codec *, - struct snd_kcontrol *, - struct snd_ctl_elem_value *); - -static void call_micmute_led_update(void) -{ - int val; - - switch (dell_led_mode) { - case MICMUTE_LED_ON: - val = 1; - break; - case MICMUTE_LED_OFF: - val = 0; - break; - case MICMUTE_LED_FOLLOW_CAPTURE: - val = dell_capture; - break; - case MICMUTE_LED_FOLLOW_MUTE: - default: - val = !dell_capture; - break; - } - - if (val == dell_led_value) - return; - dell_led_value = val; - dell_micmute_led_set_func(dell_led_value); -} - -static void update_dell_wmi_micmute_led(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - if (dell_old_cap_hook) - dell_old_cap_hook(codec, kcontrol, ucontrol); - - if (!ucontrol || !dell_micmute_led_set_func) - return; - if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { - /* TODO: How do I verify if it's a mono or stereo here? */ - dell_capture = (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]); - call_micmute_led_update(); - } -} -static int dell_mic_mute_led_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static void dell_micmute_update(struct hda_codec *codec) { - static const char * const texts[] = { - "On", "Off", "Follow Capture", "Follow Mute", - }; - - return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); -} + struct hda_gen_spec *spec = codec->spec; -static int dell_mic_mute_led_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = dell_led_mode; - return 0; + dell_micmute_led_set_func(spec->micmute_led.led_value); } -static int dell_mic_mute_led_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int mode; - - mode = ucontrol->value.enumerated.item[0]; - if (mode > MICMUTE_LED_FOLLOW_MUTE) - mode = MICMUTE_LED_FOLLOW_MUTE; - if (mode == dell_led_mode) - return 0; - dell_led_mode = mode; - call_micmute_led_update(); - return 1; -} - -static const struct snd_kcontrol_new dell_mic_mute_mode_ctls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic Mute-LED Mode", - .info = dell_mic_mute_led_mode_info, - .get = dell_mic_mute_led_mode_get, - .put = dell_mic_mute_led_mode_put, - }, - {} -}; - static void alc_fixup_dell_wmi(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; bool removefunc = false; if (action == HDA_FIXUP_ACT_PROBE) { @@ -121,25 +28,14 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec, return; } - removefunc = true; - if (dell_micmute_led_set_func(false) >= 0) { - dell_led_value = 0; - if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch) - codec_dbg(codec, "Skipping micmute LED control due to several ADCs"); - else { - dell_old_cap_hook = spec->gen.cap_sync_hook; - spec->gen.cap_sync_hook = update_dell_wmi_micmute_led; - removefunc = false; - add_mixer(spec, dell_mic_mute_mode_ctls); - } - } - + removefunc = (dell_micmute_led_set_func(false) < 0) || + (snd_hda_gen_add_micmute_led(codec, + dell_micmute_update) < 0); } if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { symbol_put(dell_micmute_led_set); dell_micmute_led_set_func = NULL; - dell_old_cap_hook = NULL; } } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 20a171ac4bb2..0a5085537034 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -37,15 +37,8 @@ #include "hda_jack.h" #include <sound/hda_hwdep.h> -#ifdef CONFIG_PM -#define codec_in_pm(codec) atomic_read(&(codec)->core.in_pm) -#define hda_codec_is_power_on(codec) \ - (!pm_runtime_suspended(hda_codec_dev(codec))) -#else -#define codec_in_pm(codec) 0 -#define hda_codec_is_power_on(codec) 1 -#endif - +#define codec_in_pm(codec) snd_hdac_is_in_pm(&codec->core) +#define hda_codec_is_power_on(codec) snd_hdac_is_power_on(&codec->core) #define codec_has_epss(codec) \ ((codec)->core.power_caps & AC_PWRST_EPSS) #define codec_has_clkstop(codec) \ @@ -858,6 +851,39 @@ static void snd_hda_codec_dev_release(struct device *dev) kfree(codec); } +#define DEV_NAME_LEN 31 + +static int snd_hda_codec_device_init(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp) +{ + char name[DEV_NAME_LEN]; + struct hda_codec *codec; + int err; + + dev_dbg(card->dev, "%s: entry\n", __func__); + + if (snd_BUG_ON(!bus)) + return -EINVAL; + if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) + return -EINVAL; + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + sprintf(name, "hdaudioC%dD%d", card->number, codec_addr); + err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr); + if (err < 0) { + kfree(codec); + return err; + } + + codec->core.type = HDA_DEV_LEGACY; + *codecp = codec; + + return err; +} + /** * snd_hda_codec_new - create a HDA codec * @bus: the bus to assign @@ -869,7 +895,19 @@ static void snd_hda_codec_dev_release(struct device *dev) int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp) { - struct hda_codec *codec; + int ret; + + ret = snd_hda_codec_device_init(bus, card, codec_addr, codecp); + if (ret < 0) + return ret; + + return snd_hda_codec_device_new(bus, card, codec_addr, *codecp); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_new); + +int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec *codec) +{ char component[31]; hda_nid_t fg; int err; @@ -879,25 +917,14 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, .dev_free = snd_hda_codec_dev_free, }; + dev_dbg(card->dev, "%s: entry\n", __func__); + if (snd_BUG_ON(!bus)) return -EINVAL; if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) return -EINVAL; - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (!codec) - return -ENOMEM; - - sprintf(component, "hdaudioC%dD%d", card->number, codec_addr); - err = snd_hdac_device_init(&codec->core, &bus->core, component, - codec_addr); - if (err < 0) { - kfree(codec); - return err; - } - codec->core.dev.release = snd_hda_codec_dev_release; - codec->core.type = HDA_DEV_LEGACY; codec->core.exec_verb = codec_exec_verb; codec->bus = bus; @@ -957,15 +984,13 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, if (err < 0) goto error; - if (codecp) - *codecp = codec; return 0; error: put_device(hda_codec_dev(codec)); return err; } -EXPORT_SYMBOL_GPL(snd_hda_codec_new); +EXPORT_SYMBOL_GPL(snd_hda_codec_device_new); /** * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults @@ -2846,14 +2871,13 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) { unsigned int state; - atomic_inc(&codec->core.in_pm); - + snd_hdac_enter_pm(&codec->core); if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); update_power_acct(codec, true); - atomic_dec(&codec->core.in_pm); + snd_hdac_leave_pm(&codec->core); return state; } @@ -2862,8 +2886,7 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) */ static void hda_call_codec_resume(struct hda_codec *codec) { - atomic_inc(&codec->core.in_pm); - + snd_hdac_enter_pm(&codec->core); if (codec->core.regmap) regcache_mark_dirty(codec->core.regmap); @@ -2886,7 +2909,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) hda_jackpoll_work(&codec->jackpoll_work.work); else snd_hda_jack_report_sync(codec); - atomic_dec(&codec->core.in_pm); + snd_hdac_leave_pm(&codec->core); } static int hda_codec_runtime_suspend(struct device *dev) @@ -2992,6 +3015,7 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) sync_power_up_states(codec); return 0; } +EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls); /* * PCM stuff @@ -3197,6 +3221,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec) return 0; } +EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms); /* assign all PCMs of the given codec */ int snd_hda_codec_build_pcms(struct hda_codec *codec) @@ -3252,8 +3277,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, for (; knew->name; knew++) { struct snd_kcontrol *kctl; int addr = 0, idx = 0; - if (knew->iface == -1) /* skip this codec private value */ - continue; + if (knew->iface == (__force snd_ctl_elem_iface_t)-1) + continue; /* skip this codec private value */ for (;;) { kctl = snd_ctl_new1(knew, codec); if (!kctl) @@ -3843,7 +3868,7 @@ EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl); * This function is a helper to set a pin ctl value more safely. * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the * value in pin target array via snd_hda_codec_set_pin_target(), then - * actually writes the value via either snd_hda_codec_update_cache() or + * actually writes the value via either snd_hda_codec_write_cache() or * snd_hda_codec_write() depending on @cached flag. */ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, @@ -3852,7 +3877,7 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, val = snd_hda_correct_pin_ctl(codec, pin, val); snd_hda_codec_set_pin_target(codec, pin, val); if (cached) - return snd_hda_codec_update_cache(codec, pin, 0, + return snd_hda_codec_write_cache(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); else return snd_hda_codec_write(codec, pin, 0, diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index a8b1b31f161c..0d98bb9068b1 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -84,6 +84,7 @@ struct hda_bus { */ typedef int (*hda_codec_patch_t)(struct hda_codec *); +#define HDA_CODEC_ID_SKIP_PROBE 0x00000001 #define HDA_CODEC_ID_GENERIC_HDMI 0x00000101 #define HDA_CODEC_ID_GENERIC 0x00000201 @@ -308,6 +309,8 @@ struct hda_codec { */ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); +int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec *codec); int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); @@ -382,9 +385,6 @@ snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, return snd_hdac_regmap_write(&codec->core, nid, verb, parm); } -#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \ - snd_hda_codec_write_cache(codec, nid, flags, verb, parm) - /* the struct for codec->pin_configs */ struct hda_pincfg { hda_nid_t nid; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index db773e219aaa..579984ecdec3 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -209,7 +209,7 @@ static void parse_user_hints(struct hda_codec *codec) */ #define update_pin_ctl(codec, pin, val) \ - snd_hda_codec_update_cache(codec, pin, 0, \ + snd_hda_codec_write_cache(codec, pin, 0, \ AC_VERB_SET_PIN_WIDGET_CONTROL, val) /* restore the pinctl based on the cached value */ @@ -898,7 +898,7 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, hda_nid_t nid = path->path[i]; if (enable && path->multi[i]) - snd_hda_codec_update_cache(codec, nid, 0, + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]); if (has_amp_in(codec, path, i)) @@ -930,7 +930,7 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) return; if (codec->inv_eapd) enable = !enable; - snd_hda_codec_update_cache(codec, pin, 0, + snd_hda_codec_write_cache(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, enable ? 0x02 : 0x00); } @@ -3900,6 +3900,142 @@ static int parse_mic_boost(struct hda_codec *codec) } /* + * mic mute LED hook helpers + */ +enum { + MICMUTE_LED_ON, + MICMUTE_LED_OFF, + MICMUTE_LED_FOLLOW_CAPTURE, + MICMUTE_LED_FOLLOW_MUTE, +}; + +static void call_micmute_led_update(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int val; + + switch (spec->micmute_led.led_mode) { + case MICMUTE_LED_ON: + val = 1; + break; + case MICMUTE_LED_OFF: + val = 0; + break; + case MICMUTE_LED_FOLLOW_CAPTURE: + val = !!spec->micmute_led.capture; + break; + case MICMUTE_LED_FOLLOW_MUTE: + default: + val = !spec->micmute_led.capture; + break; + } + + if (val == spec->micmute_led.led_value) + return; + spec->micmute_led.led_value = val; + if (spec->micmute_led.update) + spec->micmute_led.update(codec); +} + +static void update_micmute_led(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int mask; + + if (spec->micmute_led.old_hook) + spec->micmute_led.old_hook(codec, kcontrol, ucontrol); + + if (!ucontrol) + return; + mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + if (!strcmp("Capture Switch", ucontrol->id.name)) { + /* TODO: How do I verify if it's a mono or stereo here? */ + if (ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]) + spec->micmute_led.capture |= mask; + else + spec->micmute_led.capture &= ~mask; + call_micmute_led_update(codec); + } +} + +static int micmute_led_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { + "On", "Off", "Follow Capture", "Follow Mute", + }; + + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); +} + +static int micmute_led_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->micmute_led.led_mode; + return 0; +} + +static int micmute_led_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int mode; + + mode = ucontrol->value.enumerated.item[0]; + if (mode > MICMUTE_LED_FOLLOW_MUTE) + mode = MICMUTE_LED_FOLLOW_MUTE; + if (mode == spec->micmute_led.led_mode) + return 0; + spec->micmute_led.led_mode = mode; + call_micmute_led_update(codec); + return 1; +} + +static const struct snd_kcontrol_new micmute_led_mode_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Mute-LED Mode", + .info = micmute_led_mode_info, + .get = micmute_led_mode_get, + .put = micmute_led_mode_put, +}; + +/** + * snd_hda_gen_add_micmute_led - helper for setting up mic mute LED hook + * @codec: the HDA codec + * @hook: the callback for updating LED + * + * Called from the codec drivers for offering the mic mute LED controls. + * When established, it sets up cap_sync_hook and triggers the callback at + * each time when the capture mixer switch changes. The callback is supposed + * to update the LED accordingly. + * + * Returns 0 if the hook is established or a negative error code. + */ +int snd_hda_gen_add_micmute_led(struct hda_codec *codec, + void (*hook)(struct hda_codec *)) +{ + struct hda_gen_spec *spec = codec->spec; + + spec->micmute_led.led_mode = MICMUTE_LED_FOLLOW_MUTE; + spec->micmute_led.capture = 0; + spec->micmute_led.led_value = 0; + spec->micmute_led.old_hook = spec->cap_sync_hook; + spec->micmute_led.update = hook; + spec->cap_sync_hook = update_micmute_led; + if (!snd_hda_gen_add_kctl(spec, NULL, &micmute_led_mode_ctl)) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led); + +/* * parse digital I/Os and set up NIDs in BIOS auto-parse mode */ static void parse_digital(struct hda_codec *codec) @@ -5837,7 +5973,7 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) hda_nid_t nid = pin->nid; if (is_jack_detectable(codec, nid) && !snd_hda_jack_tbl_get(codec, nid)) - snd_hda_codec_update_cache(codec, nid, 0, + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, 0); } } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 61772317de46..10123664fa61 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -86,6 +86,16 @@ struct badness_table { extern const struct badness_table hda_main_out_badness; extern const struct badness_table hda_extra_out_badness; +struct hda_micmute_hook { + unsigned int led_mode; + unsigned int capture; + unsigned int led_value; + void (*update)(struct hda_codec *codec); + void (*old_hook)(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +}; + struct hda_gen_spec { char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; @@ -276,6 +286,9 @@ struct hda_gen_spec { struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + /* mic mute LED hook; called via cap_sync_hook */ + struct hda_micmute_hook micmute_led; + /* PCM hooks */ void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, struct hda_codec *codec, @@ -342,4 +355,7 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on); int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); +int snd_hda_gen_add_micmute_led(struct hda_codec *codec, + void (*hook)(struct hda_codec *)); + #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1ae1850b3bfd..1b2ce304152a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1319,15 +1319,16 @@ static const struct vga_switcheroo_client_ops azx_vs_ops = { static int register_vga_switcheroo(struct azx *chip) { struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct pci_dev *p; int err; if (!hda->use_vga_switcheroo) return 0; - /* FIXME: currently only handling DIS controller - * is there any machine with two switchable HDMI audio controllers? - */ - err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, - VGA_SWITCHEROO_DIS); + + p = get_bound_vga(chip->pci); + err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, p); + pci_dev_put(p); + if (err < 0) return err; hda->vga_switcheroo_registered = 1; @@ -1429,7 +1430,7 @@ static struct pci_dev *get_bound_vga(struct pci_dev *pci) p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus), pci->bus->number, 0); if (p) { - if ((p->class >> 8) == PCI_CLASS_DISPLAY_VGA) + if ((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) return p; pci_dev_put(p); } @@ -2207,7 +2208,7 @@ out_free: */ static struct snd_pci_quirk power_save_blacklist[] = { /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ - SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0), + SND_PCI_QUIRK(0x1849, 0xc892, "Asrock B85M-ITX", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ @@ -2535,7 +2536,8 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, /* AMD Raven */ { PCI_DEVICE(0x1022, 0x15e3), - .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, + .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB | + AZX_DCAPS_PM_RUNTIME }, /* ATI HDMI */ { PCI_DEVICE(0x1002, 0x0002), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 757857313426..fd476fb40e1b 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -148,7 +148,7 @@ static void ad_vmaster_eapd_hook(void *private_data, int enabled) return; if (codec->inv_eapd) enabled = !enabled; - snd_hda_codec_update_cache(codec, spec->eapd_nid, 0, + snd_hda_codec_write_cache(codec, spec->eapd_nid, 0, AC_VERB_SET_EAPD_BTLENABLE, enabled ? 0x02 : 0x00); } @@ -991,7 +991,7 @@ static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) if (spec->eapd_nid) ad_vmaster_eapd_hook(private_data, enabled); - snd_hda_codec_update_cache(codec, 0x01, 0, + snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, enabled ? 0x00 : 0x02); } diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 321e95c409c1..0166a3d7cd55 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -897,7 +897,7 @@ struct ca0132_spec { const struct hda_verb *base_init_verbs; const struct hda_verb *base_exit_verbs; const struct hda_verb *chip_init_verbs; - const struct hda_verb *sbz_init_verbs; + const struct hda_verb *desktop_init_verbs; struct hda_verb *spec_init_verbs; struct auto_pin_cfg autocfg; @@ -965,9 +965,11 @@ struct ca0132_spec { long cur_ctl_vals[TUNING_CTLS_COUNT]; #endif /* - * Sound Blaster Z PCI region 2 iomem, used for input and output - * switching, and other unknown commands. + * The Recon3D, Sound Blaster Z, Sound Blaster ZxR, and Sound Blaster + * AE-5 all use PCI region 2 to toggle GPIO and other currently unknown + * things. */ + bool use_pci_mmio; void __iomem *mem_base; /* @@ -994,6 +996,7 @@ enum { QUIRK_ALIENWARE_M17XR4, QUIRK_SBZ, QUIRK_R3DI, + QUIRK_R3D, }; static const struct hda_pintbl alienware_pincfgs[] = { @@ -1025,6 +1028,21 @@ static const struct hda_pintbl sbz_pincfgs[] = { {} }; +/* Recon3D pin configs taken from Windows Driver */ +static const struct hda_pintbl r3d_pincfgs[] = { + { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ + { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ + { 0x0d, 0x014510f0 }, /* Digital Out */ + { 0x0e, 0x01c520f0 }, /* SPDIF In */ + { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */ + { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */ + { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */ + { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */ + { 0x13, 0x908700f0 }, /* What U Hear In*/ + { 0x18, 0x50d000f0 }, /* N/A */ + {} +}; + /* Recon3D integrated pin configs taken from Windows Driver */ static const struct hda_pintbl r3di_pincfgs[] = { { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ @@ -1050,6 +1068,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI), + SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), {} }; @@ -3073,6 +3092,24 @@ static bool dspload_wait_loaded(struct hda_codec *codec) */ /* + * For cards with PCI-E region2 (Sound Blaster Z/ZxR, Recon3D, and AE-5) + * the mmio address 0x320 is used to set GPIO pins. The format for the data + * The first eight bits are just the number of the pin. So far, I've only seen + * this number go to 7. + */ +static void ca0132_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, + bool enable) +{ + struct ca0132_spec *spec = codec->spec; + unsigned short gpio_data; + + gpio_data = gpio_pin & 0xF; + gpio_data |= ((enable << 8) & 0x100); + + writew(gpio_data, spec->mem_base + 0x320); +} + +/* * Sets up the GPIO pins so that they are discoverable. If this isn't done, * the card shows as having no GPIO pins. */ @@ -3947,15 +3984,19 @@ static int ca0132_alt_select_out(struct hda_codec *codec) /*speaker out config*/ switch (spec->quirk) { case QUIRK_SBZ: - writew(0x0007, spec->mem_base + 0x320); - writew(0x0104, spec->mem_base + 0x320); - writew(0x0101, spec->mem_base + 0x320); + ca0132_mmio_gpio_set(codec, 7, false); + ca0132_mmio_gpio_set(codec, 4, true); + ca0132_mmio_gpio_set(codec, 1, true); chipio_set_control_param(codec, 0x0D, 0x18); break; case QUIRK_R3DI: chipio_set_control_param(codec, 0x0D, 0x24); r3di_gpio_out_set(codec, R3DI_LINE_OUT); break; + case QUIRK_R3D: + chipio_set_control_param(codec, 0x0D, 0x24); + ca0132_mmio_gpio_set(codec, 1, true); + break; } /* disable headphone node */ @@ -3983,15 +4024,19 @@ static int ca0132_alt_select_out(struct hda_codec *codec) /* Headphone out config*/ switch (spec->quirk) { case QUIRK_SBZ: - writew(0x0107, spec->mem_base + 0x320); - writew(0x0104, spec->mem_base + 0x320); - writew(0x0001, spec->mem_base + 0x320); + ca0132_mmio_gpio_set(codec, 7, true); + ca0132_mmio_gpio_set(codec, 4, true); + ca0132_mmio_gpio_set(codec, 1, false); chipio_set_control_param(codec, 0x0D, 0x12); break; case QUIRK_R3DI: chipio_set_control_param(codec, 0x0D, 0x21); r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT); break; + case QUIRK_R3D: + chipio_set_control_param(codec, 0x0D, 0x21); + ca0132_mmio_gpio_set(codec, 0x1, false); + break; } snd_hda_codec_write(codec, spec->out_pins[0], 0, @@ -4025,15 +4070,19 @@ static int ca0132_alt_select_out(struct hda_codec *codec) /* Surround out config*/ switch (spec->quirk) { case QUIRK_SBZ: - writew(0x0007, spec->mem_base + 0x320); - writew(0x0104, spec->mem_base + 0x320); - writew(0x0101, spec->mem_base + 0x320); + ca0132_mmio_gpio_set(codec, 7, false); + ca0132_mmio_gpio_set(codec, 4, true); + ca0132_mmio_gpio_set(codec, 1, true); chipio_set_control_param(codec, 0x0D, 0x18); break; case QUIRK_R3DI: chipio_set_control_param(codec, 0x0D, 0x24); r3di_gpio_out_set(codec, R3DI_LINE_OUT); break; + case QUIRK_R3D: + ca0132_mmio_gpio_set(codec, 1, true); + chipio_set_control_param(codec, 0x0D, 0x24); + break; } /* enable line out node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, @@ -4291,7 +4340,8 @@ static int ca0132_alt_select_in(struct hda_codec *codec) case REAR_MIC: switch (spec->quirk) { case QUIRK_SBZ: - writew(0x0000, spec->mem_base + 0x320); + case QUIRK_R3D: + ca0132_mmio_gpio_set(codec, 0, false); tmp = FLOAT_THREE; break; case QUIRK_R3DI: @@ -4323,7 +4373,8 @@ static int ca0132_alt_select_in(struct hda_codec *codec) ca0132_mic_boost_set(codec, 0); switch (spec->quirk) { case QUIRK_SBZ: - writew(0x0000, spec->mem_base + 0x320); + case QUIRK_R3D: + ca0132_mmio_gpio_set(codec, 0, false); break; case QUIRK_R3DI: r3di_gpio_mic_set(codec, R3DI_REAR_MIC); @@ -4349,8 +4400,9 @@ static int ca0132_alt_select_in(struct hda_codec *codec) case FRONT_MIC: switch (spec->quirk) { case QUIRK_SBZ: - writew(0x0100, spec->mem_base + 0x320); - writew(0x0005, spec->mem_base + 0x320); + case QUIRK_R3D: + ca0132_mmio_gpio_set(codec, 0, true); + ca0132_mmio_gpio_set(codec, 5, false); tmp = FLOAT_THREE; break; case QUIRK_R3DI: @@ -5516,8 +5568,7 @@ static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid, sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]); - knew.tlv.c = 0; - knew.tlv.p = 0; + knew.tlv.c = NULL; switch (nid) { case XBASS_XOVER: @@ -5729,11 +5780,11 @@ static const struct snd_kcontrol_new ca0132_mixer[] = { }; /* - * SBZ specific control mixer. Removes auto-detect for mic, and adds surround - * controls. Also sets both the Front Playback and Capture Volume controls to - * alt so they set the DSP's decibel level. + * Desktop specific control mixer. Removes auto-detect for mic, and adds + * surround controls. Also sets both the Front Playback and Capture Volume + * controls to alt so they set the DSP's decibel level. */ -static const struct snd_kcontrol_new sbz_mixer[] = { +static const struct snd_kcontrol_new desktop_mixer[] = { CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT), CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT), @@ -5804,8 +5855,8 @@ static int ca0132_build_controls(struct hda_codec *codec) */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; for (i = 0; i < num_fx; i++) { - /* SBZ breaks if Echo Cancellation is used */ - if (spec->quirk == QUIRK_SBZ) { + /* SBZ and R3D break if Echo Cancellation is used. */ + if (spec->quirk == QUIRK_SBZ || spec->quirk == QUIRK_R3D) { if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID + OUT_EFFECTS_COUNT)) continue; @@ -6187,10 +6238,10 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) } /* - * Recon3Di r3di_setup_defaults sub functions. + * Recon3D r3d_setup_defaults sub functions. */ -static void r3di_dsp_scp_startup(struct hda_codec *codec) +static void r3d_dsp_scp_startup(struct hda_codec *codec) { unsigned int tmp; @@ -6211,7 +6262,7 @@ static void r3di_dsp_scp_startup(struct hda_codec *codec) } -static void r3di_dsp_initial_mic_setup(struct hda_codec *codec) +static void r3d_dsp_initial_mic_setup(struct hda_codec *codec) { unsigned int tmp; @@ -6421,10 +6472,10 @@ static void ca0132_setup_defaults(struct hda_codec *codec) } /* - * Setup default parameters for Recon3Di DSP. + * Setup default parameters for Recon3D/Recon3Di DSP. */ -static void r3di_setup_defaults(struct hda_codec *codec) +static void r3d_setup_defaults(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; unsigned int tmp; @@ -6434,9 +6485,9 @@ static void r3di_setup_defaults(struct hda_codec *codec) if (spec->dsp_state != DSP_DOWNLOADED) return; - r3di_dsp_scp_startup(codec); + r3d_dsp_scp_startup(codec); - r3di_dsp_initial_mic_setup(codec); + r3d_dsp_initial_mic_setup(codec); /*remove DSP headroom*/ tmp = FLOAT_ZERO; @@ -6450,7 +6501,8 @@ static void r3di_setup_defaults(struct hda_codec *codec) /* Set speaker source? */ dspio_set_uint_param(codec, 0x32, 0x00, tmp); - r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); + if (spec->quirk == QUIRK_R3DI) + r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); /* Setup effect defaults */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; @@ -6462,7 +6514,6 @@ static void r3di_setup_defaults(struct hda_codec *codec) ca0132_effects[idx].def_vals[i]); } } - } /* @@ -6727,7 +6778,12 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) { - ca0132_select_mic(codec); + struct ca0132_spec *spec = codec->spec; + + if (spec->use_alt_functions) + ca0132_alt_select_in(codec); + else + ca0132_select_mic(codec); } static void ca0132_init_unsol(struct hda_codec *codec) @@ -6798,8 +6854,8 @@ static struct hda_verb ca0132_init_verbs0[] = { {} }; -/* Extra init verbs for SBZ */ -static struct hda_verb sbz_init_verbs[] = { +/* Extra init verbs for desktop cards. */ +static struct hda_verb ca0132_init_verbs1[] = { {0x15, 0x70D, 0x20}, {0x15, 0x70E, 0x19}, {0x15, 0x707, 0x00}, @@ -6891,16 +6947,12 @@ static void sbz_region2_exit(struct hda_codec *codec) writeb(0x0, spec->mem_base + 0x100); for (i = 0; i < 8; i++) writeb(0xb3, spec->mem_base + 0x304); - /* - * I believe these are GPIO, with the right most hex digit being the - * gpio pin, and the second digit being on or off. We see this more in - * the input/output select functions. - */ - writew(0x0000, spec->mem_base + 0x320); - writew(0x0001, spec->mem_base + 0x320); - writew(0x0104, spec->mem_base + 0x320); - writew(0x0005, spec->mem_base + 0x320); - writew(0x0007, spec->mem_base + 0x320); + + ca0132_mmio_gpio_set(codec, 0, false); + ca0132_mmio_gpio_set(codec, 1, false); + ca0132_mmio_gpio_set(codec, 4, true); + ca0132_mmio_gpio_set(codec, 5, false); + ca0132_mmio_gpio_set(codec, 7, false); } static void sbz_set_pin_ctl_default(struct hda_codec *codec) @@ -6916,7 +6968,7 @@ static void sbz_set_pin_ctl_default(struct hda_codec *codec) AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); } -static void sbz_clear_unsolicited(struct hda_codec *codec) +static void ca0132_clear_unsolicited(struct hda_codec *codec) { hda_nid_t pins[7] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13}; unsigned int i; @@ -6969,21 +7021,22 @@ static void sbz_exit_chip(struct hda_codec *codec) chipio_set_control_param(codec, 0x0D, 0x24); - sbz_clear_unsolicited(codec); + ca0132_clear_unsolicited(codec); sbz_set_pin_ctl_default(codec); snd_hda_codec_write(codec, 0x0B, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); - if (dspload_is_loaded(codec)) - dsp_reset(codec); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x00); - sbz_region2_exit(codec); } +static void r3d_exit_chip(struct hda_codec *codec) +{ + ca0132_clear_unsolicited(codec); + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5b); +} + static void ca0132_exit_chip(struct hda_codec *codec) { /* put any chip cleanup stuffs here. */ @@ -7098,9 +7151,27 @@ static void sbz_pre_dsp_setup(struct hda_codec *codec) AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44); } -/* - * Extra commands that don't really fit anywhere else. - */ +static void r3d_pre_dsp_setup(struct hda_codec *codec) +{ + + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfc); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfd); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfe); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xff); + + chipio_write(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x1E); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x1C); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x5B); + + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44); +} + static void r3di_pre_dsp_setup(struct hda_codec *codec) { chipio_write(codec, 0x18b0a4, 0x000000c2); @@ -7125,13 +7196,12 @@ static void r3di_pre_dsp_setup(struct hda_codec *codec) AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04); } - /* * These are sent before the DSP is downloaded. Not sure * what they do, or if they're necessary. Could possibly * be removed. Figure they're better to leave in. */ -static void sbz_region2_startup(struct hda_codec *codec) +static void ca0132_mmio_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -7171,7 +7241,7 @@ static void ca0132_alt_init(struct hda_codec *codec) ca0132_gpio_init(codec); sbz_pre_dsp_setup(codec); snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_sequence_write(codec, spec->sbz_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); break; case QUIRK_R3DI: codec_dbg(codec, "R3DI alt_init"); @@ -7182,6 +7252,11 @@ static void ca0132_alt_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4); break; + case QUIRK_R3D: + r3d_pre_dsp_setup(codec); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); + break; } } @@ -7218,8 +7293,8 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - if (spec->quirk == QUIRK_SBZ) - sbz_region2_startup(codec); + if (spec->use_pci_mmio) + ca0132_mmio_init(codec); snd_hda_power_up_pm(codec); @@ -7236,14 +7311,13 @@ static int ca0132_init(struct hda_codec *codec) ca0132_refresh_widget_caps(codec); - if (spec->quirk == QUIRK_SBZ) - writew(0x0107, spec->mem_base + 0x320); - switch (spec->quirk) { case QUIRK_R3DI: - r3di_setup_defaults(codec); + case QUIRK_R3D: + r3d_setup_defaults(codec); break; case QUIRK_SBZ: + sbz_setup_defaults(codec); break; default: ca0132_setup_defaults(codec); @@ -7274,20 +7348,12 @@ static int ca0132_init(struct hda_codec *codec) ca0132_gpio_setup(codec); snd_hda_sequence_write(codec, spec->spec_init_verbs); - switch (spec->quirk) { - case QUIRK_SBZ: - sbz_setup_defaults(codec); - ca0132_alt_select_out(codec); - ca0132_alt_select_in(codec); - break; - case QUIRK_R3DI: + if (spec->use_alt_functions) { ca0132_alt_select_out(codec); ca0132_alt_select_in(codec); - break; - default: + } else { ca0132_select_out(codec); ca0132_select_mic(codec); - break; } snd_hda_jack_report_sync(codec); @@ -7316,16 +7382,17 @@ static void ca0132_free(struct hda_codec *codec) case QUIRK_SBZ: sbz_exit_chip(codec); break; + case QUIRK_R3D: + r3d_exit_chip(codec); + break; case QUIRK_R3DI: r3di_gpio_shutdown(codec); - snd_hda_sequence_write(codec, spec->base_exit_verbs); - ca0132_exit_chip(codec); - break; - default: - snd_hda_sequence_write(codec, spec->base_exit_verbs); - ca0132_exit_chip(codec); break; } + + snd_hda_sequence_write(codec, spec->base_exit_verbs); + ca0132_exit_chip(codec); + snd_hda_power_down(codec); if (spec->mem_base) iounmap(spec->mem_base); @@ -7386,8 +7453,15 @@ static void ca0132_config(struct hda_codec *codec) spec->unsol_tag_amic1 = 0x11; break; case QUIRK_SBZ: - codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); - snd_hda_apply_pincfgs(codec, sbz_pincfgs); + case QUIRK_R3D: + if (spec->quirk == QUIRK_SBZ) { + codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); + snd_hda_apply_pincfgs(codec, sbz_pincfgs); + } + if (spec->quirk == QUIRK_R3D) { + codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3d_pincfgs); + } spec->num_outputs = 2; spec->out_pins[0] = 0x0B; /* Line out */ @@ -7473,8 +7547,8 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) struct ca0132_spec *spec = codec->spec; spec->chip_init_verbs = ca0132_init_verbs0; - if (spec->quirk == QUIRK_SBZ) - spec->sbz_init_verbs = sbz_init_verbs; + if (spec->quirk == QUIRK_SBZ || spec->quirk == QUIRK_R3D) + spec->desktop_init_verbs = ca0132_init_verbs1; spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, sizeof(struct hda_verb), GFP_KERNEL); @@ -7530,25 +7604,19 @@ static int patch_ca0132(struct hda_codec *codec) else spec->quirk = QUIRK_NONE; - /* Setup BAR Region 2 for Sound Blaster Z */ - if (spec->quirk == QUIRK_SBZ) { - spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); - if (spec->mem_base == NULL) { - codec_warn(codec, "pci_iomap failed!"); - codec_info(codec, "perhaps this is not an SBZ?"); - spec->quirk = QUIRK_NONE; - } - } - spec->dsp_state = DSP_DOWNLOAD_INIT; spec->num_mixers = 1; /* Set which mixers each quirk uses. */ switch (spec->quirk) { case QUIRK_SBZ: - spec->mixers[0] = sbz_mixer; + spec->mixers[0] = desktop_mixer; snd_hda_codec_set_name(codec, "Sound Blaster Z"); break; + case QUIRK_R3D: + spec->mixers[0] = desktop_mixer; + snd_hda_codec_set_name(codec, "Recon3D"); + break; case QUIRK_R3DI: spec->mixers[0] = r3di_mixer; snd_hda_codec_set_name(codec, "Recon3Di"); @@ -7558,19 +7626,34 @@ static int patch_ca0132(struct hda_codec *codec) break; } - /* Setup whether or not to use alt functions/controls */ + /* Setup whether or not to use alt functions/controls/pci_mmio */ switch (spec->quirk) { case QUIRK_SBZ: + case QUIRK_R3D: + spec->use_alt_controls = true; + spec->use_alt_functions = true; + spec->use_pci_mmio = true; + break; case QUIRK_R3DI: spec->use_alt_controls = true; spec->use_alt_functions = true; + spec->use_pci_mmio = false; break; default: spec->use_alt_controls = false; spec->use_alt_functions = false; + spec->use_pci_mmio = false; break; } + if (spec->use_pci_mmio) { + spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); + if (spec->mem_base == NULL) { + codec_warn(codec, "pci_iomap failed! Setting quirk to QUIRK_NONE."); + spec->quirk = QUIRK_NONE; + } + } + spec->base_init_verbs = ca0132_base_init_verbs; spec->base_exit_verbs = ca0132_base_exit_verbs; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index d6e079f4ec09..a7f91be45194 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1096,25 +1096,6 @@ static int cs421x_init(struct hda_codec *codec) return 0; } -static int cs421x_build_controls(struct hda_codec *codec) -{ - struct cs_spec *spec = codec->spec; - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - - if (spec->gen.autocfg.speaker_outs && - spec->vendor_nid == CS4210_VENDOR_NID) { - err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&cs421x_speaker_boost_ctl, codec)); - if (err < 0) - return err; - } - return 0; -} - static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) { unsigned int caps; @@ -1144,6 +1125,14 @@ static int cs421x_parse_auto_config(struct hda_codec *codec) return err; parse_cs421x_digital(codec); + + if (spec->gen.autocfg.speaker_outs && + spec->vendor_nid == CS4210_VENDOR_NID) { + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, + &cs421x_speaker_boost_ctl)) + return -ENOMEM; + } + return 0; } @@ -1175,7 +1164,7 @@ static int cs421x_suspend(struct hda_codec *codec) #endif static const struct hda_codec_ops cs421x_patch_ops = { - .build_controls = cs421x_build_controls, + .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cs421x_init, .free = cs_free, diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index f641c20095f7..cfd4e4f97f8f 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -37,8 +37,6 @@ struct conexant_spec { struct hda_gen_spec gen; - unsigned int beep_amp; - /* extra EAPD pins */ unsigned int num_eapds; hda_nid_t eapds[4]; @@ -62,65 +60,48 @@ struct conexant_spec { #ifdef CONFIG_SND_HDA_INPUT_BEEP -static inline void set_beep_amp(struct conexant_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - spec->gen.beep_nid = nid; - spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); -} -/* additional beep mixers; the actual parameters are overwritten at build */ +/* additional beep mixers; private_value will be overwritten */ static const struct snd_kcontrol_new cxt_beep_mixer[] = { HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), - { } /* end */ }; -/* create beep controls if needed */ -static int add_beep_ctls(struct hda_codec *codec) +static int set_beep_amp(struct conexant_spec *spec, hda_nid_t nid, + int idx, int dir) { - struct conexant_spec *spec = codec->spec; - int err; + struct snd_kcontrol_new *knew; + unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); + int i; - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = cxt_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } + spec->gen.beep_nid = nid; + for (i = 0; i < ARRAY_SIZE(cxt_beep_mixer); i++) { + knew = snd_hda_gen_add_kctl(&spec->gen, NULL, + &cxt_beep_mixer[i]); + if (!knew) + return -ENOMEM; + knew->private_value = beep_amp; } return 0; } -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#define add_beep_ctls(codec) 0 -#endif - -/* - * Automatic parser for CX20641 & co - */ -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static void cx_auto_parse_beep(struct hda_codec *codec) +static int cx_auto_parse_beep(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; hda_nid_t nid; for_each_hda_codec_node(nid, codec) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { - set_beep_amp(spec, nid, 0, HDA_OUTPUT); - break; - } + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) + return set_beep_amp(spec, nid, 0, HDA_OUTPUT); + return 0; } #else -#define cx_auto_parse_beep(codec) +#define cx_auto_parse_beep(codec) 0 #endif +/* + * Automatic parser for CX20641 & co + */ + /* parse EAPDs */ static void cx_auto_parse_eapd(struct hda_codec *codec) { @@ -179,21 +160,6 @@ static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled) enabled ? 0x00 : 0x02); } -static int cx_auto_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - - err = add_beep_ctls(codec); - if (err < 0) - return err; - - return 0; -} - static int cx_auto_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -211,6 +177,7 @@ static void cx_auto_reboot_notify(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; switch (codec->core.vendor_id) { + case 0x14f12008: /* CX8200 */ case 0x14f150f2: /* CX20722 */ case 0x14f150f4: /* CX20724 */ break; @@ -218,13 +185,14 @@ static void cx_auto_reboot_notify(struct hda_codec *codec) return; } - /* Turn the CX20722 codec into D3 to avoid spurious noises + /* Turn the problematic codec into D3 to avoid spurious noises from the internal speaker during (and after) reboot */ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + msleep(10); } static void cx_auto_free(struct hda_codec *codec) @@ -234,7 +202,7 @@ static void cx_auto_free(struct hda_codec *codec) } static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = cx_auto_build_controls, + .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cx_auto_init, .reboot_notify = cx_auto_reboot_notify, @@ -343,6 +311,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec, snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410); break; case HDA_FIXUP_ACT_PROBE: + WARN_ON(spec->gen.cap_sync_hook); spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; spec->gen.automute_hook = cxt_update_headset_mode; break; @@ -374,7 +343,7 @@ static void cxt_fixup_headset_mic(struct hda_codec *codec, * control. */ #define update_mic_pin(codec, nid, val) \ - snd_hda_codec_update_cache(codec, nid, 0, \ + snd_hda_codec_write_cache(codec, nid, 0, \ AC_VERB_SET_PIN_WIDGET_CONTROL, val) static const struct hda_input_mux olpc_xo_dc_bias = { @@ -695,16 +664,12 @@ static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled) } /* turn on/off mic-mute LED via GPIO per capture hook */ -static void cxt_fixup_gpio_mic_mute_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void cxt_gpio_micmute_update(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - if (ucontrol) - cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, - ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]); + cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->gen.micmute_led.led_value); } @@ -721,11 +686,11 @@ static void cxt_fixup_mute_led_gpio(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook; - spec->gen.cap_sync_hook = cxt_fixup_gpio_mic_mute_hook; spec->gpio_led = 0; spec->mute_led_polarity = 0; spec->gpio_mute_led_mask = 0x01; spec->gpio_mic_led_mask = 0x02; + snd_hda_gen_add_micmute_led(codec, cxt_gpio_micmute_update); } snd_hda_add_verbs(codec, gpio_init); if (spec->gpio_led) @@ -1037,7 +1002,6 @@ static int patch_conexant_auto(struct hda_codec *codec) codec->spec = spec; codec->patch_ops = cx_auto_patch_ops; - cx_auto_parse_beep(codec); cx_auto_parse_eapd(codec); spec->gen.own_eapd_ctl = 1; if (spec->dynamic_eapd) @@ -1097,6 +1061,10 @@ static int patch_conexant_auto(struct hda_codec *codec) if (err < 0) goto error; + err = cx_auto_parse_beep(codec); + if (err < 0) + goto error; + /* Some laptops with Conexant chips show stalls in S3 resume, * which falls into the single-cmd mode. * Better to make reset, then. diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 8a49415aebac..cb587dce67a9 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -177,13 +177,13 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */ - struct i915_audio_component_audio_ops i915_audio_ops; + struct drm_audio_component_audio_ops drm_audio_ops; struct hdac_chmap chmap; hda_nid_t vendor_nid; }; -#ifdef CONFIG_SND_HDA_I915 +#ifdef CONFIG_SND_HDA_COMPONENT static inline bool codec_has_acomp(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -339,13 +339,13 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, if (!per_pin) { /* no pin is bound to the pcm */ uinfo->count = 0; - mutex_unlock(&spec->pcm_lock); - return 0; + goto unlock; } eld = &per_pin->sink_eld; uinfo->count = eld->eld_valid ? eld->eld_size : 0; - mutex_unlock(&spec->pcm_lock); + unlock: + mutex_unlock(&spec->pcm_lock); return 0; } @@ -357,6 +357,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; int pcm_idx; + int err = 0; pcm_idx = kcontrol->private_value; mutex_lock(&spec->pcm_lock); @@ -365,16 +366,15 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, /* no pin is bound to the pcm */ memset(ucontrol->value.bytes.data, 0, ARRAY_SIZE(ucontrol->value.bytes.data)); - mutex_unlock(&spec->pcm_lock); - return 0; + goto unlock; } - eld = &per_pin->sink_eld; + eld = &per_pin->sink_eld; if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || eld->eld_size > ELD_MAX_SIZE) { - mutex_unlock(&spec->pcm_lock); snd_BUG(); - return -EINVAL; + err = -EINVAL; + goto unlock; } memset(ucontrol->value.bytes.data, 0, @@ -382,9 +382,10 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, if (eld->eld_valid) memcpy(ucontrol->value.bytes.data, eld->eld_buffer, eld->eld_size); - mutex_unlock(&spec->pcm_lock); - return 0; + unlock: + mutex_unlock(&spec->pcm_lock); + return err; } static const struct snd_kcontrol_new eld_bytes_ctl = { @@ -1209,8 +1210,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, pin_idx = hinfo_to_pin_index(codec, hinfo); if (!spec->dyn_pcm_assign) { if (snd_BUG_ON(pin_idx < 0)) { - mutex_unlock(&spec->pcm_lock); - return -EINVAL; + err = -EINVAL; + goto unlock; } } else { /* no pin is assigned to the PCM @@ -1218,16 +1219,13 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, */ if (pin_idx < 0) { err = hdmi_pcm_open_no_pin(hinfo, codec, substream); - mutex_unlock(&spec->pcm_lock); - return err; + goto unlock; } } err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx); - if (err < 0) { - mutex_unlock(&spec->pcm_lock); - return err; - } + if (err < 0) + goto unlock; per_cvt = get_cvt(spec, cvt_idx); /* Claim converter */ @@ -1264,12 +1262,11 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, per_cvt->assigned = 0; hinfo->nid = 0; snd_hda_spdif_ctls_unassign(codec, pcm_idx); - mutex_unlock(&spec->pcm_lock); - return -ENODEV; + err = -ENODEV; + goto unlock; } } - mutex_unlock(&spec->pcm_lock); /* Store the updated parameters */ runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; @@ -1278,7 +1275,9 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); - return 0; + unlock: + mutex_unlock(&spec->pcm_lock); + return err; } /* @@ -1867,7 +1866,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_runtime *runtime = substream->runtime; bool non_pcm; int pinctl; - int err; + int err = 0; mutex_lock(&spec->pcm_lock); pin_idx = hinfo_to_pin_index(codec, hinfo); @@ -1879,13 +1878,12 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, pin_cvt_fixup(codec, NULL, cvt_nid); snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); - mutex_unlock(&spec->pcm_lock); - return 0; + goto unlock; } if (snd_BUG_ON(pin_idx < 0)) { - mutex_unlock(&spec->pcm_lock); - return -EINVAL; + err = -EINVAL; + goto unlock; } per_pin = get_pin(spec, pin_idx); pin_nid = per_pin->pin_nid; @@ -1924,6 +1922,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, /* snd_hda_set_dev_select() has been called before */ err = spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); + unlock: mutex_unlock(&spec->pcm_lock); return err; } @@ -1945,6 +1944,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, struct hdmi_spec_per_cvt *per_cvt; struct hdmi_spec_per_pin *per_pin; int pinctl; + int err = 0; if (hinfo->nid) { pcm_idx = hinfo_to_pcm_index(codec, hinfo); @@ -1963,14 +1963,12 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, snd_hda_spdif_ctls_unassign(codec, pcm_idx); clear_bit(pcm_idx, &spec->pcm_in_use); pin_idx = hinfo_to_pin_index(codec, hinfo); - if (spec->dyn_pcm_assign && pin_idx < 0) { - mutex_unlock(&spec->pcm_lock); - return 0; - } + if (spec->dyn_pcm_assign && pin_idx < 0) + goto unlock; if (snd_BUG_ON(pin_idx < 0)) { - mutex_unlock(&spec->pcm_lock); - return -EINVAL; + err = -EINVAL; + goto unlock; } per_pin = get_pin(spec, pin_idx); @@ -1989,10 +1987,11 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_pin->setup = false; per_pin->channels = 0; mutex_unlock(&per_pin->lock); + unlock: mutex_unlock(&spec->pcm_lock); } - return 0; + return err; } static const struct hda_pcm_ops generic_ops = { @@ -2288,7 +2287,7 @@ static void generic_hdmi_free(struct hda_codec *codec) int pin_idx, pcm_idx; if (codec_has_acomp(codec)) - snd_hdac_i915_register_notifier(NULL); + snd_hdac_acomp_register_notifier(&codec->bus->core, NULL); for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@ -2471,6 +2470,38 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_set_power_to_all(codec, fg, power_state); } +/* There is a fixed mapping between audio pin node and display port. + * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: + * Pin Widget 5 - PORT B (port = 1 in i915 driver) + * Pin Widget 6 - PORT C (port = 2 in i915 driver) + * Pin Widget 7 - PORT D (port = 3 in i915 driver) + * + * on VLV, ILK: + * Pin Widget 4 - PORT B (port = 1 in i915 driver) + * Pin Widget 5 - PORT C (port = 2 in i915 driver) + * Pin Widget 6 - PORT D (port = 3 in i915 driver) + */ +static int intel_base_nid(struct hda_codec *codec) +{ + switch (codec->core.vendor_id) { + case 0x80860054: /* ILK */ + case 0x80862804: /* ILK */ + case 0x80862882: /* VLV */ + return 4; + default: + return 5; + } +} + +static int intel_pin2port(void *audio_ptr, int pin_nid) +{ + int base_nid = intel_base_nid(audio_ptr); + + if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3)) + return -1; + return pin_nid - base_nid + 1; /* intel port is 1-based */ +} + static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) { struct hda_codec *codec = audio_ptr; @@ -2481,16 +2512,7 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) if (port < 1 || port > 3) return; - switch (codec->core.vendor_id) { - case 0x80860054: /* ILK */ - case 0x80862804: /* ILK */ - case 0x80862882: /* VLV */ - pin_nid = port + 0x03; - break; - default: - pin_nid = port + 0x04; - break; - } + pin_nid = port + intel_base_nid(codec) - 1; /* intel port is 1-based */ /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume @@ -2498,7 +2520,7 @@ static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0) return; /* ditto during suspend/resume process itself */ - if (atomic_read(&(codec)->core.in_pm)) + if (snd_hdac_is_in_pm(&codec->core)) return; snd_hdac_i915_set_bclk(&codec->bus->core); @@ -2511,14 +2533,16 @@ static void register_i915_notifier(struct hda_codec *codec) struct hdmi_spec *spec = codec->spec; spec->use_acomp_notifier = true; - spec->i915_audio_ops.audio_ptr = codec; + spec->drm_audio_ops.audio_ptr = codec; /* intel_audio_codec_enable() or intel_audio_codec_disable() * will call pin_eld_notify with using audio_ptr pointer * We need make sure audio_ptr is really setup */ wmb(); - spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; - snd_hdac_i915_register_notifier(&spec->i915_audio_ops); + spec->drm_audio_ops.pin2port = intel_pin2port; + spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify; + snd_hdac_acomp_register_notifier(&codec->bus->core, + &spec->drm_audio_ops); } /* setup_stream ops override for HSW+ */ @@ -2551,6 +2575,8 @@ static int alloc_intel_hdmi(struct hda_codec *codec) /* requires i915 binding */ if (!codec->bus->core.audio_component) { codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); + /* set probe_id here to prevent generic fallback binding */ + codec->probe_id = HDA_CODEC_ID_SKIP_PROBE; return -ENODEV; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f6af3e1c2b93..b20974ef1e13 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -43,11 +43,9 @@ /* extra amp-initialization sequence types */ enum { + ALC_INIT_UNDEFINED, ALC_INIT_NONE, ALC_INIT_DEFAULT, - ALC_INIT_GPIO1, - ALC_INIT_GPIO2, - ALC_INIT_GPIO3, }; enum { @@ -85,19 +83,20 @@ struct alc_spec { struct hda_gen_spec gen; /* must be at head */ /* codec parameterization */ - const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ - unsigned int num_mixers; - unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - struct alc_customize_define cdefine; unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ + /* GPIO bits */ + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + bool gpio_write_delay; /* add a delay before writing gpio_data */ + /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ int mute_led_polarity; hda_nid_t mute_led_nid; hda_nid_t cap_mute_led_nid; - unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */ unsigned int gpio_mute_led_mask; unsigned int gpio_mic_led_mask; @@ -205,41 +204,87 @@ static void alc_process_coef_fw(struct hda_codec *codec, } /* - * Append the given mixer and verb elements for the later use - * The mixer array is referred in build_controls(), and init_verbs are - * called in init(). + * GPIO setup tables, used in initialization */ -static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix) + +/* Enable GPIO mask and set output */ +static void alc_setup_gpio(struct hda_codec *codec, unsigned int mask) +{ + struct alc_spec *spec = codec->spec; + + spec->gpio_mask |= mask; + spec->gpio_dir |= mask; + spec->gpio_data |= mask; +} + +static void alc_write_gpio_data(struct hda_codec *codec) { - if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers))) + struct alc_spec *spec = codec->spec; + + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); +} + +static void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask, + bool on) +{ + struct alc_spec *spec = codec->spec; + unsigned int oldval = spec->gpio_data; + + if (on) + spec->gpio_data |= mask; + else + spec->gpio_data &= ~mask; + if (oldval != spec->gpio_data) + alc_write_gpio_data(codec); +} + +static void alc_write_gpio(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->gpio_mask) return; - spec->mixers[spec->num_mixers++] = mix; + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_MASK, spec->gpio_mask); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir); + if (spec->gpio_write_delay) + msleep(1); + alc_write_gpio_data(codec); } -/* - * GPIO setup tables, used in initialization - */ -/* Enable GPIO mask and set output */ -static const struct hda_verb alc_gpio1_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - { } -}; +static void alc_fixup_gpio(struct hda_codec *codec, int action, + unsigned int mask) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) + alc_setup_gpio(codec, mask); +} -static const struct hda_verb alc_gpio2_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, - { } -}; +static void alc_fixup_gpio1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x01); +} -static const struct hda_verb alc_gpio3_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x03}, - { } -}; +static void alc_fixup_gpio2(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x02); +} + +static void alc_fixup_gpio3(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x03); +} + +static void alc_fixup_gpio4(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_gpio(codec, action, 0x04); +} /* * Fix hardware PLL issue @@ -447,16 +492,8 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) { alc_fill_eapd_coef(codec); alc_auto_setup_eapd(codec, true); + alc_write_gpio(codec); switch (type) { - case ALC_INIT_GPIO1: - snd_hda_sequence_write(codec, alc_gpio1_init_verbs); - break; - case ALC_INIT_GPIO2: - snd_hda_sequence_write(codec, alc_gpio2_init_verbs); - break; - case ALC_INIT_GPIO3: - snd_hda_sequence_write(codec, alc_gpio3_init_verbs); - break; case ALC_INIT_DEFAULT: switch (codec->core.vendor_id) { case 0x10ec0260: @@ -656,20 +693,22 @@ do_sku: * 7~6 : Reserved */ tmp = (ass & 0x38) >> 3; /* external Amp control */ - switch (tmp) { - case 1: - spec->init_amp = ALC_INIT_GPIO1; - break; - case 3: - spec->init_amp = ALC_INIT_GPIO2; - break; - case 7: - spec->init_amp = ALC_INIT_GPIO3; - break; - case 5: - default: - spec->init_amp = ALC_INIT_DEFAULT; - break; + if (spec->init_amp == ALC_INIT_UNDEFINED) { + switch (tmp) { + case 1: + alc_setup_gpio(codec, 0x01); + break; + case 3: + alc_setup_gpio(codec, 0x02); + break; + case 7: + alc_setup_gpio(codec, 0x03); + break; + case 5: + default: + spec->init_amp = ALC_INIT_DEFAULT; + break; + } } /* is laptop or Desktop and enable the function "Mute internal speaker @@ -722,47 +761,14 @@ static void alc_fixup_inv_dmic(struct hda_codec *codec, } -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), - { } /* end */ -}; -#endif - static int alc_build_controls(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int i, err; + int err; err = snd_hda_gen_build_controls(codec); if (err < 0) return err; - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* create beep controls if needed */ - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = alc_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); return 0; } @@ -973,8 +979,30 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) * Digital-beep handlers */ #ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir)) + +/* additional beep mixers; private_value will be overwritten */ +static const struct snd_kcontrol_new alc_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), +}; + +/* set up and create beep controls */ +static int set_beep_amp(struct alc_spec *spec, hda_nid_t nid, + int idx, int dir) +{ + struct snd_kcontrol_new *knew; + unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); + int i; + + for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) { + knew = snd_hda_gen_add_kctl(&spec->gen, NULL, + &alc_beep_mixer[i]); + if (!knew) + return -ENOMEM; + knew->private_value = beep_amp; + } + return 0; +} static const struct snd_pci_quirk beep_white_list[] = { SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1), @@ -999,7 +1027,7 @@ static inline int has_cdefine_beep(struct hda_codec *codec) return spec->cdefine.enable_pcbeep; } #else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#define set_beep_amp(spec, nid, idx, dir) 0 #define has_cdefine_beep(codec) 0 #endif @@ -1104,12 +1132,12 @@ static void alc880_fixup_vol_knob(struct hda_codec *codec, static const struct hda_fixup alc880_fixups[] = { [ALC880_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, }, [ALC880_FIXUP_GPIO2] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = alc_gpio2_init_verbs, + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio2, }, [ALC880_FIXUP_MEDION_RIM] = { .type = HDA_FIXUP_VERBS, @@ -1501,8 +1529,11 @@ static int patch_alc880(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog) - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -1544,8 +1575,8 @@ enum { static void alc260_gpio1_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gen.hp_jack_present); + + alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present); } static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, @@ -1562,7 +1593,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ snd_hda_jack_detect_enable_callback(codec, 0x0f, snd_hda_gen_hp_automute); - snd_hda_add_verbs(codec, alc_gpio1_init_verbs); + alc_setup_gpio(codec, 0x01); } } @@ -1589,8 +1620,6 @@ static void alc260_fixup_kn1(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: snd_hda_apply_pincfgs(codec, pincfgs); - break; - case HDA_FIXUP_ACT_PROBE: spec->init_amp = ALC_INIT_NONE; break; } @@ -1600,7 +1629,7 @@ static void alc260_fixup_fsc_s7020(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->init_amp = ALC_INIT_NONE; } @@ -1638,8 +1667,8 @@ static const struct hda_fixup alc260_fixups[] = { }, }, [ALC260_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, }, [ALC260_FIXUP_GPIO1_TOGGLE] = { .type = HDA_FIXUP_FUNC, @@ -1751,8 +1780,11 @@ static int patch_alc260(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog) - set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -1824,47 +1856,14 @@ static void alc889_fixup_coef(struct hda_codec *codec, alc_update_coef_idx(codec, 7, 0, 0x2030); } -/* toggle speaker-output according to the hp-jack state */ -static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) -{ - unsigned int gpiostate, gpiomask, gpiodir; - - gpiostate = snd_hda_codec_read(codec, codec->core.afg, 0, - AC_VERB_GET_GPIO_DATA, 0); - - if (!muted) - gpiostate |= (1 << pin); - else - gpiostate &= ~(1 << pin); - - gpiomask = snd_hda_codec_read(codec, codec->core.afg, 0, - AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= (1 << pin); - - gpiodir = snd_hda_codec_read(codec, codec->core.afg, 0, - AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= (1 << pin); - - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); - - msleep(1); - - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); -} - /* set up GPIO at initialization */ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - if (action != HDA_FIXUP_ACT_INIT) - return; - alc882_gpio_mute(codec, 0, 0); - alc882_gpio_mute(codec, 1, 0); + struct alc_spec *spec = codec->spec; + + spec->gpio_write_delay = true; + alc_fixup_gpio3(codec, fix, action); } /* Fix the connection of some pins for ALC889: @@ -2143,20 +2142,20 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, }, [ALC882_FIXUP_GPIO2] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = alc_gpio2_init_verbs, + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio2, }, [ALC882_FIXUP_GPIO3] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = alc_gpio3_init_verbs, + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio3, }, [ALC882_FIXUP_ASUS_W2JC] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = alc_gpio1_init_verbs, + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio1, .chained = true, .chain_id = ALC882_FIXUP_EAPD, }, @@ -2376,12 +2375,37 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { }; static const struct hda_model_fixup alc882_fixup_models[] = { + {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"}, + {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"}, + {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"}, + {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"}, + {.id = ALC889_FIXUP_CD, .name = "cd"}, + {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"}, + {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"}, + {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"}, + {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"}, + {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"}, + {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"}, + {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"}, + {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"}, + {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"}, + {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"}, {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, + {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"}, + {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"}, + {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"}, + {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"}, + {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"}, + {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"}, + {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"}, + {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"}, {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"}, {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"}, + {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"}, {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"}, + {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"}, {} }; @@ -2435,8 +2459,11 @@ static int patch_alc882(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog && spec->gen.beep_nid) - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (!spec->gen.no_analog && spec->gen.beep_nid) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -2557,6 +2584,14 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { static const struct hda_model_fixup alc262_fixup_models[] = { {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"}, + {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"}, + {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"}, + {.id = ALC262_FIXUP_TYAN, .name = "tyan"}, + {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"}, + {.id = ALC262_FIXUP_BENQ, .name = "benq"}, + {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"}, + {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"}, {} }; @@ -2598,8 +2633,11 @@ static int patch_alc262(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog && spec->gen.beep_nid) - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (!spec->gen.no_analog && spec->gen.beep_nid) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -2645,7 +2683,6 @@ static const struct snd_kcontrol_new alc268_beep_mixer[] = { .put = alc268_beep_switch_put, .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) }, - { } }; /* set PCBEEP vol = 0, mute connections */ @@ -2686,6 +2723,7 @@ static const struct hda_fixup alc268_fixups[] = { static const struct hda_model_fixup alc268_fixup_models[] = { {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, + {.id = ALC268_FIXUP_SPDIF, .name = "spdif"}, {} }; @@ -2713,7 +2751,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) static int patch_alc268(struct hda_codec *codec) { struct alc_spec *spec; - int err; + int i, err; /* ALC268 has no aa-loopback mixer */ err = alc_alloc_spec(codec, 0); @@ -2735,7 +2773,13 @@ static int patch_alc268(struct hda_codec *codec) if (err > 0 && !spec->gen.no_analog && spec->gen.autocfg.speaker_pins[0] != 0x1d) { - add_mixer(spec, alc268_beep_mixer); + for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) { + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, + &alc268_beep_mixer[i])) { + err = -ENOMEM; + goto error; + } + } snd_hda_add_verbs(codec, alc268_beep_init_verbs); if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) /* override the amp caps for beep generator */ @@ -3454,9 +3498,8 @@ static int alc269_resume(struct hda_codec *codec) * suspend, and won't restore the data after resume, so we restore it * in the driver. */ - if (spec->gpio_led) - snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); + if (spec->gpio_data) + alc_write_gpio_data(codec); if (spec->has_alc5505_dsp) alc5505_dsp_resume(codec); @@ -3696,18 +3739,10 @@ static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, bool enabled) { struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_led; if (spec->mute_led_polarity) enabled = !enabled; - - if (enabled) - spec->gpio_led &= ~mask; - else - spec->gpio_led |= mask; - if (spec->gpio_led != oldval) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); + alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ } /* turn on/off mute LED via GPIO per vmaster hook */ @@ -3720,104 +3755,79 @@ static void alc_fixup_gpio_mute_hook(void *private_data, int enabled) } /* turn on/off mic-mute LED via GPIO per capture hook */ -static void alc_fixup_gpio_mic_mute_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_gpio_micmute_update(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (ucontrol) - alc_update_gpio_led(codec, spec->gpio_mic_led_mask, - ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]); + alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->gen.micmute_led.led_value); } -static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +/* setup mute and mic-mute GPIO bits, add hooks appropriately */ +static void alc_fixup_hp_gpio_led(struct hda_codec *codec, + int action, + unsigned int mute_mask, + unsigned int micmute_mask) { struct alc_spec *spec = codec->spec; - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 }, - {} - }; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + alc_fixup_gpio(codec, action, mute_mask | micmute_mask); + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + if (mute_mask) { + spec->gpio_mute_led_mask = mute_mask; spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; - spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; - spec->gpio_led = 0; - spec->mute_led_polarity = 0; - spec->gpio_mute_led_mask = 0x08; - spec->gpio_mic_led_mask = 0x10; - snd_hda_add_verbs(codec, gpio_init); + } + if (micmute_mask) { + spec->gpio_mic_led_mask = micmute_mask; + snd_hda_gen_add_micmute_led(codec, alc_gpio_micmute_update); } } -static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, +static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x22 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x22 }, - {} - }; + alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); +} - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; - spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; - spec->gpio_led = 0; - spec->mute_led_polarity = 0; - spec->gpio_mute_led_mask = 0x02; - spec->gpio_mic_led_mask = 0x20; - snd_hda_add_verbs(codec, gpio_init); - } +static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20); } /* turn on/off mic-mute LED per capture hook */ -static void alc269_fixup_hp_cap_mic_mute_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_cap_micmute_update(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - unsigned int pinval, enable, disable; + unsigned int pinval; + if (!spec->cap_mute_led_nid) + return; pinval = snd_hda_codec_get_pin_target(codec, spec->cap_mute_led_nid); pinval &= ~AC_PINCTL_VREFEN; - enable = pinval | AC_PINCTL_VREF_80; - disable = pinval | AC_PINCTL_VREF_HIZ; - - if (!ucontrol) - return; - - if (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]) - pinval = disable; + if (spec->gen.micmute_led.led_value) + pinval |= AC_PINCTL_VREF_80; else - pinval = enable; - - if (spec->cap_mute_led_nid) - snd_hda_set_pin_ctl_cache(codec, spec->cap_mute_led_nid, pinval); + pinval |= AC_PINCTL_VREF_HIZ; + snd_hda_set_pin_ctl_cache(codec, spec->cap_mute_led_nid, pinval); } static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x08 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x08 }, - {} - }; + alc_fixup_hp_gpio_led(codec, action, 0x08, 0); if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; - spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook; - spec->gpio_led = 0; - spec->mute_led_polarity = 0; - spec->gpio_mute_led_mask = 0x08; + /* Like hp_gpio_mic1_led, but also needs GPIO4 low to + * enable headphone amp + */ + spec->gpio_mask |= 0x10; + spec->gpio_dir |= 0x10; spec->cap_mute_led_nid = 0x18; - snd_hda_add_verbs(codec, gpio_init); + snd_hda_gen_add_micmute_led(codec, alc_cap_micmute_update); codec->power_filter = led_power_filter; } } @@ -3825,22 +3835,12 @@ static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, static void alc280_fixup_hp_gpio4(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - /* Like hp_gpio_mic1_led, but also needs GPIO4 low to enable headphone amp */ struct alc_spec *spec = codec->spec; - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 }, - {} - }; + alc_fixup_hp_gpio_led(codec, action, 0x08, 0); if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; - spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook; - spec->gpio_led = 0; - spec->mute_led_polarity = 0; - spec->gpio_mute_led_mask = 0x08; spec->cap_mute_led_nid = 0x18; - snd_hda_add_verbs(codec, gpio_init); + snd_hda_gen_add_micmute_led(codec, alc_cap_micmute_update); codec->power_filter = led_power_filter; } } @@ -3890,38 +3890,29 @@ static int alc_register_micmute_input_device(struct hda_codec *codec) return 0; } +/* GPIO1 = set according to SKU external amp + * GPIO2 = mic mute hotkey + * GPIO3 = mute LED + * GPIO4 = mic mute LED + */ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - /* GPIO1 = set according to SKU external amp - GPIO2 = mic mute hotkey - GPIO3 = mute LED - GPIO4 = mic mute LED */ - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x1e }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x1a }, - { 0x01, AC_VERB_SET_GPIO_DATA, 0x02 }, - {} - }; - struct alc_spec *spec = codec->spec; + alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10); if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->init_amp = ALC_INIT_DEFAULT; if (alc_register_micmute_input_device(codec) != 0) return; - snd_hda_add_verbs(codec, gpio_init); + spec->gpio_mask |= 0x06; + spec->gpio_dir |= 0x02; + spec->gpio_data |= 0x02; snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); snd_hda_jack_detect_enable_callback(codec, codec->core.afg, gpio2_mic_hotkey_event); - - spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; - spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; - spec->gpio_led = 0; - spec->mute_led_polarity = 0; - spec->gpio_mute_led_mask = 0x08; - spec->gpio_mic_led_mask = 0x10; return; } @@ -3929,40 +3920,28 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, return; switch (action) { - case HDA_FIXUP_ACT_PROBE: - spec->init_amp = ALC_INIT_DEFAULT; - break; case HDA_FIXUP_ACT_FREE: input_unregister_device(spec->kb_dev); spec->kb_dev = NULL; } } +/* Line2 = mic mute hotkey + * GPIO2 = mic mute LED + */ static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - /* Line2 = mic mute hotkey - GPIO2 = mic mute LED */ - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 }, - {} - }; - struct alc_spec *spec = codec->spec; + alc_fixup_hp_gpio_led(codec, action, 0, 0x04); if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->init_amp = ALC_INIT_DEFAULT; if (alc_register_micmute_input_device(codec) != 0) return; - snd_hda_add_verbs(codec, gpio_init); snd_hda_jack_detect_enable_callback(codec, 0x1b, gpio2_mic_hotkey_event); - - spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; - spec->gpio_led = 0; - spec->mute_led_polarity = 0; - spec->gpio_mic_led_mask = 0x04; return; } @@ -3970,9 +3949,6 @@ static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, return; switch (action) { - case HDA_FIXUP_ACT_PROBE: - spec->init_amp = ALC_INIT_DEFAULT; - break; case HDA_FIXUP_ACT_FREE: input_unregister_device(spec->kb_dev); spec->kb_dev = NULL; @@ -3988,14 +3964,10 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a); if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; - spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook; - spec->mute_led_polarity = 0; - spec->mute_led_nid = 0x1a; spec->cap_mute_led_nid = 0x18; - spec->gen.vmaster_mute_enum = 1; - codec->power_filter = led_power_filter; + snd_hda_gen_add_micmute_led(codec, alc_cap_micmute_update); } } @@ -4843,6 +4815,7 @@ static void alc_probe_headset_mode(struct hda_codec *codec) spec->headphone_mic_pin = cfg->inputs[i].pin; } + WARN_ON(spec->gen.cap_sync_hook); spec->gen.cap_sync_hook = alc_update_headset_mode_hook; spec->gen.automute_hook = alc_update_headset_mode; spec->gen.hp_automute_hook = alc_update_headset_jack_cb; @@ -4934,13 +4907,10 @@ static void alc288_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; - int present; alc_update_headset_jack_cb(codec, jack); /* Headset Mic enable or disable, only for Dell Dino */ - present = spec->gen.hp_jack_present ? 0x40 : 0; - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - present); + alc_update_gpio_data(codec, 0x40, spec->gen.hp_jack_present); } static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, @@ -4949,6 +4919,9 @@ static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); if (action == HDA_FIXUP_ACT_PROBE) { struct alc_spec *spec = codec->spec; + /* toggled via hp_automute_hook */ + spec->gpio_mask |= 0x40; + spec->gpio_dir |= 0x40; spec->gen.hp_automute_hook = alc288_update_headset_jack_cb; } } @@ -4969,7 +4942,7 @@ static void alc_no_shutup(struct hda_codec *codec) static void alc_fixup_no_shutup(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { struct alc_spec *spec = codec->spec; spec->shutup = alc_no_shutup; } @@ -5051,10 +5024,9 @@ static void alc_fixup_dell_xps13(struct hda_codec *codec, * it causes a click noise at start up */ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ); + spec->shutup = alc_shutup_dell_xps13; break; case HDA_FIXUP_ACT_PROBE: - spec->shutup = alc_shutup_dell_xps13; - /* Make the internal mic the default input source. */ for (i = 0; i < imux->num_items; i++) { if (spec->gen.imux_pins[i] == 0x12) { @@ -5231,13 +5203,6 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - /* TX300 needs to set up GPIO2 for the speaker amp */ - static const struct hda_verb gpio2_verbs[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 }, - { 0x01, AC_VERB_SET_GPIO_DATA, 0x04 }, - {} - }; static const struct hda_pintbl dock_pins[] = { { 0x1b, 0x21114000 }, /* dock speaker pin */ {} @@ -5245,13 +5210,18 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_add_verbs(codec, gpio2_verbs); + spec->init_amp = ALC_INIT_DEFAULT; + /* TX300 needs to set up GPIO2 for the speaker amp */ + alc_setup_gpio(codec, 0x04); snd_hda_apply_pincfgs(codec, dock_pins); spec->gen.auto_mute_via_amp = 1; spec->gen.automute_hook = asus_tx300_automute; snd_hda_jack_detect_enable_callback(codec, 0x1b, snd_hda_gen_hp_automute); break; + case HDA_FIXUP_ACT_PROBE: + spec->init_amp = ALC_INIT_DEFAULT; + break; case HDA_FIXUP_ACT_BUILD: /* this is a bit tricky; give more sane names for the main * (tablet) speaker and the dock speaker, respectively @@ -5325,30 +5295,26 @@ static void alc280_fixup_hp_9480m(struct hda_codec *codec, int action) { struct alc_spec *spec = codec->spec; - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 }, - {} - }; + alc_fixup_hp_gpio_led(codec, action, 0x08, 0); if (action == HDA_FIXUP_ACT_PRE_PROBE) { - /* Set the hooks to turn the headphone amp on/off - * as needed - */ - spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + /* amp at GPIO4; toggled via alc280_hp_gpio4_automute_hook() */ + spec->gpio_mask |= 0x10; + spec->gpio_dir |= 0x10; spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook; + } +} - /* The GPIOs are currently off */ - spec->gpio_led = 0; - - /* GPIO3 is connected to the output mute LED, - * high is on, low is off - */ - spec->mute_led_polarity = 0; - spec->gpio_mute_led_mask = 0x08; +static void alc275_fixup_gpio4_off(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; - /* Initialize GPIO configuration */ - snd_hda_add_verbs(codec, gpio_init); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gpio_mask |= 0x04; + spec->gpio_dir |= 0x04; + /* set data bit low */ } } @@ -5492,7 +5458,6 @@ enum { ALC280_FIXUP_HP_9480M, ALC288_FIXUP_DELL_HEADSET_MODE, ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC288_FIXUP_DELL_XPS_13_GPIO6, ALC288_FIXUP_DELL_XPS_13, ALC288_FIXUP_DISABLE_AAMIX, ALC292_FIXUP_DELL_E7X, @@ -5540,13 +5505,8 @@ static const struct hda_fixup alc269_fixups[] = { } }, [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x01, AC_VERB_SET_GPIO_MASK, 0x04}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - { } - }, + .type = HDA_FIXUP_FUNC, + .v.func = alc275_fixup_gpio4_off, .chained = true, .chain_id = ALC269_FIXUP_SONY_VAIO }, @@ -6113,22 +6073,11 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE }, - [ALC288_FIXUP_DELL_XPS_13_GPIO6] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x01, AC_VERB_SET_GPIO_MASK, 0x40}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x40}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - { } - }, - .chained = true, - .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE - }, [ALC288_FIXUP_DISABLE_AAMIX] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_disable_aamix, .chained = true, - .chain_id = ALC288_FIXUP_DELL_XPS_13_GPIO6 + .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE }, [ALC288_FIXUP_DELL_XPS_13] = { .type = HDA_FIXUP_FUNC, @@ -6291,14 +6240,9 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE }, [ALC256_FIXUP_ASUS_AIO_GPIO2] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* Set up GPIO2 for the speaker amp */ - { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 }, - { 0x01, AC_VERB_SET_GPIO_DATA, 0x04 }, - {} - }, + .type = HDA_FIXUP_FUNC, + /* Set up GPIO2 for the speaker amp */ + .v.func = alc_fixup_gpio4, }, [ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, @@ -6530,6 +6474,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -6713,13 +6658,95 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"}, {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"}, + {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"}, + {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"}, {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"}, {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"}, {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, + {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"}, {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"}, + {.id = ALC269_FIXUP_SONY_VAIO, .name = "vaio"}, + {.id = ALC269_FIXUP_DELL_M101Z, .name = "dell-m101z"}, + {.id = ALC269_FIXUP_ASUS_G73JW, .name = "asus-g73jw"}, + {.id = ALC269_FIXUP_LENOVO_EAPD, .name = "lenovo-eapd"}, + {.id = ALC275_FIXUP_SONY_HWEQ, .name = "sony-hweq"}, + {.id = ALC269_FIXUP_PCM_44K, .name = "pcm44k"}, + {.id = ALC269_FIXUP_LIFEBOOK, .name = "lifebook"}, + {.id = ALC269_FIXUP_LIFEBOOK_EXTMIC, .name = "lifebook-extmic"}, + {.id = ALC269_FIXUP_LIFEBOOK_HP_PIN, .name = "lifebook-hp-pin"}, + {.id = ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, .name = "lifebook-u7x7"}, + {.id = ALC269VB_FIXUP_AMIC, .name = "alc269vb-amic"}, + {.id = ALC269VB_FIXUP_DMIC, .name = "alc269vb-dmic"}, + {.id = ALC269_FIXUP_HP_MUTE_LED_MIC1, .name = "hp-mute-led-mic1"}, + {.id = ALC269_FIXUP_HP_MUTE_LED_MIC2, .name = "hp-mute-led-mic2"}, + {.id = ALC269_FIXUP_HP_MUTE_LED_MIC3, .name = "hp-mute-led-mic3"}, + {.id = ALC269_FIXUP_HP_GPIO_MIC1_LED, .name = "hp-gpio-mic1"}, + {.id = ALC269_FIXUP_HP_LINE1_MIC1_LED, .name = "hp-line1-mic1"}, + {.id = ALC269_FIXUP_NO_SHUTUP, .name = "noshutup"}, + {.id = ALC286_FIXUP_SONY_MIC_NO_PRESENCE, .name = "sony-nomic"}, + {.id = ALC269_FIXUP_ASPIRE_HEADSET_MIC, .name = "aspire-headset-mic"}, + {.id = ALC269_FIXUP_ASUS_X101, .name = "asus-x101"}, + {.id = ALC271_FIXUP_HP_GATE_MIC_JACK, .name = "acer-ao7xx"}, + {.id = ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, .name = "acer-aspire-e1"}, + {.id = ALC269_FIXUP_ACER_AC700, .name = "acer-ac700"}, + {.id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, .name = "limit-mic-boost"}, + {.id = ALC269VB_FIXUP_ASUS_ZENBOOK, .name = "asus-zenbook"}, + {.id = ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, .name = "asus-zenbook-ux31a"}, + {.id = ALC269VB_FIXUP_ORDISSIMO_EVE2, .name = "ordissimo"}, + {.id = ALC282_FIXUP_ASUS_TX300, .name = "asus-tx300"}, + {.id = ALC283_FIXUP_INT_MIC, .name = "alc283-int-mic"}, + {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"}, + {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"}, + {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"}, + {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"}, + {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"}, + {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"}, + {.id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"}, + {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"}, + {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"}, + {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"}, + {.id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, .name = "alc255-dell-mute"}, + {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"}, + {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"}, + {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, + {.id = ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, .name = "hp-gpio2-hotkey"}, + {.id = ALC280_FIXUP_HP_DOCK_PINS, .name = "hp-dock-pins"}, + {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic"}, + {.id = ALC280_FIXUP_HP_9480M, .name = "hp-9480m"}, + {.id = ALC288_FIXUP_DELL_HEADSET_MODE, .name = "alc288-dell-headset"}, + {.id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc288-dell1"}, + {.id = ALC288_FIXUP_DELL_XPS_13, .name = "alc288-dell-xps13"}, + {.id = ALC292_FIXUP_DELL_E7X, .name = "dell-e7x"}, + {.id = ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, .name = "alc293-dell"}, + {.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"}, + {.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"}, + {.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"}, + {.id = ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE, .name = "alc256-dell-xps13"}, + {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"}, + {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"}, + {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"}, + {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"}, + {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"}, + {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"}, + {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"}, + {.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"}, + {.id = ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, .name = "dell-inspiron-7559"}, + {.id = ALC269_FIXUP_ATIV_BOOK_8, .name = "ativ-book"}, + {.id = ALC221_FIXUP_HP_MIC_NO_PRESENCE, .name = "alc221-hp-mic"}, + {.id = ALC256_FIXUP_ASUS_HEADSET_MODE, .name = "alc256-asus-headset"}, + {.id = ALC256_FIXUP_ASUS_MIC, .name = "alc256-asus-mic"}, + {.id = ALC256_FIXUP_ASUS_AIO_GPIO2, .name = "alc256-asus-aio"}, + {.id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc233-asus"}, + {.id = ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, .name = "alc233-eapd"}, + {.id = ALC294_FIXUP_LENOVO_MIC_LOCATION, .name = "alc294-lenovo-mic"}, + {.id = ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, .name = "alc225-wyse"}, + {.id = ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, .name = "alc274-dell-aio"}, + {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"}, + {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"}, + {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"}, {} }; #define ALC225_STANDARD_PINS \ @@ -6983,7 +7010,7 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60130}, {0x19, 0x03a11020}, {0x21, 0x0321101f}), - SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL_XPS_13_GPIO6, + SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60120}, {0x14, 0x90170110}, {0x21, 0x0321101f}), @@ -7140,18 +7167,6 @@ static int patch_alc269(struct hda_codec *codec) spec->shutup = alc_default_shutup; spec->init_hook = alc_default_init; - snd_hda_pick_fixup(codec, alc269_fixup_models, - alc269_fixup_tbl, alc269_fixups); - snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups); - snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl, - alc269_fixups); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - alc_auto_parse_customize_define(codec); - - if (has_cdefine_beep(codec)) - spec->gen.beep_nid = 0x01; - switch (codec->core.vendor_id) { case 0x10ec0269: spec->codec_variant = ALC269_TYPE_ALC269VA; @@ -7271,13 +7286,28 @@ static int patch_alc269(struct hda_codec *codec) spec->init_hook = alc5505_dsp_init; } + snd_hda_pick_fixup(codec, alc269_fixup_models, + alc269_fixup_tbl, alc269_fixups); + snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups); + snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl, + alc269_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + + alc_auto_parse_customize_define(codec); + + if (has_cdefine_beep(codec)) + spec->gen.beep_nid = 0x01; + /* automatic parse from the BIOS config */ err = alc269_parse_auto_config(codec); if (err < 0) goto error; - if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) - set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT); + if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) { + err = set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT); + if (err < 0) + goto error; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -7406,8 +7436,11 @@ static int patch_alc861(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog) - set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); + if (err < 0) + goto error; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -7447,16 +7480,21 @@ static void alc861vd_fixup_dallas(struct hda_codec *codec, } } +/* reset GPIO1 */ +static void alc660vd_fixup_asus_gpio1(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->gpio_mask |= 0x02; + alc_fixup_gpio(codec, action, 0x01); +} + static const struct hda_fixup alc861vd_fixups[] = { [ALC660VD_FIX_ASUS_GPIO1] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - /* reset GPIO1 */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, - { } - } + .type = HDA_FIXUP_FUNC, + .v.func = alc660vd_fixup_asus_gpio1, }, [ALC861VD_FIX_DALLAS] = { .type = HDA_FIXUP_FUNC, @@ -7495,8 +7533,11 @@ static int patch_alc861vd(struct hda_codec *codec) if (err < 0) goto error; - if (!spec->gen.no_analog) - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (!spec->gen.no_analog) { + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + if (err < 0) + goto error; + } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); @@ -7577,7 +7618,7 @@ static unsigned int gpio_led_power_filter(struct hda_codec *codec, unsigned int power_state) { struct alc_spec *spec = codec->spec; - if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_led) + if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data) return AC_PWRST_D0; return power_state; } @@ -7586,18 +7627,10 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - static const struct hda_verb gpio_init[] = { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 }, - {} - }; + alc_fixup_hp_gpio_led(codec, action, 0x01, 0); if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; - spec->gpio_led = 0; spec->mute_led_polarity = 1; - spec->gpio_mute_led_mask = 0x01; - snd_hda_add_verbs(codec, gpio_init); codec->power_filter = gpio_led_power_filter; } } @@ -8110,7 +8143,10 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { }; static const struct hda_model_fixup alc662_fixup_models[] = { + {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"}, + {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"}, {.id = ALC272_FIXUP_MARIO, .name = "mario"}, + {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"}, {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"}, @@ -8119,8 +8155,23 @@ static const struct hda_model_fixup alc662_fixup_models[] = { {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"}, {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"}, {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"}, + {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"}, {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"}, + {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"}, {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, + {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"}, + {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"}, + {.id = ALC662_FIXUP_BASS_16, .name = "bass16"}, + {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"}, + {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"}, + {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"}, + {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"}, + {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"}, + {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"}, + {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"}, + {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"}, + {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"}, + {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, {} }; @@ -8214,18 +8265,20 @@ static int patch_alc662(struct hda_codec *codec) if (!spec->gen.no_analog && spec->gen.beep_nid) { switch (codec->core.vendor_id) { case 0x10ec0662: - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); break; case 0x10ec0272: case 0x10ec0663: case 0x10ec0665: case 0x10ec0668: - set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); break; case 0x10ec0273: - set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); + err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); break; } + if (err < 0) + goto error; } snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 63d15b545b33..046705b4691a 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -332,33 +332,15 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, } /* hook for controlling mic-mute LED GPIO */ -static void stac_capture_led_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void stac_capture_led_update(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - unsigned int mask; - bool cur_mute, prev_mute; - if (!kcontrol || !ucontrol) - return; - - mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - prev_mute = !spec->mic_enabled; - if (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]) - spec->mic_enabled |= mask; + if (spec->gen.micmute_led.led_value) + spec->gpio_data |= spec->mic_mute_led_gpio; else - spec->mic_enabled &= ~mask; - cur_mute = !spec->mic_enabled; - if (cur_mute != prev_mute) { - if (cur_mute) - spec->gpio_data |= spec->mic_mute_led_gpio; - else - spec->gpio_data &= ~spec->mic_mute_led_gpio; - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - } + spec->gpio_data &= ~spec->mic_mute_led_gpio; + stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); } static int stac_vrefout_set(struct hda_codec *codec, @@ -4656,8 +4638,7 @@ static void stac_setup_gpio(struct hda_codec *codec) spec->gpio_dir |= spec->mic_mute_led_gpio; spec->mic_enabled = 0; spec->gpio_data |= spec->mic_mute_led_gpio; - - spec->gen.cap_sync_hook = stac_capture_led_hook; + snd_hda_gen_add_micmute_led(codec, stac_capture_led_update); } } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index fc30d1e8aa76..6b9617aee0e6 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -90,13 +90,6 @@ enum VIA_HDA_CODEC { struct via_spec { struct hda_gen_spec gen; - /* codec parameterization */ - const struct snd_kcontrol_new *mixers[6]; - unsigned int num_mixers; - - const struct hda_verb *init_verbs[5]; - unsigned int num_iverbs; - /* HP mode source */ unsigned int dmic_enabled; enum VIA_HDA_CODEC codec_type; @@ -107,8 +100,6 @@ struct via_spec { /* work to check hp jack state */ int hp_work_active; int vt1708_jack_detect; - - unsigned int beep_amp; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -262,69 +253,51 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { - { +static const struct snd_kcontrol_new via_pin_power_ctl_enum = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Dynamic Power-Control", .info = via_pin_power_ctl_info, .get = via_pin_power_ctl_get, .put = via_pin_power_ctl_put, - }, - {} /* terminator */ }; #ifdef CONFIG_SND_HDA_INPUT_BEEP -static inline void set_beep_amp(struct via_spec *spec, hda_nid_t nid, - int idx, int dir) -{ - spec->gen.beep_nid = nid; - spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); -} - /* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new cxt_beep_mixer[] = { +static const struct snd_kcontrol_new via_beep_mixer[] = { HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), - { } /* end */ }; -/* create beep controls if needed */ -static int add_beep_ctls(struct hda_codec *codec) +static int set_beep_amp(struct via_spec *spec, hda_nid_t nid, + int idx, int dir) { - struct via_spec *spec = codec->spec; - int err; + struct snd_kcontrol_new *knew; + unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); + int i; - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = cxt_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } + spec->gen.beep_nid = nid; + for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) { + knew = snd_hda_gen_add_kctl(&spec->gen, NULL, + &via_beep_mixer[i]); + if (!knew) + return -ENOMEM; + knew->private_value = beep_amp; } return 0; } -static void auto_parse_beep(struct hda_codec *codec) +static int auto_parse_beep(struct hda_codec *codec) { struct via_spec *spec = codec->spec; hda_nid_t nid; for_each_hda_codec_node(nid, codec) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { - set_beep_amp(spec, nid, 0, HDA_OUTPUT); - break; - } + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) + return set_beep_amp(spec, nid, 0, HDA_OUTPUT); + return 0; } #else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#define add_beep_ctls(codec) 0 -#define auto_parse_beep(codec) +#define auto_parse_beep(codec) 0 #endif /* check AA path's mute status */ @@ -403,30 +376,6 @@ static void analog_low_current_mode(struct hda_codec *codec) return __analog_low_current_mode(codec, false); } -static int via_build_controls(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err, i; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - - err = add_beep_ctls(codec); - if (err < 0) - return err; - - spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - - return 0; -} - static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream, @@ -481,7 +430,7 @@ static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) static int via_init(struct hda_codec *codec); static const struct hda_codec_ops via_patch_ops = { - .build_controls = via_build_controls, + .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = via_init, .free = via_free, @@ -545,16 +494,13 @@ static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { - { +static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Jack Detect", .count = 1, .info = snd_ctl_boolean_mono_info, .get = vt1708_jack_detect_get, .put = vt1708_jack_detect_put, - }, - {} /* terminator */ }; static const struct badness_table via_main_out_badness = { @@ -586,12 +532,17 @@ static int via_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; - auto_parse_beep(codec); - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); if (err < 0) return err; + err = auto_parse_beep(codec); + if (err < 0) + return err; + + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum)) + return -ENOMEM; + /* disable widget PM at start for compatibility */ codec->power_save_node = 0; spec->gen.power_down_unused = 0; @@ -600,12 +551,6 @@ static int via_parse_auto_config(struct hda_codec *codec) static int via_init(struct hda_codec *codec) { - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_iverbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - /* init power states */ __analog_low_current_mode(codec, true); @@ -623,7 +568,7 @@ static int vt1708_build_controls(struct hda_codec *codec) int err; int old_interval = codec->jackpoll_interval; codec->jackpoll_interval = msecs_to_jiffies(100); - err = via_build_controls(codec); + err = snd_hda_gen_build_controls(codec); codec->jackpoll_interval = old_interval; return err; } @@ -684,22 +629,29 @@ static int patch_vt1708(struct hda_codec *codec) vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); + err = snd_hda_add_verbs(codec, vt1708_init_verbs); + if (err < 0) + goto error; + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } + if (err < 0) + goto error; /* add jack detect on/off control */ - spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; - - spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) { + err = -ENOMEM; + goto error; + } /* clear jackpoll_interval again; it's set dynamically */ codec->jackpoll_interval = 0; return 0; + + error: + via_free(codec); + return err; } static int patch_vt1709(struct hda_codec *codec) @@ -715,12 +667,14 @@ static int patch_vt1709(struct hda_codec *codec) spec->gen.mixer_nid = 0x18; err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } static int patch_vt1708S(struct hda_codec *codec); @@ -741,12 +695,14 @@ static int patch_vt1708B(struct hda_codec *codec) /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } /* Patch for VT1708S */ @@ -791,16 +747,20 @@ static int patch_vt1708S(struct hda_codec *codec) if (codec->core.vendor_id == 0x11064397) snd_hda_codec_set_name(codec, "VT1705"); + err = snd_hda_add_verbs(codec, vt1708S_init_verbs); + if (err < 0) + goto error; + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } /* Patch for VT1702 */ @@ -832,16 +792,20 @@ static int patch_vt1702(struct hda_codec *codec) (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | (1 << AC_AMPCAP_MUTE_SHIFT)); + err = snd_hda_add_verbs(codec, vt1702_init_verbs); + if (err < 0) + goto error; + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } /* Patch for VT1718S */ @@ -904,16 +868,20 @@ static int patch_vt1718S(struct hda_codec *codec) override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); + err = snd_hda_add_verbs(codec, vt1718S_init_verbs); + if (err < 0) + goto error; + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } /* Patch for VT1716S */ @@ -955,9 +923,9 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, return 1; } -static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), - { +static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol = + HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT); +static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Digital Mic Capture Switch", .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, @@ -965,16 +933,12 @@ static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { .info = vt1716s_dmic_info, .get = vt1716s_dmic_get, .put = vt1716s_dmic_put, - }, - {} /* end */ }; /* mono-out mixer elements */ -static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { - HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), - { } /* end */ -}; +static const struct snd_kcontrol_new vt1716S_mono_out_mixer = + HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT); static const struct hda_verb vt1716S_init_verbs[] = { /* Enable Boost Volume backdoor */ @@ -1000,19 +964,27 @@ static int patch_vt1716S(struct hda_codec *codec) override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); + err = snd_hda_add_verbs(codec, vt1716S_init_verbs); + if (err < 0) + goto error; + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; + if (err < 0) + goto error; - spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; - spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) || + !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) || + !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) { + err = -ENOMEM; + goto error; + } return 0; + + error: + via_free(codec); + return err; } /* for vt2002P */ @@ -1107,19 +1079,23 @@ static int patch_vt2002P(struct hda_codec *codec) snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - /* automatic parse from the BIOS config */ - err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - if (spec->codec_type == VT1802) - spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; + err = snd_hda_add_verbs(codec, vt1802_init_verbs); else - spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; + err = snd_hda_add_verbs(codec, vt2002P_init_verbs); + if (err < 0) + goto error; + + /* automatic parse from the BIOS config */ + err = via_parse_auto_config(codec); + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } /* for vt1812 */ @@ -1148,16 +1124,20 @@ static int patch_vt1812(struct hda_codec *codec) override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); + err = snd_hda_add_verbs(codec, vt1812_init_verbs); + if (err < 0) + goto error; + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } /* patch for vt3476 */ @@ -1185,16 +1165,20 @@ static int patch_vt3476(struct hda_codec *codec) spec->gen.mixer_nid = 0x3f; add_secret_dac_path(codec); + err = snd_hda_add_verbs(codec, vt3476_init_verbs); + if (err < 0) + goto error; + /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); - if (err < 0) { - via_free(codec); - return err; - } - - spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; + if (err < 0) + goto error; return 0; + + error: + via_free(codec); + return err; } /* diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 65bb3ac6af4c..97f49b751e6e 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -27,17 +27,11 @@ static void update_tpacpi_mute_led(void *private_data, int enabled) led_set_func(TPACPI_LED_MUTE, !enabled); } -static void update_tpacpi_micmute_led(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void update_tpacpi_micmute(struct hda_codec *codec) { - if (!ucontrol || !led_set_func) - return; - if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { - /* TODO: How do I verify if it's a mono or stereo here? */ - bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1]; - led_set_func(TPACPI_LED_MICMUTE, !val); - } + struct hda_gen_spec *spec = codec->spec; + + led_set_func(TPACPI_LED_MICMUTE, spec->micmute_led.led_value); } static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, @@ -63,15 +57,10 @@ static void hda_fixup_thinkpad_acpi(struct hda_codec *codec, spec->vmaster_mute.hook = update_tpacpi_mute_led; removefunc = false; } - if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) { - if (spec->num_adc_nids > 1 && !spec->dyn_adc_switch) - codec_dbg(codec, - "Skipping micmute LED control due to several ADCs"); - else { - spec->cap_sync_hook = update_tpacpi_micmute_led; - removefunc = false; - } - } + if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0 && + snd_hda_gen_add_micmute_led(codec, + update_tpacpi_micmute) > 0) + removefunc = false; } if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { |