diff options
author | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:05 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2011-09-15 15:08:18 +0200 |
commit | e060c38434b2caa78efe7cedaff4191040b65a15 (patch) | |
tree | 407361230bf6733f63d8e788e4b5e6566ee04818 /sound/pci/hda | |
parent | 10e4ac572eeffe5317019bd7330b6058a400dfc2 (diff) | |
parent | cc39c6a9bbdebfcf1a7dee64d83bf302bc38d941 (diff) |
Merge branch 'master' into for-next
Fast-forward merge with Linus to be able to merge patches
based on more recent version of the tree.
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/Kconfig | 1 | ||||
-rw-r--r-- | sound/pci/hda/alc268_quirks.c | 36 | ||||
-rw-r--r-- | sound/pci/hda/alc269_quirks.c | 7 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 114 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 15 | ||||
-rw-r--r-- | sound/pci/hda/hda_eld.c | 31 | ||||
-rw-r--r-- | sound/pci/hda/hda_local.h | 2 | ||||
-rw-r--r-- | sound/pci/hda/patch_analog.c | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 741 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 71 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 100 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 289 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 6 |
13 files changed, 1178 insertions, 239 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7489b460855..bb7e102d672 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -243,6 +243,7 @@ config SND_HDA_GENERIC config SND_HDA_POWER_SAVE bool "Aggressive power-saving on HD-audio" + depends on PM help Say Y here to enable more aggressive power-saving mode on HD-audio driver. The power-saving timeout can be configured diff --git a/sound/pci/hda/alc268_quirks.c b/sound/pci/hda/alc268_quirks.c index be58bf2f3ae..2e5876ce71f 100644 --- a/sound/pci/hda/alc268_quirks.c +++ b/sound/pci/hda/alc268_quirks.c @@ -476,8 +476,8 @@ static const struct snd_pci_quirk alc268_ssid_cfg_tbl[] = { static const struct alc_config_preset alc268_presets[] = { [ALC267_QUANTA_IL1] = { - .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer, - alc268_capture_nosrc_mixer }, + .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer }, + .cap_mixer = alc268_capture_nosrc_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc267_quanta_il1_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -492,8 +492,8 @@ static const struct alc_config_preset alc268_presets[] = { .init_hook = alc_inithook, }, [ALC268_3ST] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, + .mixers = { alc268_base_mixer, alc268_beep_mixer }, + .cap_mixer = alc268_capture_alt_mixer, .init_verbs = { alc268_base_init_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), .dac_nids = alc268_dac_nids, @@ -507,8 +507,8 @@ static const struct alc_config_preset alc268_presets[] = { .input_mux = &alc268_capture_source, }, [ALC268_TOSHIBA] = { - .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, + .mixers = { alc268_toshiba_mixer, alc268_beep_mixer }, + .cap_mixer = alc268_capture_alt_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_toshiba_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -525,8 +525,8 @@ static const struct alc_config_preset alc268_presets[] = { .init_hook = alc_inithook, }, [ALC268_ACER] = { - .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, + .mixers = { alc268_acer_mixer, alc268_beep_mixer }, + .cap_mixer = alc268_capture_alt_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_acer_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -543,8 +543,8 @@ static const struct alc_config_preset alc268_presets[] = { .init_hook = alc_inithook, }, [ALC268_ACER_DMIC] = { - .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, + .mixers = { alc268_acer_dmic_mixer, alc268_beep_mixer }, + .cap_mixer = alc268_capture_alt_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_acer_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -561,9 +561,8 @@ static const struct alc_config_preset alc268_presets[] = { .init_hook = alc_inithook, }, [ALC268_ACER_ASPIRE_ONE] = { - .mixers = { alc268_acer_aspire_one_mixer, - alc268_beep_mixer, - alc268_capture_nosrc_mixer }, + .mixers = { alc268_acer_aspire_one_mixer, alc268_beep_mixer}, + .cap_mixer = alc268_capture_nosrc_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_acer_aspire_one_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -579,8 +578,8 @@ static const struct alc_config_preset alc268_presets[] = { .init_hook = alc_inithook, }, [ALC268_DELL] = { - .mixers = { alc268_dell_mixer, alc268_beep_mixer, - alc268_capture_nosrc_mixer }, + .mixers = { alc268_dell_mixer, alc268_beep_mixer}, + .cap_mixer = alc268_capture_nosrc_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_dell_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -596,8 +595,8 @@ static const struct alc_config_preset alc268_presets[] = { .init_hook = alc_inithook, }, [ALC268_ZEPTO] = { - .mixers = { alc268_base_mixer, alc268_capture_alt_mixer, - alc268_beep_mixer }, + .mixers = { alc268_base_mixer, alc268_beep_mixer }, + .cap_mixer = alc268_capture_alt_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_toshiba_verbs }, .num_dacs = ARRAY_SIZE(alc268_dac_nids), @@ -616,7 +615,8 @@ static const struct alc_config_preset alc268_presets[] = { }, #ifdef CONFIG_SND_DEBUG [ALC268_TEST] = { - .mixers = { alc268_test_mixer, alc268_capture_mixer }, + .mixers = { alc268_test_mixer }, + .cap_mixer = alc268_capture_mixer, .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, alc268_volume_init_verbs, alc268_beep_init_verbs }, diff --git a/sound/pci/hda/alc269_quirks.c b/sound/pci/hda/alc269_quirks.c index 14fdcf29b15..5ac0e2162a4 100644 --- a/sound/pci/hda/alc269_quirks.c +++ b/sound/pci/hda/alc269_quirks.c @@ -531,17 +531,10 @@ static const struct snd_pci_quirk alc269_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC), SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC), SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC), SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC), SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC), SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC), SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC), - SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901", - ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", - ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC), - SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC), SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK), SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC), diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 9c27a3a4c4d..3e7850c238c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -91,8 +91,10 @@ EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset); #ifdef CONFIG_SND_HDA_POWER_SAVE static void hda_power_work(struct work_struct *work); static void hda_keep_power_on(struct hda_codec *codec); +#define hda_codec_is_power_on(codec) ((codec)->power_on) #else static inline void hda_keep_power_on(struct hda_codec *codec) {} +#define hda_codec_is_power_on(codec) 1 #endif /** @@ -1101,7 +1103,7 @@ void snd_hda_shutup_pins(struct hda_codec *codec) } EXPORT_SYMBOL_HDA(snd_hda_shutup_pins); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ static void restore_shutup_pins(struct hda_codec *codec) { @@ -1499,7 +1501,7 @@ static void purify_inactive_streams(struct hda_codec *codec) } } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* clean up all streams; called from suspend */ static void hda_cleanup_all_streams(struct hda_codec *codec) { @@ -1838,7 +1840,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec @@ -1868,7 +1870,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) } } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) @@ -3082,7 +3084,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* * command cache */ @@ -3199,53 +3201,32 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, seq->param); } EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ -/* - * set power state of the codec - */ -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) +void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state, + bool eapd_workaround) { - hda_nid_t nid; + hda_nid_t nid = codec->start_nid; int i; - /* this delay seems necessary to avoid click noise at power-down */ - if (power_state == AC_PWRST_D3) - msleep(100); - snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, - power_state); - /* partial workaround for "azx_get_response timeout" */ - if (power_state == AC_PWRST_D0 && - (codec->vendor_id & 0xffff0000) == 0x14f10000) - msleep(10); - - nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int wcaps = get_wcaps(codec, nid); - if (wcaps & AC_WCAP_POWER) { - unsigned int wid_type = get_wcaps_type(wcaps); - if (power_state == AC_PWRST_D3 && - wid_type == AC_WID_PIN) { - unsigned int pincap; - /* - * don't power down the widget if it controls - * eapd and EAPD_BTLENABLE is set. - */ - pincap = snd_hda_query_pin_caps(codec, nid); - if (pincap & AC_PINCAP_EAPD) { - int eapd = snd_hda_codec_read(codec, - nid, 0, + if (!(wcaps & AC_WCAP_POWER)) + continue; + /* don't power down the widget if it controls eapd and + * EAPD_BTLENABLE is set. + */ + if (eapd_workaround && power_state == AC_PWRST_D3 && + get_wcaps_type(wcaps) == AC_WID_PIN && + (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { + int eapd = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_EAPD_BTLENABLE, 0); - eapd &= 0x02; - if (eapd) - continue; - } - } - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - power_state); + if (eapd & 0x02) + continue; } + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, + power_state); } if (power_state == AC_PWRST_D0) { @@ -3262,6 +3243,26 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, } while (time_after_eq(end_time, jiffies)); } } +EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); + +/* + * set power state of the codec + */ +static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + if (codec->patch_ops.set_power_state) { + codec->patch_ops.set_power_state(codec, fg, power_state); + return; + } + + /* this delay seems necessary to avoid click noise at power-down */ + if (power_state == AC_PWRST_D3) + msleep(100); + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, + power_state); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); +} #ifdef CONFIG_SND_HDA_HWDEP /* execute additional init verbs */ @@ -3274,7 +3275,7 @@ static void hda_exec_init_verbs(struct hda_codec *codec) static inline void hda_exec_init_verbs(struct hda_codec *codec) {} #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM /* * call suspend and power-down; used both from PM and power-save */ @@ -3315,7 +3316,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) snd_hda_codec_resume_cache(codec); } } -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ /** @@ -4071,9 +4072,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); #ifdef CONFIG_SND_HDA_POWER_SAVE -static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state); - static void hda_power_work(struct work_struct *work) { struct hda_codec *codec = @@ -4376,11 +4374,8 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus) if (!bus) return; list_for_each_entry(codec, &bus->codec_list, list) { -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!codec->power_on) - continue; -#endif - if (codec->patch_ops.reboot_notify) + if (hda_codec_is_power_on(codec) && + codec->patch_ops.reboot_notify) codec->patch_ops.reboot_notify(codec); } } @@ -5079,11 +5074,10 @@ int snd_hda_suspend(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!codec->power_on) - continue; -#endif - hda_call_codec_suspend(codec); + if (hda_codec_is_power_on(codec)) + hda_call_codec_suspend(codec); + if (codec->patch_ops.post_suspend) + codec->patch_ops.post_suspend(codec); } return 0; } @@ -5103,6 +5097,8 @@ int snd_hda_resume(struct hda_bus *bus) struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { + if (codec->patch_ops.pre_resume) + codec->patch_ops.pre_resume(codec); if (snd_hda_codec_needs_resume(codec)) hda_call_codec_resume(codec); } diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index f465e07a487..755f2b0f9d8 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -26,10 +26,6 @@ #include <sound/pcm.h> #include <sound/hwdep.h> -#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE) -#define SND_HDA_NEEDS_RESUME /* resume control code is required */ -#endif - /* * nodes */ @@ -704,8 +700,12 @@ struct hda_codec_ops { int (*init)(struct hda_codec *codec); void (*free)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); -#ifdef SND_HDA_NEEDS_RESUME + void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state); +#ifdef CONFIG_PM int (*suspend)(struct hda_codec *codec, pm_message_t state); + int (*post_suspend)(struct hda_codec *codec); + int (*pre_resume)(struct hda_codec *codec); int (*resume)(struct hda_codec *codec); #endif #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -927,7 +927,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); /* cached write */ -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_sequence_write_cache(struct hda_codec *codec, @@ -1008,6 +1008,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, */ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); void snd_hda_bus_reboot_notify(struct hda_bus *bus); +void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state, + bool eapd_workaround); /* * power management diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 28ce17d09c3..c34f730f481 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -144,25 +144,17 @@ static int cea_sampling_frequencies[8] = { SNDRV_PCM_RATE_192000, /* 7: 192000Hz */ }; -static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid, +static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid, int byte_index) { unsigned int val; val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD, byte_index); - #ifdef BE_PARANOID printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val); #endif - - if ((val & AC_ELDD_ELD_VALID) == 0) { - snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n", - byte_index); - val = 0; - } - - return val & AC_ELDD_ELD_DATA; + return val; } #define GRAB_BITS(buf, byte, lowbit, bits) \ @@ -344,11 +336,26 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld, if (!buf) return -ENOMEM; - for (i = 0; i < size; i++) - buf[i] = hdmi_get_eld_byte(codec, nid, i); + for (i = 0; i < size; i++) { + unsigned int val = hdmi_get_eld_data(codec, nid, i); + if (!(val & AC_ELDD_ELD_VALID)) { + if (!i) { + snd_printd(KERN_INFO + "HDMI: invalid ELD data\n"); + ret = -EINVAL; + goto error; + } + snd_printd(KERN_INFO + "HDMI: invalid ELD data byte %d\n", i); + val = 0; + } else + val &= AC_ELDD_ELD_DATA; + buf[i] = val; + } ret = hdmi_update_eld(eld, buf, size); +error: kfree(buf); return ret; } diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 88b277e9740..2e7ac31afa8 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -131,7 +131,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM void snd_hda_codec_resume_amp(struct hda_codec *codec); #endif diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 1362c8ba4d1..8648917acff 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -563,7 +563,7 @@ static void ad198x_free(struct hda_codec *codec) snd_hda_detach_beep_device(codec); } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int ad198x_suspend(struct hda_codec *codec, pm_message_t state) { ad198x_shutup(codec); @@ -579,7 +579,7 @@ static const struct hda_codec_ops ad198x_patch_ops = { #ifdef CONFIG_SND_HDA_POWER_SAVE .check_power_status = ad198x_check_power_status, #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM .suspend = ad198x_suspend, #endif .reboot_notify = ad198x_shutup, diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 7f93739b1e3..d6c93d92b55 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -25,6 +25,7 @@ #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" +#include <sound/tlv.h> /* */ @@ -61,9 +62,15 @@ struct cs_spec { unsigned int hp_detect:1; unsigned int mic_detect:1; + /* CS421x */ + unsigned int spdif_detect:1; + unsigned int sense_b:1; + hda_nid_t vendor_nid; + struct hda_input_mux input_mux; + unsigned int last_input; }; -/* available models */ +/* available models with CS420x */ enum { CS420X_MBP53, CS420X_MBP55, @@ -72,6 +79,12 @@ enum { CS420X_MODELS }; +/* CS421x boards */ +enum { + CS421X_CDB4210, + CS421X_MODELS +}; + /* Vendor-specific processing widget */ #define CS420X_VENDOR_NID 0x11 #define CS_DIG_OUT1_PIN_NID 0x10 @@ -111,21 +124,42 @@ enum { /* 0x0009 - 0x0014 -> 12 test regs */ /* 0x0015 - visibility reg */ +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Trasmitter(sense) +*/ +#define CS4210_DAC_NID 0x02 +#define CS4210_ADC_NID 0x03 +#define CS421X_VENDOR_NID 0x0B +#define CS421X_DMIC_PIN_NID 0x09 /* Port E */ +#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */ + +#define CS421X_IDX_DEV_CFG 0x01 +#define CS421X_IDX_ADC_CFG 0x02 +#define CS421X_IDX_DAC_CFG 0x03 +#define CS421X_IDX_SPK_CTL 0x04 + +#define SPDIF_EVENT 0x04 static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) { - snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + struct cs_spec *spec = codec->spec; + snd_hda_codec_write(codec, spec->vendor_nid, 0, AC_VERB_SET_COEF_INDEX, idx); - return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0, + return snd_hda_codec_read(codec, spec->vendor_nid, 0, AC_VERB_GET_PROC_COEF, 0); } static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, unsigned int coef) { - snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + struct cs_spec *spec = codec->spec; + snd_hda_codec_write(codec, spec->vendor_nid, 0, AC_VERB_SET_COEF_INDEX, idx); - snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0, + snd_hda_codec_write(codec, spec->vendor_nid, 0, AC_VERB_SET_PROC_COEF, coef); } @@ -341,17 +375,16 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx) static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, unsigned int *idxp) { - int i; + int i, idx; hda_nid_t nid; nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { unsigned int type; - int idx; type = get_wcaps_type(get_wcaps(codec, nid)); if (type != AC_WID_AUD_IN) continue; - idx = snd_hda_get_conn_index(codec, nid, pin, 0); + idx = snd_hda_get_conn_index(codec, nid, pin, false); if (idx >= 0) { *idxp = idx; return nid; @@ -835,6 +868,8 @@ static int build_digital_input(struct hda_codec *codec) /* * auto-mute and auto-mic switching + * CS421x auto-output redirecting + * HP/SPK/SPDIF */ static void cs_automute(struct hda_codec *codec) @@ -842,9 +877,25 @@ static void cs_automute(struct hda_codec *codec) struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int hp_present; + unsigned int spdif_present; hda_nid_t nid; int i; + spdif_present = 0; + if (cfg->dig_outs) { + nid = cfg->dig_out_pins[0]; + if (is_jack_detectable(codec, nid)) { + /* + TODO: SPDIF output redirect when SENSE_B is enabled. + Shared (SENSE_A) jack (e.g HP/mini-TOSLINK) + assumed. + */ + if (snd_hda_jack_detect(codec, nid) + /* && spec->sense_b */) + spdif_present = 1; + } + } + hp_present = 0; for (i = 0; i < cfg->hp_outs; i++) { nid = cfg->hp_pins[i]; @@ -854,11 +905,19 @@ static void cs_automute(struct hda_codec *codec) if (hp_present) break; } + + /* mute speakers if spdif or hp jack is plugged in */ for (i = 0; i < cfg->speaker_outs; i++) { nid = cfg->speaker_pins[i]; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, hp_present ? 0 : PIN_OUT); + /* detect on spdif is specific to CS421x */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spdif_present ? 0 : PIN_OUT); + } } if (spec->board_config == CS420X_MBP53 || spec->board_config == CS420X_MBP55 || @@ -867,21 +926,62 @@ static void cs_automute(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, gpio); } + + /* specific to CS421x */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + /* mute HPs if spdif jack (SENSE_B) is present */ + for (i = 0; i < cfg->hp_outs; i++) { + nid = cfg->hp_pins[i]; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + (spdif_present && spec->sense_b) ? 0 : PIN_HP); + } + + /* SPDIF TX on/off */ + if (cfg->dig_outs) { + nid = cfg->dig_out_pins[0]; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + spdif_present ? PIN_OUT : 0); + + } + /* Update board GPIOs if neccessary ... */ + } } +/* + * Auto-input redirect for CS421x + * Switch max 3 inputs of a single ADC (nid 3) +*/ + static void cs_automic(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid; unsigned int present; - + nid = cfg->inputs[spec->automic_idx].pin; present = snd_hda_jack_detect(codec, nid); - if (present) - change_cur_input(codec, spec->automic_idx, 0); - else - change_cur_input(codec, !spec->automic_idx, 0); + + /* specific to CS421x, single ADC */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + if (present) { + spec->last_input = spec->cur_input; + spec->cur_input = spec->automic_idx; + } else { + spec->cur_input = spec->last_input; + } + + snd_hda_codec_write_cache(codec, spec->cur_adc, 0, + AC_VERB_SET_CONNECT_SEL, + spec->adc_idx[spec->cur_input]); + } else { + if (present) + change_cur_input(codec, spec->automic_idx, 0); + else + change_cur_input(codec, !spec->automic_idx, 0); + } } /* @@ -911,23 +1011,28 @@ static void init_output(struct hda_codec *codec) for (i = 0; i < cfg->line_outs; i++) snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + /* HP */ for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); if (!cfg->speaker_outs) continue; - if (is_jack_detectable(codec, nid)) { + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | HP_EVENT); spec->hp_detect = 1; } } + + /* Speaker */ for (i = 0; i < cfg->speaker_outs; i++) snd_hda_codec_write(codec, cfg->speaker_pins[i], 0, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - if (spec->hp_detect) + + /* SPDIF is enabled on presence detect for CS421x */ + if (spec->hp_detect || spec->spdif_detect) cs_automute(codec); } @@ -961,19 +1066,31 @@ static void init_input(struct hda_codec *codec) AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | MIC_EVENT); } - change_cur_input(codec, spec->cur_input, 1); - if (spec->mic_detect) - cs_automic(codec); - - coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */ - if (is_active_pin(codec, CS_DMIC2_PIN_NID)) - coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */ - if (is_active_pin(codec, CS_DMIC1_PIN_NID)) - coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 - * No effect if SPDIF_OUT2 is selected in - * IDX_SPDIF_CTL. - */ - cs_vendor_coef_set(codec, IDX_ADC_CFG, coef); + /* specific to CS421x */ + if (spec->vendor_nid == CS421X_VENDOR_NID) { + if (spec->mic_detect) + cs_automic(codec); + else { + spec->cur_adc = spec->adc_nid[spec->cur_input]; + snd_hda_codec_write(codec, spec->cur_adc, 0, + AC_VERB_SET_CONNECT_SEL, + spec->adc_idx[spec->cur_input]); + } + } else { + change_cur_input(codec, spec->cur_input, 1); + if (spec->mic_detect) + cs_automic(codec); + + coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */ + if (is_active_pin(codec, CS_DMIC2_PIN_NID)) + coef |= 0x0500; /* DMIC2 2 chan on, GPIO1 off */ + if (is_active_pin(codec, CS_DMIC1_PIN_NID)) + coef |= 0x1800; /* DMIC1 2 chan on, GPIO0 off + * No effect if SPDIF_OUT2 is + * selected in IDX_SPDIF_CTL. + */ + cs_vendor_coef_set(codec, IDX_ADC_CFG, coef); + } } static const struct hda_verb cs_coef_init_verbs[] = { @@ -1221,16 +1338,16 @@ static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { [CS420X_IMAC27] = imac27_pincfgs, }; -static void fix_pincfg(struct hda_codec *codec, int model) +static void fix_pincfg(struct hda_codec *codec, int model, + const struct cs_pincfg **pin_configs) { - const struct cs_pincfg *cfg = cs_pincfgs[model]; + const struct cs_pincfg *cfg = pin_configs[model]; if (!cfg) return; for (; cfg->nid; cfg++) snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); } - static int patch_cs420x(struct hda_codec *codec) { struct cs_spec *spec; @@ -1241,11 +1358,13 @@ static int patch_cs420x(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + spec->vendor_nid = CS420X_VENDOR_NID; + spec->board_config = snd_hda_check_board_config(codec, CS420X_MODELS, cs420x_models, cs420x_cfg_tbl); if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config); + fix_pincfg(codec, spec->board_config, cs_pincfgs); switch (spec->board_config) { case CS420X_IMAC27: @@ -1272,6 +1391,562 @@ static int patch_cs420x(struct hda_codec *codec) return err; } +/* + * Cirrus Logic CS4210 + * + * 1 DAC => HP(sense) / Speakers, + * 1 ADC <= LineIn(sense) / MicIn / DMicIn, + * 1 SPDIF OUT => SPDIF Trasmitter(sense) +*/ + +/* CS4210 board names */ +static const char *cs421x_models[CS421X_MODELS] = { + [CS421X_CDB4210] = "cdb4210", +}; + +static const struct snd_pci_quirk cs421x_cfg_tbl[] = { + /* Test Intel board + CDB2410 */ + SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), + {} /* terminator */ +}; + +/* CS4210 board pinconfigs */ +/* Default CS4210 (CDB4210)*/ +static const struct cs_pincfg cdb4210_pincfgs[] = { + { 0x05, 0x0321401f }, + { 0x06, 0x90170010 }, + { 0x07, 0x03813031 }, + { 0x08, 0xb7a70037 }, + { 0x09, 0xb7a6003e }, + { 0x0a, 0x034510f0 }, + {} /* terminator */ +}; + +static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = { + [CS421X_CDB4210] = cdb4210_pincfgs, +}; + +static const struct hda_verb cs421x_coef_init_verbs[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 1}, + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG}, + /* + Disable Coefficient Index Auto-Increment(DAI)=1, + PDREF=0 + */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG}, + /* ADC SZCMode = Digital Soft Ramp */ + {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 }, + + {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG}, + {0x0B, AC_VERB_SET_PROC_COEF, + (0x0002 /* DAC SZCMode = Digital Soft Ramp */ + | 0x0004 /* Mute DAC on FIFO error */ + | 0x0008 /* Enable DAC High Pass Filter */ + )}, + {} /* terminator */ +}; + +/* Errata: CS4210 rev A1 Silicon + * + * http://www.cirrus.com/en/pubs/errata/ + * + * Description: + * 1. Performance degredation is present in the ADC. + * 2. Speaker output is not completely muted upon HP detect. + * 3. Noise is present when clipping occurs on the amplified + * speaker outputs. + * + * Workaround: + * The following verb sequence written to the registers during + * initialization will correct the issues listed above. + */ + +static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = { + {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011}, + {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A}, + {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */ + + {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B}, + {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */ + + {} /* terminator */ +}; + +/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */ +static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0); + +static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 3; + return 0; +} + +static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003; + return 0; +} + +static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + unsigned int vol = ucontrol->value.integer.value[0]; + unsigned int coef = + cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL); + unsigned int original_coef = coef; + + coef &= ~0x0003; + coef |= (vol & 0x0003); + if (original_coef == coef) + return 0; + else { + cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef); + return 1; + } +} + +static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = { + + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Speaker Boost Playback Volume", + .info = cs421x_boost_vol_info, + .get = cs421x_boost_vol_get, + .put = cs421x_boost_vol_put, + .tlv = { .p = cs421x_speaker_boost_db_scale }, +}; + +static void cs421x_pinmux_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + unsigned int def_conf, coef; + + /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */ + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + + if (spec->gpio_mask) + coef |= 0x0008; /* B1,B2 are GPIOs */ + else + coef &= ~0x0008; + + if (spec->sense_b) + coef |= 0x0010; /* B2 is SENSE_B, not inverted */ + else + coef &= ~0x0010; + + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + + if ((spec->gpio_mask || spec->sense_b) && + is_active_pin(codec, CS421X_DMIC_PIN_NID)) { + + /* + GPIO or SENSE_B forced - disconnect the DMIC pin. + */ + def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID); + def_conf &= ~AC_DEFCFG_PORT_CONN; + def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT); + snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf); + } +} + +static void init_cs421x_digital(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + + for (i = 0; i < cfg->dig_outs; i++) { + hda_nid_t nid = cfg->dig_out_pins[i]; + if (!cfg->speaker_outs) + continue; + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | SPDIF_EVENT); + spec->spdif_detect = 1; + } + } +} + +static int cs421x_init(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + snd_hda_sequence_write(codec, cs421x_coef_init_verbs); + snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes); + + cs421x_pinmux_init(codec); + + if (spec->gpio_mask) { + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, + spec->gpio_mask); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, + spec->gpio_dir); + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->gpio_data); + } + + init_output(codec); + init_input(codec); + init_cs421x_digital(codec); + + return 0; +} + +/* + * CS4210 Input MUX (1 ADC) + */ +static int cs421x_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + return snd_hda_input_mux_info(&spec->input_mux, uinfo); +} + +static int cs421x_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_input; + return 0; +} + +static int cs421x_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, + spec->adc_nid[0], &spec->cur_input); + +} + +static struct snd_kcontrol_new cs421x_capture_source = { + + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = cs421x_mux_enum_info, + .get = cs421x_mux_enum_get, + .put = cs421x_mux_enum_put, +}; + +static int cs421x_add_input_volume_control(struct hda_codec *codec, int item) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + const struct hda_input_mux *imux = &spec->input_mux; + hda_nid_t pin = cfg->inputs[item].pin; + struct snd_kcontrol *kctl; + u32 caps; + + if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP)) + return 0; + + caps = query_amp_caps(codec, pin, HDA_INPUT); + caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + if (caps <= 1) + return 0; + + return add_volume(codec, imux->items[item].label, 0, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); +} + +/* add a (input-boost) volume control to the given input pin */ +static int build_cs421x_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux = &spec->input_mux; + int i, err, type_idx; + const char *label; + + if (!spec->num_inputs) + return 0; + + /* make bind-capture */ + spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw); + spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol); + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + int n; + if (!spec->capture_bind[i]) + return -ENOMEM; + kctl = snd_ctl_new1(&cs_capture_ctls[i], codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = (long)spec->capture_bind[i]; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + for (n = 0; n < AUTO_PIN_LAST; n++) { + if (!spec->adc_nid[n]) + continue; + err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]); + if (err < 0) + return err; + } + } + + /* Add Input MUX Items + Capture Volume/Switch */ + for (i = 0; i < spec->num_inputs; i++) { + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx); + + err = cs421x_add_input_volume_control(codec, i); + if (err < 0) + return err; + } + + /* + Add 'Capture Source' Switch if + * 2 inputs and no mic detec + * 3 inputs + */ + if ((spec->num_inputs == 2 && !spec->mic_detect) || + (spec->num_inputs == 3)) { + + err = snd_hda_ctl_add(codec, spec->adc_nid[0], + snd_ctl_new1(&cs421x_capture_source, codec)); + if (err < 0) + return err; + } + + return 0; +} + +/* Single DAC (Mute/Gain) */ +static int build_cs421x_output(struct hda_codec *codec) +{ + hda_nid_t dac = CS4210_DAC_NID; + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct snd_kcontrol *kctl; + int err; + char *name = "HP/Speakers"; + + fix_volume_caps(codec, dac); + if (!spec->vmaster_sw) { + err = add_vmaster(codec, dac); + if (err < 0) + return err; + } + + err = add_mute(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_sw, kctl); + if (err < 0) + return err; + + err = add_volume(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + err = snd_ctl_add_slave(spec->vmaster_vol, kctl); + if (err < 0) + return err; + + if (cfg->speaker_outs) { + err = snd_hda_ctl_add(codec, 0, + snd_ctl_new1(&cs421x_speaker_bost_ctl, codec)); + if (err < 0) + return err; + } + return err; +} + +static int cs421x_build_controls(struct hda_codec *codec) +{ + int err; + + err = build_cs421x_output(codec); + if (err < 0) + return err; + err = build_cs421x_input(codec); + if (err < 0) + return err; + err = build_digital_output(codec); + if (err < 0) + return err; + return cs421x_init(codec); +} + +static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res) +{ + switch ((res >> 26) & 0x3f) { + case HP_EVENT: + case SPDIF_EVENT: + cs_automute(codec); + break; + + case MIC_EVENT: + cs_automic(codec); + break; + } +} + +static int parse_cs421x_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); + spec->cur_input = spec->last_input = i; + spec->num_inputs++; + + /* check whether the automatic mic switch is available */ + if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) { + spec->mic_detect = 1; + spec->automic_idx = i; + } + } + return 0; +} + +static int cs421x_parse_auto_config(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + err = parse_output(codec); + if (err < 0) + return err; + err = parse_cs421x_input(codec); + if (err < 0) + return err; + err = parse_digital_output(codec); + if (err < 0) + return err; + return 0; +} + +#ifdef CONFIG_PM +/* + Manage PDREF, when transitioning to D3hot + (DAC,ADC) -> D3, PDREF=1, AFG->D3 +*/ +static int cs421x_suspend(struct hda_codec *codec, pm_message_t state) +{ + unsigned int coef; + + snd_hda_shutup_pins(codec); + + snd_hda_codec_write(codec, CS4210_DAC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + snd_hda_codec_write(codec, CS4210_ADC_NID, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + + coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG); + coef |= 0x0004; /* PDREF */ + cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef); + + return 0; +} +#endif + +static struct hda_codec_ops cs4210_patch_ops = { + .build_controls = cs421x_build_controls, + .build_pcms = cs_build_pcms, + .init = cs421x_init, + .free = cs_free, + .unsol_event = cs421x_unsol_event, +#ifdef CONFIG_PM + .suspend = cs421x_suspend, +#endif +}; + +static int patch_cs421x(struct hda_codec *codec) +{ + struct cs_spec *spec; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + codec->spec = spec; + + spec->vendor_nid = CS421X_VENDOR_NID; + + spec->board_config = + snd_hda_check_board_config(codec, CS421X_MODELS, + cs421x_models, cs421x_cfg_tbl); + if (spec->board_config >= 0) + fix_pincfg(codec, spec->board_config, cs421x_pincfgs); + /* + Setup GPIO/SENSE for each board (if used) + */ + switch (spec->board_config) { + case CS421X_CDB4210: + snd_printd("CS4210 board: %s\n", + cs421x_models[spec->board_config]); +/* spec->gpio_mask = 3; + spec->gpio_dir = 3; + spec->gpio_data = 3; +*/ + spec->sense_b = 1; + + break; + } + + /* + Update the GPIO/DMIC/SENSE_B pinmux before the configuration + is auto-parsed. If GPIO or SENSE_B is forced, DMIC input + is disabled. + */ + cs421x_pinmux_init(codec); + + err = cs421x_parse_auto_config(codec); + if (err < 0) + goto error; + + codec->patch_ops = cs4210_patch_ops; + + return 0; + + error: + kfree(codec->spec); + codec->spec = NULL; + return err; +} + /* * patch entries @@ -1279,11 +1954,13 @@ static int patch_cs420x(struct hda_codec *codec) static const struct hda_codec_preset snd_hda_preset_cirrus[] = { { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x }, { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x }, + { .id = 0x10134210, .name = "CS4210", .patch = patch_cs421x }, {} /* terminator */ }; MODULE_ALIAS("snd-hda-codec-id:10134206"); MODULE_ALIAS("snd-hda-codec-id:10134207"); +MODULE_ALIAS("snd-hda-codec-id:10134210"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 884f67b8f4e..7696d05b935 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -446,6 +446,19 @@ static int conexant_init_jacks(struct hda_codec *codec) return 0; } +static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state) +{ + if (power_state == AC_PWRST_D3) + msleep(100); + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, + power_state); + /* partial workaround for "azx_get_response timeout" */ + if (power_state == AC_PWRST_D0) + msleep(10); + snd_hda_codec_set_power_to_all(codec, fg, power_state, true); +} + static int conexant_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -588,6 +601,7 @@ static const struct hda_codec_ops conexant_patch_ops = { .build_pcms = conexant_build_pcms, .init = conexant_init, .free = conexant_free, + .set_power_state = conexant_set_power, #ifdef CONFIG_SND_HDA_POWER_SAVE .suspend = conexant_suspend, #endif @@ -3334,6 +3348,8 @@ static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, #define MAX_AUTO_DACS 5 +#define DAC_SLAVE_FLAG 0x8000 /* filled dac is a slave */ + /* fill analog DAC list from the widget tree */ static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) { @@ -3356,16 +3372,26 @@ static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs) /* fill pin_dac_pair list from the pin and dac list */ static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins, int num_pins, hda_nid_t *dacs, int *rest, - struct pin_dac_pair *filled, int type) + struct pin_dac_pair *filled, int nums, + int type) { - int i, nums; + int i, start = nums; - nums = 0; - for (i = 0; i < num_pins; i++) { + for (i = 0; i < num_pins; i++, nums++) { filled[nums].pin = pins[i]; filled[nums].type = type; filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest); - nums++; + if (filled[nums].dac) + continue; + if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) { + filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG; + continue; + } + if (filled[0].dac && get_connection_index(codec, pins[i], filled[0].dac) >= 0) { + filled[nums].dac = filled[0].dac | DAC_SLAVE_FLAG; + continue; + } + snd_printdd("Failed to find a DAC for pin 0x%x", pins[i]); } return nums; } @@ -3381,19 +3407,19 @@ static void cx_auto_parse_output(struct hda_codec *codec) rest = fill_cx_auto_dacs(codec, dacs); /* parse all analog output pins */ nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs, - dacs, &rest, spec->dac_info, - AUTO_PIN_LINE_OUT); - nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, - dacs, &rest, spec->dac_info + nums, - AUTO_PIN_HP_OUT); - nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, - dacs, &rest, spec->dac_info + nums, - AUTO_PIN_SPEAKER_OUT); + dacs, &rest, spec->dac_info, 0, + AUTO_PIN_LINE_OUT); + nums = fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs, + dacs, &rest, spec->dac_info, nums, + AUTO_PIN_HP_OUT); + nums = fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs, + dacs, &rest, spec->dac_info, nums, + AUTO_PIN_SPEAKER_OUT); spec->dac_info_filled = nums; /* fill multiout struct */ for (i = 0; i < nums; i++) { hda_nid_t dac = spec->dac_info[i].dac; - if (!dac) + if (!dac || (dac & DAC_SLAVE_FLAG)) continue; switch (spec->dac_info[i].type) { case AUTO_PIN_LINE_OUT: @@ -3848,7 +3874,7 @@ static void cx_auto_parse_input(struct hda_codec *codec) } if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items) cx_auto_check_auto_mic(codec); - if (imux->num_items > 1 && !spec->auto_mic) { + if (imux->num_items > 1) { for (i = 1; i < imux->num_items; i++) { if (spec->imux_info[i].adc != spec->imux_info[0].adc) { spec->adc_switching = 1; @@ -4021,6 +4047,8 @@ static void cx_auto_init_output(struct hda_codec *codec) nid = spec->dac_info[i].dac; if (!nid) nid = spec->multiout.dac_nids[0]; + else if (nid & DAC_SLAVE_FLAG) + nid &= ~DAC_SLAVE_FLAG; select_connection(codec, spec->dac_info[i].pin, nid); } if (spec->auto_mute) { @@ -4153,9 +4181,11 @@ static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac, hda_nid_t pin, const char *name, int idx) { unsigned int caps; - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - if (caps & AC_AMPCAP_NUM_STEPS) - return cx_auto_add_pb_volume(codec, dac, name, idx); + if (dac && !(dac & DAC_SLAVE_FLAG)) { + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + if (caps & AC_AMPCAP_NUM_STEPS) + return cx_auto_add_pb_volume(codec, dac, name, idx); + } caps = query_amp_caps(codec, pin, HDA_OUTPUT); if (caps & AC_AMPCAP_NUM_STEPS) return cx_auto_add_pb_volume(codec, pin, name, idx); @@ -4177,8 +4207,7 @@ static int cx_auto_build_output_controls(struct hda_codec *codec) for (i = 0; i < spec->dac_info_filled; i++) { const char *label; int idx, type; - if (!spec->dac_info[i].dac) - continue; + hda_nid_t dac = spec->dac_info[i].dac; type = spec->dac_info[i].type; if (type == AUTO_PIN_LINE_OUT) type = spec->autocfg.line_out_type; @@ -4197,7 +4226,7 @@ static int cx_auto_build_output_controls(struct hda_codec *codec) idx = num_spk++; break; } - err = try_add_pb_volume(codec, spec->dac_info[i].dac, + err = try_add_pb_volume(codec, dac, spec->dac_info[i].pin, label, idx); if (err < 0) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 52ce07534e5..7cabd731716 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -565,11 +565,11 @@ static void alc_hp_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (!spec->automute) - return; spec->jack_present = detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), spec->autocfg.hp_pins); + if (!spec->automute) + return; update_speakers(codec); } @@ -578,11 +578,11 @@ static void alc_line_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (!spec->automute || !spec->detect_line) - return; spec->line_jack_present = detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), spec->autocfg.line_out_pins); + if (!spec->automute || !spec->detect_line) + return; update_speakers(codec); } @@ -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++) { @@ -1781,6 +1784,7 @@ static const char * const alc_slave_vols[] = { "Speaker Playback Volume", "Mono Playback Volume", "Line-Out Playback Volume", + "PCM Playback Volume", NULL, }; @@ -1795,6 +1799,7 @@ static const char * const alc_slave_sws[] = { "Mono Playback Switch", "IEC958 Playback Switch", "Line-Out Playback Switch", + "PCM Playback Switch", NULL, }; @@ -1911,7 +1916,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) @@ -2386,7 +2391,7 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state) } #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int alc_resume(struct hda_codec *codec) { msleep(150); /* to avoid pop noise */ @@ -2406,7 +2411,7 @@ static const struct hda_codec_ops alc_patch_ops = { .init = alc_init, .free = alc_free, .unsol_event = alc_unsol_event, -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM .resume = alc_resume, #endif #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -2801,7 +2806,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) int i; again: - spec->multiout.num_dacs = 0; + /* set num_dacs once to full for alc_auto_look_for_dac() */ + spec->multiout.num_dacs = cfg->line_outs; spec->multiout.hp_nid = 0; spec->multiout.extra_out_nid[0] = 0; memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); @@ -2834,6 +2840,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) } } + /* re-count num_dacs and squash invalid entries */ + spec->multiout.num_dacs = 0; for (i = 0; i < cfg->line_outs; i++) { if (spec->private_dac_nids[i]) spec->multiout.num_dacs++; @@ -3075,16 +3083,22 @@ static void alc_auto_init_multi_out(struct hda_codec *codec) static void alc_auto_init_extra_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t pin; + hda_nid_t pin, dac; pin = spec->autocfg.hp_pins[0]; - if (pin) - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, - spec->multiout.hp_nid); + if (pin) { + dac = spec->multiout.hp_nid; + if (!dac) + dac = spec->multiout.dac_nids[0]; + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + } pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, - spec->multiout.extra_out_nid[0]); + if (pin) { + dac = spec->multiout.extra_out_nid[0]; + if (!dac) + dac = spec->multiout.dac_nids[0]; + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + } } /* @@ -3674,7 +3688,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); @@ -3801,7 +3815,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); @@ -3980,7 +3994,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); @@ -4134,7 +4148,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); @@ -4290,7 +4304,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); @@ -4410,7 +4424,7 @@ static void alc269_shutup(struct hda_codec *codec) } } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int alc269_resume(struct hda_codec *codec) { if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) { @@ -4433,7 +4447,7 @@ static int alc269_resume(struct hda_codec *codec) hda_call_check_power_status(codec, 0x01); return 0; } -#endif /* SND_HDA_NEEDS_RESUME */ +#endif /* CONFIG_PM */ static void alc269_fixup_hweq(struct hda_codec *codec, const struct alc_fixup *fix, int action) @@ -4478,6 +4492,22 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec, spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; } +static void alc269_fixup_stereo_dmic(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + int coef; + + if (action != ALC_FIXUP_ACT_INIT) + return; + /* The digital-mic unit sends PDM (differential signal) instead of + * the standard PCM, thus you can't record a valid mono stream as is. + * Below is a workaround specific to ALC269 to control the dmic + * signal source as mono. + */ + coef = alc_read_coef_idx(codec, 0x07); + alc_write_coef_idx(codec, 0x07, coef | 0x80); +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -4488,6 +4518,7 @@ enum { ALC275_FIXUP_SONY_HWEQ, ALC271_FIXUP_DMIC, ALC269_FIXUP_PCM_44K, + ALC269_FIXUP_STEREO_DMIC, }; static const struct alc_fixup alc269_fixups[] = { @@ -4550,10 +4581,19 @@ static const struct alc_fixup alc269_fixups[] = { .type = ALC_FIXUP_FUNC, .v.func = alc269_fixup_pcm_44k, }, + [ALC269_FIXUP_STEREO_DMIC] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc269_fixup_stereo_dmic, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), + SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), @@ -4702,7 +4742,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); @@ -4725,7 +4765,7 @@ static int patch_alc269(struct hda_codec *codec) spec->vmaster_nid = 0x02; codec->patch_ops = alc_patch_ops; -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM codec->patch_ops.resume = alc269_resume; #endif if (board_config == ALC_MODEL_AUTO) @@ -4840,7 +4880,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); @@ -4981,7 +5021,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); @@ -5197,7 +5237,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); @@ -5333,7 +5373,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); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 56425a53cf1..5145b663ef6 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -95,6 +95,7 @@ enum { STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, STAC_92HD83XXX_HP, + STAC_92HD83XXX_HP_cNB11_INTQUAD, STAC_HP_DV7_4000, STAC_92HD83XXX_MODELS }; @@ -212,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; @@ -671,6 +673,32 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +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; +} +#endif + static unsigned int stac92xx_vref_set(struct hda_codec *codec, hda_nid_t nid, unsigned int new_vref) { @@ -1636,10 +1664,17 @@ static const unsigned int hp_dv7_4000_pin_configs[10] = { 0x40f000f0, 0x40f000f0, }; +static const unsigned int hp_cNB11_intquad_pin_configs[10] = { + 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, + 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, + 0x40f000f0, 0x40f000f0, +}; + static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, [STAC_DELL_S14] = dell_s14_pin_configs, + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs, [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, }; @@ -1649,6 +1684,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_DELL_S14] = "dell-s14", [STAC_92HD83XXX_HP] = "hp", + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", [STAC_HP_DV7_4000] = "hp-dv7-4000", }; @@ -1661,7 +1697,47 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, "unknown Dell", STAC_DELL_S14), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600, - "HP", STAC_92HD83XXX_HP), + "HP", STAC_92HD83XXX_HP), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593, + "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), {} /* terminator */ }; @@ -4020,6 +4096,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); @@ -4209,10 +4287,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; + } } } @@ -4382,11 +4462,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, @@ -4784,10 +4879,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", @@ -4885,7 +4981,7 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer, #define stac927x_proc_hook NULL #endif -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int stac92xx_resume(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4901,29 +4997,81 @@ static int stac92xx_resume(struct hda_codec *codec) stac_issue_unsol_event(codec, spec->autocfg.line_out_pins[0]); } + 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) - hda_call_check_power_status(codec, 0x01); + 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); +} + /* - * using power check for controlling mute led of HP notebooks - * check for mute state only on Speakers (nid = 0x10) - * - * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise - * the LED is NOT working properly ! - * - * Changed name to reflect that it now works for any designated - * model, not just HP HDX. + * For this feature CONFIG_SND_HDA_POWER_SAVE is needed + * as mute LED state is updated in check_power_status hook */ - -#ifdef CONFIG_SND_HDA_POWER_SAVE -static int stac92xx_hp_check_power_status(struct hda_codec *codec, - hda_nid_t nid) +static int stac92xx_update_led_status(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; - int i, muted = 1; + 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]; @@ -4933,27 +5081,58 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec, break; } } - 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 (muted && spec->multiout.hp_nid) + if (!(snd_hda_codec_amp_read(codec, + spec->multiout.hp_nid, 0, HDA_OUTPUT, 0) & + HDA_AMP_MUTE)) { + muted = 0; /* HP is not muted */ + } + num_ext_dacs = ARRAY_SIZE(spec->multiout.extra_out_nid); + for (i = 0; muted && i < num_ext_dacs; i++) { + nid = spec->multiout.extra_out_nid[i]; + if (nid == 0) + break; + if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & + HDA_AMP_MUTE)) { + muted = 0; /* extra output is not muted */ + } } + /*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 */ - stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data); + 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); + } return 0; } -#endif -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +/* + * use power check for controlling mute led of HP notebooks + */ +static int stac92xx_check_power_status(struct hda_codec *codec, + hda_nid_t nid) { - stac92xx_shutup(codec); + stac92xx_update_led_status(codec); + return 0; } -#endif +#endif /* CONFIG_SND_HDA_POWER_SAVE */ +#endif /* CONFIG_PM */ static const struct hda_codec_ops stac92xx_patch_ops = { .build_controls = stac92xx_build_controls, @@ -4961,7 +5140,7 @@ static const struct hda_codec_ops stac92xx_patch_ops = { .init = stac92xx_init, .free = stac92xx_free, .unsol_event = stac92xx_unsol_event, -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM .suspend = stac92xx_suspend, .resume = stac92xx_resume, #endif @@ -5477,12 +5656,19 @@ 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_hp_check_power_status; + stac92xx_check_power_status; } #endif @@ -5805,12 +5991,19 @@ 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_hp_check_power_status; + stac92xx_check_power_status; } #endif diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index f38160b00e1..4ebfbd874c9 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1708,7 +1708,7 @@ static void via_unsol_event(struct hda_codec *codec, via_gpio_control(codec); } -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM static int via_suspend(struct hda_codec *codec, pm_message_t state) { struct via_spec *spec = codec->spec; @@ -1736,7 +1736,7 @@ static const struct hda_codec_ops via_patch_ops = { .init = via_init, .free = via_free, .unsol_event = via_unsol_event, -#ifdef SND_HDA_NEEDS_RESUME +#ifdef CONFIG_PM .suspend = via_suspend, #endif #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -2084,7 +2084,7 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec) struct via_spec *spec = codec->spec; struct nid_path *path; bool check_dac; - hda_nid_t pin, dac; + hda_nid_t pin, dac = 0; int err; pin = spec->autocfg.speaker_pins[0]; |