From 45eebda7b4a73deb268b0cbcde06b603a2ba46a2 Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Tue, 26 Jul 2011 16:56:20 -0500 Subject: ALSA: hda - Add support for vref-out based mute LED control on IDT codecs This patch also registers all necessary callbacks to support mute LED only when such control is enabled. And it keeps codec AFG in D0 or D1 state all the time when aggressive power managemnt is enabled for vref-out control (and mute LED) work correctly. Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 199 ++++++++++++++++++++++++++++++++--------- 1 file changed, 156 insertions(+), 43 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fcf4c7142103..aa376b59c006 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -213,6 +213,7 @@ struct sigmatel_spec { unsigned int gpio_mute; unsigned int gpio_led; unsigned int gpio_led_polarity; + unsigned int vref_led; /* stream */ unsigned int stream_delay; @@ -672,6 +673,30 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; } +static int stac_vrefout_set(struct hda_codec *codec, + hda_nid_t nid, unsigned int new_vref) +{ + int error, pinctl; + + snd_printdd("%s, nid %x ctl %x\n", __func__, nid, new_vref); + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + if (pinctl < 0) + return pinctl; + + pinctl &= 0xff; + pinctl &= ~AC_PINCTL_VREFEN; + pinctl |= (new_vref & AC_PINCTL_VREFEN); + + error = snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + if (error < 0) + return error; + + return 1; +} + static unsigned int stac92xx_vref_set(struct hda_codec *codec, hda_nid_t nid, unsigned int new_vref) { @@ -4069,6 +4094,8 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, { unsigned int gpiostate, gpiomask, gpiodir; + snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DATA, 0); gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); @@ -4258,10 +4285,12 @@ static void stac_store_hints(struct hda_codec *codec) spec->eapd_switch = val; get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity); if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - if (spec->gpio_led_polarity) - spec->gpio_data |= spec->gpio_led; + if (spec->gpio_led <= 8) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + if (spec->gpio_led_polarity) + spec->gpio_data |= spec->gpio_led; + } } } @@ -4431,11 +4460,26 @@ static void stac92xx_free_kctls(struct hda_codec *codec) snd_array_free(&spec->kctls); } +static void stac92xx_shutup_pins(struct hda_codec *codec) +{ + unsigned int i, def_conf; + + if (codec->bus->shutdown) + return; + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) + snd_hda_codec_write(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } +} + static void stac92xx_shutup(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - snd_hda_shutup_pins(codec); + stac92xx_shutup_pins(codec); if (spec->eapd_mask) stac_gpio_set(codec, spec->gpio_mask, @@ -4833,10 +4877,11 @@ static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity) if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) { while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%d_%d", + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &spec->gpio_led_polarity, &spec->gpio_led) == 2) { - spec->gpio_led = 1 << spec->gpio_led; + if (spec->gpio_led < 4) + spec->gpio_led = 1 << spec->gpio_led; return 1; } if (sscanf(dev->name, "HP_Mute_LED_%d", @@ -4935,17 +4980,6 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer, #endif #ifdef CONFIG_PM -static int stac92xx_pre_resume(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - /* sync mute LED */ - if (spec->gpio_led) - stac_gpio_set(codec, spec->gpio_mask, - spec->gpio_dir, spec->gpio_data); - return 0; -} - static int stac92xx_resume(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4964,7 +4998,65 @@ static int stac92xx_resume(struct hda_codec *codec) return 0; } +static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +{ + stac92xx_shutup(codec); + return 0; +} + #ifdef CONFIG_SND_HDA_POWER_SAVE +static int stac92xx_pre_resume(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + /* sync mute LED */ + if (spec->gpio_led) { + if (spec->gpio_led <= 8) { + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); + } else { + stac_vrefout_set(codec, + spec->gpio_led, spec->vref_led); + } + } + return 0; +} + +static int stac92xx_post_suspend(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + if (spec->gpio_led > 8) { + /* with vref-out pin used for mute led control + * codec AFG is prevented from D3 state, but on + * system suspend it can (and should) be used + */ + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + } + return 0; +} + +static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + unsigned int afg_power_state = power_state; + struct sigmatel_spec *spec = codec->spec; + + if (power_state == AC_PWRST_D3) { + if (spec->gpio_led > 8) { + /* with vref-out pin used for mute led control + * codec AFG is prevented from D3 state + */ + afg_power_state = AC_PWRST_D1; + } + /* this delay seems necessary to avoid click noise at power-down */ + msleep(100); + } + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, + afg_power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); +} + /* * For this feature CONFIG_SND_HDA_POWER_SAVE is needed * as mute LED state is updated in check_power_status hook @@ -4973,8 +5065,12 @@ static int stac92xx_update_led_status(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; int i, num_ext_dacs, muted = 1; + unsigned int muted_lvl, notmtd_lvl; hda_nid_t nid; + if (!spec->gpio_led) + return 0; + for (i = 0; i < spec->multiout.num_dacs; i++) { nid = spec->multiout.dac_nids[i]; if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & @@ -4999,17 +5095,27 @@ static int stac92xx_update_led_status(struct hda_codec *codec) muted = 0; /* extra output is not muted */ } } - if (muted) - spec->gpio_data &= ~spec->gpio_led; /* orange */ - else - spec->gpio_data |= spec->gpio_led; /* white */ + /*polarity defines *not* muted state level*/ + if (spec->gpio_led <= 8) { + if (muted) + spec->gpio_data &= ~spec->gpio_led; /* orange */ + else + spec->gpio_data |= spec->gpio_led; /* white */ - if (!spec->gpio_led_polarity) { - /* LED state is inverted on these systems */ - spec->gpio_data ^= spec->gpio_led; + if (!spec->gpio_led_polarity) { + /* LED state is inverted on these systems */ + spec->gpio_data ^= spec->gpio_led; + } + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); + } else { + notmtd_lvl = spec->gpio_led_polarity ? + AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_GRD; + muted_lvl = spec->gpio_led_polarity ? + AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ; + spec->vref_led = muted ? muted_lvl : notmtd_lvl; + stac_vrefout_set(codec, spec->gpio_led, spec->vref_led); } - - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); return 0; } @@ -5023,13 +5129,7 @@ static int stac92xx_check_power_status(struct hda_codec *codec, return 0; } -#endif - -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) -{ - stac92xx_shutup(codec); - return 0; -} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ #endif /* CONFIG_PM */ static const struct hda_codec_ops stac92xx_patch_ops = { @@ -5041,7 +5141,6 @@ static const struct hda_codec_ops stac92xx_patch_ops = { #ifdef CONFIG_PM .suspend = stac92xx_suspend, .resume = stac92xx_resume, - .pre_resume = stac92xx_pre_resume, #endif .reboot_notify = stac92xx_shutup, }; @@ -5555,10 +5654,17 @@ again: #ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - /* register check_power_status callback. */ + if (spec->gpio_led <= 8) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + } else { + codec->patch_ops.set_power_state = + stac92xx_set_power_state; + codec->patch_ops.post_suspend = + stac92xx_post_suspend; + } + codec->patch_ops.pre_resume = stac92xx_pre_resume; codec->patch_ops.check_power_status = stac92xx_check_power_status; } @@ -5883,10 +5989,17 @@ again: #ifdef CONFIG_SND_HDA_POWER_SAVE if (spec->gpio_led) { - spec->gpio_mask |= spec->gpio_led; - spec->gpio_dir |= spec->gpio_led; - spec->gpio_data |= spec->gpio_led; - /* register check_power_status callback. */ + if (spec->gpio_led <= 8) { + spec->gpio_mask |= spec->gpio_led; + spec->gpio_dir |= spec->gpio_led; + spec->gpio_data |= spec->gpio_led; + } else { + codec->patch_ops.set_power_state = + stac92xx_set_power_state; + codec->patch_ops.post_suspend = + stac92xx_post_suspend; + } + codec->patch_ops.pre_resume = stac92xx_pre_resume; codec->patch_ops.check_power_status = stac92xx_check_power_status; } -- cgit v1.2.3 From 767cd365b22820df07b962b49ce04b220b98e537 Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Wed, 27 Jul 2011 20:03:51 +1200 Subject: ALSA: asihpi - bug fix pa use before init. Fixes bug introduced by 1c073b67. Also declare pa local to block in which it is used. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpioctl.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 65fcf4770731..e0cff0c72e51 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -107,7 +107,6 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) union hpi_response_buffer_v1 *hr; u16 res_max_size; u32 uncopied_bytes; - struct hpi_adapter *pa = NULL; int err = 0; if (cmd != HPI_IOCTL_LINUX) @@ -182,6 +181,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* -1=no data 0=read from user mem, 1=write to user mem */ int wrflag = -1; u32 adapter = hm->h.adapter_index; + struct hpi_adapter *pa = &adapters[adapter]; if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) { hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER, @@ -197,9 +197,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } - pa = &adapters[adapter]; - - if (mutex_lock_interruptible(&adapters[adapter].mutex)) { + if (mutex_lock_interruptible(&pa->mutex)) { err = -EINTR; goto out; } @@ -235,8 +233,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) "stream buffer size %d\n", size); - mutex_unlock(&adapters - [adapter].mutex); + mutex_unlock(&pa->mutex); err = -EINVAL; goto out; } @@ -277,7 +274,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) uncopied_bytes, size); } - mutex_unlock(&adapters[adapter].mutex); + mutex_unlock(&pa->mutex); } /* on return response size must be set */ -- cgit v1.2.3 From 60a6a8425a84fa46a3831ce79197640b8224311b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 27 Jul 2011 14:01:24 +0200 Subject: ALSA: hda - Fix Oops with Realtek quirks with NULL adc_nids Somce quirk models don't set adc_nids but let the parser filling it. But the recent code has unnecessary NULL-checks of spec->input_mux, and it resulted in NULL dereferences. This patch fixes that regression. Reported-and-tested-by: Oliver Neukum Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 694327ae8b71..0383dd8a11df 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1911,7 +1911,7 @@ static int alc_build_controls(struct hda_codec *codec) return err; } } - if (spec->cap_mixer) { + if (spec->cap_mixer && spec->adc_nids) { const char *kname = kctl ? kctl->id.name : NULL; for (knew = spec->cap_mixer; knew->name; knew++) { if (kname && strcmp(knew->name, kname) == 0) @@ -3677,7 +3677,7 @@ static int patch_alc880(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc880_presets[board_config]); - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -3804,7 +3804,7 @@ static int patch_alc260(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc260_presets[board_config]); - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -3983,7 +3983,7 @@ static int patch_alc882(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc882_presets[board_config]); - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -4137,7 +4137,7 @@ static int patch_alc262(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc262_presets[board_config]); - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -4293,7 +4293,7 @@ static int patch_alc268(struct hda_codec *codec) (0 << AC_AMPCAP_MUTE_SHIFT)); } - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -4705,7 +4705,7 @@ static int patch_alc269(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc269_presets[board_config]); - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -4843,7 +4843,7 @@ static int patch_alc861(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc861_presets[board_config]); - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -4984,7 +4984,7 @@ static int patch_alc861vd(struct hda_codec *codec) add_verb(spec, alc660vd_eapd_verbs); } - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -5200,7 +5200,7 @@ static int patch_alc662(struct hda_codec *codec) if (board_config != ALC_MODEL_AUTO) setup_preset(codec, &alc662_presets[board_config]); - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); @@ -5336,7 +5336,7 @@ static int patch_alc680(struct hda_codec *codec) #endif } - if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { + if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); alc_rebuild_imux_for_auto_mic(codec); alc_remove_invalid_adc_nids(codec); -- cgit v1.2.3 From ae6ff61e43fe4f348a7f764ff0c13fb4240da7b8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 Jul 2011 15:02:26 +0300 Subject: ALSA: asihpi - off by one in asihpi_hpi_ioctl() "adapter" is used as an array index in the adapters[] array so the off by one would make us read past the end. 1c073b67979 "ALSA: asihpi - Remove spurious adapter index check" reverted Dan Rosenberg's check that would have prevented the overflow here. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index e0cff0c72e51..9683f84ecdc8 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -183,7 +183,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) u32 adapter = hm->h.adapter_index; struct hpi_adapter *pa = &adapters[adapter]; - if ((adapter > HPI_MAX_ADAPTERS) || (!pa->type)) { + if ((adapter >= HPI_MAX_ADAPTERS) || (!pa->type)) { hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER, HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER_NUMBER); -- cgit v1.2.3 From c48a8fb0d31d6147d8d76b8e2ad7f51a2fbb5c4d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 27 Jul 2011 16:41:57 +0200 Subject: ALSA: hda - Fix duplicated DAC assignments for Realtek Copying hp_pins and speaker_pins from line_out_pins may confuse the parser, and it can lead to duplicated initializations for the same pin with a wrong DAC assignment. The problem appears in 3.0 kernel code. Cc: (for 3.0) Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0383dd8a11df..e125c60fe352 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -895,13 +895,15 @@ static void alc_init_auto_hp(struct hda_codec *codec) if (present == 3) spec->automute_hp_lo = 1; /* both HP and LO automute */ - if (!cfg->speaker_pins[0]) { + if (!cfg->speaker_pins[0] && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { memcpy(cfg->speaker_pins, cfg->line_out_pins, sizeof(cfg->speaker_pins)); cfg->speaker_outs = cfg->line_outs; } - if (!cfg->hp_pins[0]) { + if (!cfg->hp_pins[0] && + cfg->line_out_type == AUTO_PIN_HP_OUT) { memcpy(cfg->hp_pins, cfg->line_out_pins, sizeof(cfg->hp_pins)); cfg->hp_outs = cfg->line_outs; @@ -920,6 +922,7 @@ static void alc_init_auto_hp(struct hda_codec *codec) spec->automute_mode = ALC_AUTOMUTE_PIN; } if (spec->automute && cfg->line_out_pins[0] && + cfg->speaker_pins[0] && cfg->line_out_pins[0] != cfg->hp_pins[0] && cfg->line_out_pins[0] != cfg->speaker_pins[0]) { for (i = 0; i < cfg->line_outs; i++) { -- cgit v1.2.3