From 19aed14f3d9359b20408a9ac74237a35ead98203 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 08:45:33 +0100 Subject: [PATCH] --- yaml --- r: 353150 b: refs/heads/master c: 52fd5cbc9bef6a2e20bfbdae771498ef97c67b34 h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/sound/pci/hda/Kconfig | 4 - trunk/sound/pci/hda/hda_auto_parser.c | 25 + trunk/sound/pci/hda/patch_analog.c | 915 +++++--- trunk/sound/pci/hda/patch_cirrus.c | 1323 ++++++++++- trunk/sound/pci/hda/patch_conexant.c | 1526 +++++++++++-- trunk/sound/pci/hda/patch_sigmatel.c | 3019 +++++++++---------------- trunk/sound/pci/hda/patch_via.c | 2788 ++++++++++++++++++++--- 8 files changed, 6767 insertions(+), 2835 deletions(-) diff --git a/[refs] b/[refs] index d9cd0a7df3d8..f24f10ddd255 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 9b473e8516c0d6745dd4c0ec69f9c17f14df0469 +refs/heads/master: 52fd5cbc9bef6a2e20bfbdae771498ef97c67b34 diff --git a/trunk/sound/pci/hda/Kconfig b/trunk/sound/pci/hda/Kconfig index 4004d405e082..9aff5cfafe2b 100644 --- a/trunk/sound/pci/hda/Kconfig +++ b/trunk/sound/pci/hda/Kconfig @@ -99,7 +99,6 @@ config SND_HDA_CODEC_REALTEK config SND_HDA_CODEC_ANALOG bool "Build Analog Device HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include Analog Device HD-audio codec support in snd-hda-intel driver, such as AD1986A. @@ -124,7 +123,6 @@ config SND_HDA_CODEC_SIGMATEL config SND_HDA_CODEC_VIA bool "Build VIA HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include VIA HD-audio codec support in snd-hda-intel driver, such as VT1708. @@ -151,7 +149,6 @@ config SND_HDA_CODEC_HDMI config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" default y - select SND_HDA_GENERIC help Say Y here to include Cirrus Logic codec support in snd-hda-intel driver, such as CS4206. @@ -164,7 +161,6 @@ config SND_HDA_CODEC_CIRRUS config SND_HDA_CODEC_CONEXANT bool "Build Conexant HD-audio codec support" default y - select SND_HDA_GENERIC help Say Y here to include Conexant HD-audio codec support in snd-hda-intel driver, such as CX20549. diff --git a/trunk/sound/pci/hda/hda_auto_parser.c b/trunk/sound/pci/hda/hda_auto_parser.c index 33b3ece224c6..a4810c7437bd 100644 --- a/trunk/sound/pci/hda/hda_auto_parser.c +++ b/trunk/sound/pci/hda/hda_auto_parser.c @@ -97,6 +97,28 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) } } +/* check whether the given pin has a proper pin I/O capability bit */ +static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin, + unsigned int dev) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, pin); + + /* some old hardware don't return the proper pincaps */ + if (!pincap) + return true; + + switch (dev) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + case AC_JACK_HP_OUT: + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + return !!(pincap & AC_PINCAP_OUT); + default: + return !!(pincap & AC_PINCAP_IN); + } +} + /* * Parse all pin widgets and store the useful pin nids to cfg * @@ -164,6 +186,9 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, dev = AC_JACK_SPEAKER; } + if (!check_pincap_validity(codec, nid, dev)) + continue; + switch (dev) { case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); diff --git a/trunk/sound/pci/hda/patch_analog.c b/trunk/sound/pci/hda/patch_analog.c index 02fe0d1da6e0..308a5b9e6b9d 100644 --- a/trunk/sound/pci/hda/patch_analog.c +++ b/trunk/sound/pci/hda/patch_analog.c @@ -31,15 +31,11 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" struct ad198x_spec { - struct hda_gen_spec gen; - const struct snd_kcontrol_new *mixers[6]; int num_mixers; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - hda_nid_t beep_dev_nid; const struct hda_verb *init_verbs[6]; /* initialization verbs * don't forget NULL termination! */ @@ -53,6 +49,11 @@ struct ad198x_spec { unsigned int cur_eapd; unsigned int need_dac_fix; + const hda_nid_t *alt_dac_nid; + const struct hda_pcm_stream *stream_analog_alt_playback; + int independent_hp; + int num_active_streams; + /* capture */ unsigned int num_adc_nids; const hda_nid_t *adc_nids; @@ -72,8 +73,15 @@ struct ad198x_spec { unsigned int spdif_route; + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + struct hda_input_mux private_imux; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + unsigned int jack_present: 1; unsigned int inv_jack_detect: 1;/* inverted jack-detection */ + unsigned int inv_eapd: 1; /* inverted EAPD implementation */ unsigned int analog_beep: 1; /* analog beep input present */ unsigned int avoid_init_slave_vol:1; @@ -142,6 +150,8 @@ static const char * const ad1988_6stack_fp_slave_pfxs[] = { NULL }; +static void ad198x_free_kctls(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 ad_beep_mixer[] = { @@ -162,33 +172,6 @@ static const struct snd_kcontrol_new ad_beep2_mixer[] = { #define set_beep_amp(spec, nid, idx, dir) /* NOP */ #endif -#ifdef CONFIG_SND_HDA_INPUT_BEEP -static int create_beep_ctls(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - const struct snd_kcontrol_new *knew; - - if (!spec->beep_amp) - return 0; - - knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; - for ( ; knew->name; knew++) { - int err; - 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; - } - return 0; -} -#else -#define create_beep_ctls(codec) 0 -#endif - static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -220,9 +203,22 @@ static int ad198x_build_controls(struct hda_codec *codec) } /* create beep controls if needed */ - err = create_beep_ctls(codec); - if (err < 0) - return err; +#ifdef CONFIG_SND_HDA_INPUT_BEEP + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; + for ( ; 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 /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { @@ -248,6 +244,8 @@ static int ad198x_build_controls(struct hda_codec *codec) return err; } + ad198x_free_kctls(codec); /* no longer needed */ + /* assign Capture Source enums to NID */ kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); if (!kctl) @@ -279,6 +277,72 @@ static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) } #endif +static void activate_ctl(struct hda_codec *codec, const char *name, int active) +{ + struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); + if (ctl) { + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->vd[0].access |= active ? 0 : + SNDRV_CTL_ELEM_ACCESS_INACTIVE; + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; + ctl->vd[0].access |= active ? + SNDRV_CTL_ELEM_ACCESS_WRITE : 0; + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); + } +} + +static void set_stream_active(struct hda_codec *codec, bool active) +{ + struct ad198x_spec *spec = codec->spec; + if (active) + spec->num_active_streams++; + else + spec->num_active_streams--; + activate_ctl(codec, "Independent HP", spec->num_active_streams == 0); +} + +static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { "OFF", "ON", NULL}; + int index; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + index = uinfo->value.enumerated.item; + if (index >= 2) + index = 1; + strcpy(uinfo->value.enumerated.name, texts[index]); + return 0; +} + +static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->independent_hp; + return 0; +} + +static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ad198x_spec *spec = codec->spec; + unsigned int select = ucontrol->value.enumerated.item[0]; + if (spec->independent_hp != select) { + spec->independent_hp = select; + if (spec->independent_hp) + spec->multiout.hp_nid = 0; + else + spec->multiout.hp_nid = spec->alt_dac_nid[0]; + return 1; + } + return 0; +} + /* * Analog playback callbacks */ @@ -287,8 +351,15 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + int err; + set_stream_active(codec, true); + err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); + if (err < 0) { + set_stream_active(codec, false); + return err; + } + return 0; } static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -310,6 +381,43 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } +static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_active(codec, false); + return 0; +} + +static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct ad198x_spec *spec = codec->spec; + if (!spec->independent_hp) + return -EBUSY; + set_stream_active(codec, true); + return 0; +} + +static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_active(codec, false); + return 0; +} + +static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = ad1988_alt_playback_pcm_open, + .close = ad1988_alt_playback_pcm_close + }, +}; + /* * Digital out */ @@ -383,6 +491,7 @@ static const struct hda_pcm_stream ad198x_pcm_analog_playback = { .open = ad198x_playback_pcm_open, .prepare = ad198x_playback_pcm_prepare, .cleanup = ad198x_playback_pcm_cleanup, + .close = ad198x_playback_pcm_close }, }; @@ -447,18 +556,43 @@ static int ad198x_build_pcms(struct hda_codec *codec) } } + if (spec->alt_dac_nid && spec->stream_analog_alt_playback) { + codec->num_pcms++; + info = spec->pcm_rec + 2; + info->name = "AD198x Headphone"; + info->pcm_type = HDA_PCM_TYPE_AUDIO; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid[0]; + } + return 0; } +static void ad198x_free_kctls(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} + static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, hda_nid_t hp) { + struct ad198x_spec *spec = codec->spec; if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, - !codec->inv_eapd ? 0x00 : 0x02); + !spec->inv_eapd ? 0x00 : 0x02); if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD) snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, - !codec->inv_eapd ? 0x00 : 0x02); + !spec->inv_eapd ? 0x00 : 0x02); } static void ad198x_power_eapd(struct hda_codec *codec) @@ -502,7 +636,7 @@ static void ad198x_free(struct hda_codec *codec) if (!spec) return; - snd_hda_gen_spec_free(&spec->gen); + ad198x_free_kctls(codec); kfree(spec); snd_hda_detach_beep_device(codec); } @@ -539,7 +673,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - if (codec->inv_eapd) + if (spec->inv_eapd) ucontrol->value.integer.value[0] = ! spec->cur_eapd; else ucontrol->value.integer.value[0] = spec->cur_eapd; @@ -554,7 +688,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; eapd = !!ucontrol->value.integer.value[0]; - if (codec->inv_eapd) + if (spec->inv_eapd) eapd = !eapd; if (eapd == spec->cur_eapd) return 0; @@ -573,66 +707,6 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -static int ad198x_auto_build_controls(struct hda_codec *codec) -{ - int err; - - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; - err = create_beep_ctls(codec); - if (err < 0) - return err; - return 0; -} - -static const struct hda_codec_ops ad198x_auto_patch_ops = { - .build_controls = ad198x_auto_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = ad198x_free, -#ifdef CONFIG_PM - .check_power_status = snd_hda_gen_check_power_status, - .suspend = ad198x_suspend, -#endif - .reboot_notify = ad198x_shutup, -}; - - -static int ad198x_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - int err; - - codec->spdif_status_reset = 1; - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - spec->gen.indep_hp = 1; - - err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); - if (err < 0) - return err; - err = snd_hda_gen_parse_auto_config(codec, cfg); - if (err < 0) - return err; - - if (spec->beep_dev_nid) { - err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid); - if (err < 0) - return err; - } - - codec->patch_ops = ad198x_auto_patch_ops; - - return 0; -} - /* * AD1986A specific */ @@ -1094,7 +1168,6 @@ static int ad1986a_samsung_p50_init(struct hda_codec *codec) /* models */ enum { - AD1986A_AUTO, AD1986A_6STACK, AD1986A_3STACK, AD1986A_LAPTOP, @@ -1107,7 +1180,6 @@ enum { }; static const char * const ad1986a_models[AD1986A_MODELS] = { - [AD1986A_AUTO] = "auto", [AD1986A_6STACK] = "6stack", [AD1986A_3STACK] = "3stack", [AD1986A_LAPTOP] = "laptop", @@ -1174,33 +1246,10 @@ static int alloc_ad_spec(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); return 0; } -/* - */ -static int ad1986a_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - /* AD1986A has the inverted EAPD implementation */ - codec->inv_eapd = 1; - - spec->beep_dev_nid = 0x19; - set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); - - /* AD1986A has a hardware problem that it can't share a stream - * with multiple output pins. The copy of front to surrounds - * causes noisy or silent outputs at a certain timing, e.g. - * changing the volume. - * So, let's disable the shared stream. - */ - spec->gen.multiout.no_share_stream = 1; - - return ad198x_parse_auto_config(codec); -} - static int patch_ad1986a(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -1211,18 +1260,6 @@ static int patch_ad1986a(struct hda_codec *codec) return err; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, - ad1986a_models, - ad1986a_cfg_tbl); - if (board_config == AD1986A_AUTO) { - err = ad1986a_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x19); if (err < 0) { ad198x_free(codec); @@ -1246,11 +1283,14 @@ static int patch_ad1986a(struct hda_codec *codec) spec->loopback.amplist = ad1986a_loopbacks; #endif spec->vmaster_nid = 0x1b; - codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ + spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ codec->patch_ops = ad198x_patch_ops; /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, + ad1986a_models, + ad1986a_cfg_tbl); switch (board_config) { case AD1986A_3STACK: spec->num_mixers = 2; @@ -1506,31 +1546,9 @@ static const struct hda_amp_list ad1983_loopbacks[] = { }; #endif -/* models */ -enum { - AD1983_AUTO, - AD1983_BASIC, - AD1983_MODELS -}; - -static const char * const ad1983_models[AD1983_MODELS] = { - [AD1983_AUTO] = "auto", - [AD1983_BASIC] = "basic", -}; - -static int ad1983_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); -} - static int patch_ad1983(struct hda_codec *codec) { struct ad198x_spec *spec; - int board_config; int err; err = alloc_ad_spec(codec); @@ -1538,17 +1556,6 @@ static int patch_ad1983(struct hda_codec *codec) return err; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1983_MODELS, - ad1983_models, NULL); - if (board_config == AD1983_AUTO) { - err = ad1983_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -1917,7 +1924,6 @@ static const struct hda_input_mux ad1981_thinkpad_capture_source = { /* models */ enum { - AD1981_AUTO, AD1981_BASIC, AD1981_HP, AD1981_THINKPAD, @@ -1926,7 +1932,6 @@ enum { }; static const char * const ad1981_models[AD1981_MODELS] = { - [AD1981_AUTO] = "auto", [AD1981_HP] = "hp", [AD1981_THINKPAD] = "thinkpad", [AD1981_BASIC] = "basic", @@ -1946,15 +1951,6 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = { {} }; -static int ad1981_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); -} - static int patch_ad1981(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -1965,18 +1961,6 @@ static int patch_ad1981(struct hda_codec *codec) return -ENOMEM; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1981_MODELS, - ad1981_models, - ad1981_cfg_tbl); - if (board_config == AD1981_AUTO) { - err = ad1981_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -2005,6 +1989,9 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1981_MODELS, + ad1981_models, + ad1981_cfg_tbl); switch (board_config) { case AD1981_HP: spec->mixers[0] = ad1981_hp_mixers; @@ -2144,13 +2131,13 @@ static int patch_ad1981(struct hda_codec *codec) /* models */ enum { - AD1988_AUTO, AD1988_6STACK, AD1988_6STACK_DIG, AD1988_3STACK, AD1988_3STACK_DIG, AD1988_LAPTOP, AD1988_LAPTOP_DIG, + AD1988_AUTO, AD1988_MODEL_LAST, }; @@ -2255,6 +2242,17 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, return err; } +static const struct snd_kcontrol_new ad1988_hp_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = ad1988_independent_hp_info, + .get = ad1988_independent_hp_get, + .put = ad1988_independent_hp_put, + }, + { } /* end */ +}; + /* 6-stack mode */ static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), @@ -2819,15 +2817,414 @@ static const struct hda_amp_list ad1988_loopbacks[] = { #endif /* + * Automatic parse of I/O pins from the BIOS configuration */ +enum { + AD_CTL_WIDGET_VOL, + AD_CTL_WIDGET_MUTE, + AD_CTL_BIND_MUTE, +}; +static const struct snd_kcontrol_new ad1988_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_BIND_MUTE(NULL, 0, 0, 0), +}; + +/* add dynamic controls */ +static int add_control(struct ad198x_spec *spec, int type, const char *name, + unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = snd_array_new(&spec->kctls); + if (!knew) + return -ENOMEM; + *knew = ad1988_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); + if (! knew->name) + return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + knew->private_value = val; + return 0; +} + +#define AD1988_PIN_CD_NID 0x18 +#define AD1988_PIN_BEEP_NID 0x10 + +static const hda_nid_t ad1988_mixer_nids[8] = { + /* A B C D E F G H */ + 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28 +}; + +static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx) +{ + static const hda_nid_t idx_to_dac[8] = { + /* A B C D E F G H */ + 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a + }; + static const hda_nid_t idx_to_dac_rev2[8] = { + /* A B C D E F G H */ + 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06 + }; + if (is_rev2(codec)) + return idx_to_dac_rev2[idx]; + else + return idx_to_dac[idx]; +} + +static const hda_nid_t ad1988_boost_nids[8] = { + 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0 +}; + +static int ad1988_pin_idx(hda_nid_t nid) +{ + static const hda_nid_t ad1988_io_pins[8] = { + 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25 + }; + int i; + for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++) + if (ad1988_io_pins[i] == nid) + return i; + return 0; /* should be -1 */ +} + +static int ad1988_pin_to_loopback_idx(hda_nid_t nid) +{ + static const int loopback_idx[8] = { + 2, 0, 1, 3, 4, 5, 1, 4 + }; + switch (nid) { + case AD1988_PIN_CD_NID: + return 6; + default: + return loopback_idx[ad1988_pin_idx(nid)]; + } +} + +static int ad1988_pin_to_adc_idx(hda_nid_t nid) +{ + static const int adc_idx[8] = { + 0, 1, 2, 8, 4, 3, 6, 7 + }; + switch (nid) { + case AD1988_PIN_CD_NID: + return 5; + default: + return adc_idx[ad1988_pin_idx(nid)]; + } +} + +/* fill in the dac_nids table from the parsed pin configuration */ +static int ad1988_auto_fill_dac_nids(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct ad198x_spec *spec = codec->spec; + int i, idx; + + spec->multiout.dac_nids = spec->private_dac_nids; + + /* check the pins hardwired to audio widget */ + for (i = 0; i < cfg->line_outs; i++) { + idx = ad1988_pin_idx(cfg->line_out_pins[i]); + spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx); + } + spec->multiout.num_dacs = cfg->line_outs; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char * const chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; + hda_nid_t nid; + int i, err; + + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t dac = spec->multiout.dac_nids[i]; + if (! dac) + continue; + nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])]; + if (i == 2) { + /* Center/LFE */ + err = add_control(spec, AD_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = add_control(spec, AD_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = add_control(spec, AD_CTL_BIND_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); + if (err < 0) + return err; + err = add_control(spec, AD_CTL_BIND_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = add_control(spec, AD_CTL_BIND_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +/* add playback controls for speaker and HP outputs */ +static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, + const char *pfx) +{ + struct ad198x_spec *spec = codec->spec; + hda_nid_t nid; + int i, idx, err; + char name[32]; + + if (! pin) + return 0; + + idx = ad1988_pin_idx(pin); + nid = ad1988_idx_to_dac(codec, idx); + /* check whether the corresponding DAC was already taken */ + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t pin = spec->autocfg.line_out_pins[i]; + hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin)); + if (dac == nid) + break; + } + if (i >= spec->autocfg.line_outs) { + /* specify the DAC as the extra output */ + if (!spec->multiout.hp_nid) + spec->multiout.hp_nid = nid; + else + spec->multiout.extra_out_nid[0] = nid; + /* control HP volume/switch on the output mixer amp */ + sprintf(name, "%s Playback Volume", pfx); + err = add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + nid = ad1988_mixer_nids[idx]; + sprintf(name, "%s Playback Switch", pfx); + if ((err = add_control(spec, AD_CTL_BIND_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0) + return err; + return 0; +} + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin, + const char *ctlname, int ctlidx, int boost) +{ + char name[32]; + int err, idx; + + sprintf(name, "%s Playback Volume", ctlname); + idx = ad1988_pin_to_loopback_idx(pin); + if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) + return err; + sprintf(name, "%s Playback Switch", ctlname); + if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0) + return err; + if (boost) { + hda_nid_t bnid; + idx = ad1988_pin_idx(pin); + bnid = ad1988_boost_nids[idx]; + if (bnid) { + sprintf(name, "%s Boost Volume", ctlname); + return add_control(spec, AD_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT)); + + } + } + return 0; +} + +/* create playback/capture controls for input pins */ +static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct ad198x_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux; + int i, err, type, type_idx; + + for (i = 0; i < cfg->num_inputs; i++) { + const char *label; + type = cfg->inputs[i].type; + label = hda_get_autocfg_input_label(codec, cfg, i); + snd_hda_add_imux_item(imux, label, + ad1988_pin_to_adc_idx(cfg->inputs[i].pin), + &type_idx); + err = new_analog_input(spec, cfg->inputs[i].pin, + label, type_idx, + type == AUTO_PIN_MIC); + if (err < 0) + return err; + } + snd_hda_add_imux_item(imux, "Mix", 9, NULL); + + if ((err = add_control(spec, AD_CTL_WIDGET_VOL, + "Analog Mix Playback Volume", + HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) + return err; + if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, + "Analog Mix Playback Switch", + HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0) + return err; + + return 0; +} + +static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + /* set as output */ + snd_hda_set_pin_ctl(codec, nid, pin_type); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + switch (nid) { + case 0x11: /* port-A - DAC 03 */ + snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00); + break; + case 0x14: /* port-B - DAC 06 */ + snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02); + break; + case 0x15: /* port-C - DAC 05 */ + snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00); + break; + case 0x17: /* port-E - DAC 0a */ + snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01); + break; + case 0x13: /* mono - DAC 04 */ + snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01); + break; + } +} + +static void ad1988_auto_init_multi_out(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + } +} + +static void ad1988_auto_init_extra_out(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.speaker_pins[0]; + if (pin) /* connect to front */ + ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); + pin = spec->autocfg.hp_pins[0]; + if (pin) /* connect to front */ + ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); +} + +static void ad1988_auto_init_analog_input(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, idx; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + int type = cfg->inputs[i].type; + int val; + switch (nid) { + case 0x15: /* port-C */ + snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); + break; + case 0x17: /* port-E */ + snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0); + break; + } + val = PIN_IN; + if (type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); + if (nid != AD1988_PIN_CD_NID) + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + idx = ad1988_pin_idx(nid); + if (ad1988_boost_nids[idx]) + snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_ZERO); + } +} + +/* parse the BIOS configuration and set up the alc_spec */ +/* return 1 if successful, 0 if the proper config is not found, or a negative error code */ static int ad1988_parse_auto_config(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; + int err; - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); + if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0) + return err; + if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) + return err; + if (! spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ + if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || + (err = ad1988_auto_create_extra_out(codec, + spec->autocfg.speaker_pins[0], + "Speaker")) < 0 || + (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0], + "Headphone")) < 0 || + (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_outs) + spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = AD1988_SPDIF_IN; + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +/* init callback for auto-configuration model -- overriding the default init */ +static int ad1988_auto_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1988_auto_init_multi_out(codec); + ad1988_auto_init_extra_out(codec); + ad1988_auto_init_analog_input(codec); + return 0; } /* @@ -2862,6 +3259,9 @@ static int patch_ad1988(struct hda_codec *codec) return err; spec = codec->spec; + if (is_rev2(codec)) + snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); + board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, ad1988_models, ad1988_cfg_tbl); if (board_config < 0) { @@ -2876,13 +3276,12 @@ static int patch_ad1988(struct hda_codec *codec) if (err < 0) { ad198x_free(codec); return err; + } else if (! err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n"); + board_config = AD1988_6STACK; } - return 0; } - if (is_rev2(codec)) - snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); - err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -2945,7 +3344,7 @@ static int patch_ad1988(struct hda_codec *codec) spec->input_mux = &ad1988_laptop_capture_source; spec->num_mixers = 1; spec->mixers[0] = ad1988_laptop_mixers; - codec->inv_eapd = 1; /* inverted EAPD */ + spec->inv_eapd = 1; /* inverted EAPD */ spec->num_init_verbs = 1; spec->init_verbs[0] = ad1988_laptop_init_verbs; if (board_config == AD1988_LAPTOP_DIG) @@ -2953,6 +3352,15 @@ static int patch_ad1988(struct hda_codec *codec) break; } + if (spec->autocfg.hp_pins[0]) { + spec->mixers[spec->num_mixers++] = ad1988_hp_mixers; + spec->slave_vols = ad1988_6stack_fp_slave_pfxs; + spec->slave_sws = ad1988_6stack_fp_slave_pfxs; + spec->alt_dac_nid = ad1988_alt_dac_nid; + spec->stream_analog_alt_playback = + &ad198x_pcm_analog_alt_playback; + } + spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids); spec->adc_nids = ad1988_adc_nids; spec->capsrc_nids = ad1988_capsrc_nids; @@ -2980,6 +3388,9 @@ static int patch_ad1988(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; switch (board_config) { + case AD1988_AUTO: + codec->patch_ops.init = ad1988_auto_init; + break; case AD1988_LAPTOP: case AD1988_LAPTOP_DIG: codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; @@ -3157,43 +3568,7 @@ static const char * const ad1884_slave_vols[] = { NULL }; -enum { - AD1884_AUTO, - AD1884_BASIC, - AD1884_MODELS -}; - -static const char * const ad1884_models[AD1884_MODELS] = { - [AD1884_AUTO] = "auto", - [AD1884_BASIC] = "basic", -}; - -static int ad1884_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); -} - -static int patch_ad1884_auto(struct hda_codec *codec) -{ - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - - err = ad1884_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; -} - -static int patch_ad1884_basic(struct hda_codec *codec) +static int patch_ad1884(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -3240,18 +3615,6 @@ static int patch_ad1884_basic(struct hda_codec *codec) return 0; } -static int patch_ad1884(struct hda_codec *codec) -{ - int board_config; - - board_config = snd_hda_check_board_config(codec, AD1884_MODELS, - ad1884_models, NULL); - if (board_config == AD1884_AUTO) - return patch_ad1884_auto(codec); - else - return patch_ad1884_basic(codec); -} - /* * Lenovo Thinkpad T61/X61 */ @@ -3424,7 +3787,6 @@ static int ad1984_build_pcms(struct hda_codec *codec) /* models */ enum { - AD1984_AUTO, AD1984_BASIC, AD1984_THINKPAD, AD1984_DELL_DESKTOP, @@ -3432,7 +3794,6 @@ enum { }; static const char * const ad1984_models[AD1984_MODELS] = { - [AD1984_AUTO] = "auto", [AD1984_BASIC] = "basic", [AD1984_THINKPAD] = "thinkpad", [AD1984_DELL_DESKTOP] = "dell_desktop", @@ -3451,16 +3812,12 @@ static int patch_ad1984(struct hda_codec *codec) struct ad198x_spec *spec; int board_config, err; - board_config = snd_hda_check_board_config(codec, AD1984_MODELS, - ad1984_models, ad1984_cfg_tbl); - if (board_config == AD1984_AUTO) - return patch_ad1884_auto(codec); - - err = patch_ad1884_basic(codec); + err = patch_ad1884(codec); if (err < 0) return err; spec = codec->spec; - + board_config = snd_hda_check_board_config(codec, AD1984_MODELS, + ad1984_models, ad1984_cfg_tbl); switch (board_config) { case AD1984_BASIC: /* additional digital mics */ @@ -4177,7 +4534,6 @@ static int ad1984a_touchsmart_init(struct hda_codec *codec) */ enum { - AD1884A_AUTO, AD1884A_DESKTOP, AD1884A_LAPTOP, AD1884A_MOBILE, @@ -4188,7 +4544,6 @@ enum { }; static const char * const ad1884a_models[AD1884A_MODELS] = { - [AD1884A_AUTO] = "auto", [AD1884A_DESKTOP] = "desktop", [AD1884A_LAPTOP] = "laptop", [AD1884A_MOBILE] = "mobile", @@ -4217,12 +4572,6 @@ static int patch_ad1884a(struct hda_codec *codec) struct ad198x_spec *spec; int err, board_config; - board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, - ad1884a_models, - ad1884a_cfg_tbl); - if (board_config == AD1884_AUTO) - return patch_ad1884_auto(codec); - err = alloc_ad_spec(codec); if (err < 0) return err; @@ -4254,6 +4603,9 @@ static int patch_ad1884a(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, + ad1884a_models, + ad1884a_cfg_tbl); switch (board_config) { case AD1884A_LAPTOP: spec->mixers[0] = ad1884a_laptop_mixers; @@ -4614,7 +4966,6 @@ static const struct hda_amp_list ad1882_loopbacks[] = { /* models */ enum { - AD1882_AUTO, AD1882_3STACK, AD1882_6STACK, AD1882_3STACK_AUTOMUTE, @@ -4622,20 +4973,11 @@ enum { }; static const char * const ad1882_models[AD1986A_MODELS] = { - [AD1882_AUTO] = "auto", [AD1882_3STACK] = "3stack", [AD1882_6STACK] = "6stack", [AD1882_3STACK_AUTOMUTE] = "3stack-automute", }; -static int ad1882_parse_auto_config(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - spec->beep_dev_nid = 0x10; - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - return ad198x_parse_auto_config(codec); -} static int patch_ad1882(struct hda_codec *codec) { @@ -4647,17 +4989,6 @@ static int patch_ad1882(struct hda_codec *codec) return err; spec = codec->spec; - board_config = snd_hda_check_board_config(codec, AD1882_MODELS, - ad1882_models, NULL); - if (board_config == AD1882_AUTO) { - err = ad1882_parse_auto_config(codec); - if (err < 0) { - ad198x_free(codec); - return err; - } - return 0; - } - err = snd_hda_attach_beep_device(codec, 0x10); if (err < 0) { ad198x_free(codec); @@ -4693,6 +5024,8 @@ static int patch_ad1882(struct hda_codec *codec) codec->patch_ops = ad198x_patch_ops; /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1882_MODELS, + ad1882_models, NULL); switch (board_config) { default: case AD1882_3STACK: diff --git a/trunk/sound/pci/hda/patch_cirrus.c b/trunk/sound/pci/hda/patch_cirrus.c index b9dfbd85d550..7b0b8c305737 100644 --- a/trunk/sound/pci/hda/patch_cirrus.c +++ b/trunk/sound/pci/hda/patch_cirrus.c @@ -24,18 +24,37 @@ #include #include #include -#include #include "hda_codec.h" #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" +#include /* */ struct cs_spec { - struct hda_gen_spec gen; + struct auto_pin_cfg autocfg; + struct hda_multi_out multiout; + struct snd_kcontrol *vmaster_sw; + struct snd_kcontrol *vmaster_vol; + + hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS]; + hda_nid_t slave_dig_outs[2]; + + unsigned int input_idx[AUTO_PIN_LAST]; + unsigned int capsrc_idx[AUTO_PIN_LAST]; + hda_nid_t adc_nid[AUTO_PIN_LAST]; + unsigned int adc_idx[AUTO_PIN_LAST]; + unsigned int num_inputs; + unsigned int cur_input; + unsigned int automic_idx; + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + hda_nid_t dig_in; + + const struct hda_bind_ctls *capture_bind[2]; unsigned int gpio_mask; unsigned int gpio_dir; @@ -43,11 +62,17 @@ struct cs_spec { unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */ + struct hda_pcm pcm_rec[2]; /* PCM information */ + + unsigned int hp_detect:1; + unsigned int mic_detect:1; + unsigned int speaker_2_1:1; /* CS421x */ unsigned int spdif_detect:1; - unsigned int spdif_present:1; unsigned int sense_b:1; hda_nid_t vendor_nid; + struct hda_input_mux input_mux; + unsigned int last_input; }; /* available models with CS420x */ @@ -123,34 +148,756 @@ enum { #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 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 + +/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ +#define CS4213_VENDOR_NID 0x09 + + +static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +{ + 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, 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) +{ + 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, spec->vendor_nid, 0, + AC_VERB_SET_PROC_COEF, coef); +} + + +#define HP_EVENT 1 +#define MIC_EVENT 2 + +/* + * PCM callbacks + */ +static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + +/* + * Digital out + */ +static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, + format, substream); +} + +static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); +} + +static void cs_update_input_select(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + if (spec->cur_adc) + snd_hda_codec_write(codec, spec->cur_adc, 0, + AC_VERB_SET_CONNECT_SEL, + spec->adc_idx[spec->cur_input]); +} + +/* + * Analog capture + */ +static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nid[spec->cur_input]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + cs_update_input_select(codec); + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + return 0; +} + +static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct cs_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +/* + */ +static const struct hda_pcm_stream cs_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = cs_playback_pcm_open, + .prepare = cs_playback_pcm_prepare, + .cleanup = cs_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream cs_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .prepare = cs_capture_pcm_prepare, + .cleanup = cs_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream cs_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .ops = { + .open = cs_dig_playback_pcm_open, + .close = cs_dig_playback_pcm_close, + .prepare = cs_dig_playback_pcm_prepare, + .cleanup = cs_dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream cs_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +static int cs_build_pcms(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->pcm_info = info; + codec->num_pcms = 0; + + info->name = "Cirrus Analog"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + if (spec->speaker_2_1) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nid[spec->cur_input]; + codec->num_pcms++; + + if (!spec->multiout.dig_out_nid && !spec->dig_in) + return 0; + + info++; + info->name = "Cirrus Digital"; + info->pcm_type = spec->autocfg.dig_out_type[0]; + if (!info->pcm_type) + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + cs_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dig_out_nid; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + cs_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + codec->num_pcms++; + + return 0; +} + +/* + * parse codec topology + */ + +static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t dac; + if (!pin) + return 0; + if (snd_hda_get_connections(codec, pin, &dac, 1) != 1) + return 0; + return dac; +} + +static int is_ext_mic(struct hda_codec *codec, unsigned int idx) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t pin = cfg->inputs[idx].pin; + unsigned int val; + if (!is_jack_detectable(codec, pin)) + return 0; + val = snd_hda_codec_get_pincfg(codec, pin); + return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT); +} + +static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin, + unsigned int *idxp) +{ + int i, idx; + hda_nid_t nid; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type != AC_WID_AUD_IN) + continue; + idx = snd_hda_get_conn_index(codec, nid, pin, false); + if (idx >= 0) { + *idxp = idx; + return nid; + } + } + return 0; +} + +static int is_active_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + val = snd_hda_codec_get_pincfg(codec, nid); + return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); +} + +static int parse_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, extra_nids; + hda_nid_t dac; + + for (i = 0; i < cfg->line_outs; i++) { + dac = get_dac(codec, cfg->line_out_pins[i]); + if (!dac) + break; + spec->dac_nid[i] = dac; + } + spec->multiout.num_dacs = i; + spec->multiout.dac_nids = spec->dac_nid; + spec->multiout.max_channels = i * 2; + + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && i == 2) + spec->speaker_2_1 = 1; /* assume 2.1 speakers */ + + /* add HP and speakers */ + extra_nids = 0; + for (i = 0; i < cfg->hp_outs; i++) { + dac = get_dac(codec, cfg->hp_pins[i]); + if (!dac) + break; + if (!i) + spec->multiout.hp_nid = dac; + else + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + for (i = 0; i < cfg->speaker_outs; i++) { + dac = get_dac(codec, cfg->speaker_pins[i]); + if (!dac) + break; + spec->multiout.extra_out_nid[extra_nids++] = dac; + } + + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = 0; + memset(cfg->line_out_pins, 0, sizeof(cfg->line_out_pins)); + } + + return 0; +} + +static int parse_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->input_idx[spec->num_inputs] = i; + spec->capsrc_idx[i] = spec->num_inputs++; + spec->cur_input = i; + spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]); + } + if (!spec->num_inputs) + return 0; + + /* check whether the automatic mic switch is available */ + if (spec->num_inputs == 2 && + cfg->inputs[0].type == AUTO_PIN_MIC && + cfg->inputs[1].type == AUTO_PIN_MIC) { + if (is_ext_mic(codec, cfg->inputs[0].pin)) { + if (!is_ext_mic(codec, cfg->inputs[1].pin)) { + spec->mic_detect = 1; + spec->automic_idx = 0; + } + } else { + if (is_ext_mic(codec, cfg->inputs[1].pin)) { + spec->mic_detect = 1; + spec->automic_idx = 1; + } + } + } + return 0; +} + + +static int parse_digital_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + + if (!cfg->dig_outs) + return 0; + if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1) + return 0; + spec->multiout.dig_out_nid = nid; + spec->multiout.share_spdif = 1; + if (cfg->dig_outs > 1 && + snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) { + spec->slave_dig_outs[0] = nid; + codec->slave_dig_outs = spec->slave_dig_outs; + } + return 0; +} + +static int parse_digital_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int idx; + + if (cfg->dig_in_pin) + spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx); + return 0; +} + +/* + * create mixer controls + */ + +static const char * const dir_sfx[2] = { "Playback", "Capture" }; + +static int add_mute(struct hda_codec *codec, const char *name, int index, + unsigned int pval, int dir, struct snd_kcontrol **kctlp) +{ + char tmp[44]; + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT); + knew.private_value = pval; + snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]); + *kctlp = snd_ctl_new1(&knew, codec); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; + return snd_hda_ctl_add(codec, 0, *kctlp); +} + +static int add_volume(struct hda_codec *codec, const char *name, + int index, unsigned int pval, int dir, + struct snd_kcontrol **kctlp) +{ + char tmp[44]; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT); + knew.private_value = pval; + snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]); + *kctlp = snd_ctl_new1(&knew, codec); + (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG; + return snd_hda_ctl_add(codec, 0, *kctlp); +} + +static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) +{ + unsigned int caps; + + /* set the upper-limit for mixer amp to 0dB */ + caps = query_amp_caps(codec, dac, HDA_OUTPUT); + caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); + caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) + << AC_AMPCAP_NUM_STEPS_SHIFT; + snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); +} + +static int add_vmaster(struct hda_codec *codec, hda_nid_t dac) +{ + struct cs_spec *spec = codec->spec; + unsigned int tlv[4]; + int err; + + spec->vmaster_sw = + snd_ctl_make_virtual_master("Master Playback Switch", NULL); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw); + if (err < 0) + return err; + + snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv); + spec->vmaster_vol = + snd_ctl_make_virtual_master("Master Playback Volume", tlv); + err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol); + if (err < 0) + return err; + return 0; +} + +static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx, + int num_ctls, int type) +{ + struct cs_spec *spec = codec->spec; + const char *name; + int err, index; + struct snd_kcontrol *kctl; + static const char * const speakers[] = { + "Front Speaker", "Surround Speaker", "Bass Speaker" + }; + static const char * const line_outs[] = { + "Front Line Out", "Surround Line Out", "Bass Line Out" + }; + + fix_volume_caps(codec, dac); + if (!spec->vmaster_sw) { + err = add_vmaster(codec, dac); + if (err < 0) + return err; + } + + index = 0; + switch (type) { + case AUTO_PIN_HP_OUT: + name = "Headphone"; + index = idx; + break; + case AUTO_PIN_SPEAKER_OUT: + if (spec->speaker_2_1) + name = idx ? "Bass Speaker" : "Speaker"; + else if (num_ctls > 1) + name = speakers[idx]; + else + name = "Speaker"; + break; + default: + if (num_ctls > 1) + name = line_outs[idx]; + else + name = "Line Out"; + break; + } + + err = add_mute(codec, name, index, + 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, index, + 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; + + return 0; +} + +static int build_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + + for (i = 0; i < cfg->line_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]), + i, cfg->line_outs, cfg->line_out_type); + if (err < 0) + return err; + } + for (i = 0; i < cfg->hp_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->hp_pins[i]), + i, cfg->hp_outs, AUTO_PIN_HP_OUT); + if (err < 0) + return err; + } + for (i = 0; i < cfg->speaker_outs; i++) { + err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]), + i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT); + if (err < 0) + return err; + } + return 0; +} + +/* + */ + +static const struct snd_kcontrol_new cs_capture_ctls[] = { + HDA_BIND_SW("Capture Switch", 0), + HDA_BIND_VOL("Capture Volume", 0), +}; + +static int change_cur_input(struct hda_codec *codec, unsigned int idx, + int force) +{ + struct cs_spec *spec = codec->spec; + + if (spec->cur_input == idx && !force) + return 0; + if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = spec->adc_nid[idx]; + snd_hda_codec_setup_stream(codec, spec->cur_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } + spec->cur_input = idx; + cs_update_input_select(codec); + return 1; +} + +static int cs_capture_source_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; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int idx; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_inputs; + if (uinfo->value.enumerated.item >= spec->num_inputs) + uinfo->value.enumerated.item = spec->num_inputs - 1; + idx = spec->input_idx[uinfo->value.enumerated.item]; + snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, cfg, + uinfo->value.enumerated.name, + sizeof(uinfo->value.enumerated.name), NULL); + return 0; +} + +static int cs_capture_source_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->capsrc_idx[spec->cur_input]; + return 0; +} + +static int cs_capture_source_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; + unsigned int idx = ucontrol->value.enumerated.item[0]; + + if (idx >= spec->num_inputs) + return -EINVAL; + idx = spec->input_idx[idx]; + return change_cur_input(codec, idx, 0); +} + +static const struct snd_kcontrol_new cs_capture_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = cs_capture_source_info, + .get = cs_capture_source_get, + .put = cs_capture_source_put, +}; + +static const struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec, + struct hda_ctl_ops *ops) +{ + struct cs_spec *spec = codec->spec; + struct hda_bind_ctls *bind; + int i, n; + + bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1), + GFP_KERNEL); + if (!bind) + return NULL; + bind->ops = ops; + n = 0; + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!spec->adc_nid[i]) + continue; + bind->values[n++] = + HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3, + spec->adc_idx[i], HDA_INPUT); + } + return bind; +} + +/* add a (input-boost) volume control to the given input pin */ +static int add_input_volume_control(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + int item) +{ + hda_nid_t pin = cfg->inputs[item].pin; + u32 caps; + const char *label; + struct snd_kcontrol *kctl; + + 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; + label = hda_get_autocfg_input_label(codec, cfg, item); + return add_volume(codec, label, 0, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl); +} + +static int build_input(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int i, err; + + 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; + } + } + + if (spec->num_inputs > 1 && !spec->mic_detect) { + err = snd_hda_ctl_add(codec, 0, + snd_ctl_new1(&cs_capture_source, codec)); + if (err < 0) + return err; + } -#define SPDIF_EVENT 0x04 + for (i = 0; i < spec->num_inputs; i++) { + err = add_input_volume_control(codec, &spec->autocfg, i); + if (err < 0) + return err; + } -/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */ -#define CS4213_VENDOR_NID 0x09 + return 0; +} +/* + */ -static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx) +static int build_digital_output(struct hda_codec *codec) { 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, spec->vendor_nid, 0, - AC_VERB_GET_PROC_COEF, 0); + int err; + + if (!spec->multiout.dig_out_nid) + return 0; + + err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid, + spec->pcm_rec[1].pcm_type); + if (err < 0) + return err; + err = snd_hda_create_spdif_share_sw(codec, &spec->multiout); + if (err < 0) + return err; + return 0; } -static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, - unsigned int coef) +static int build_digital_input(struct hda_codec *codec) { 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, spec->vendor_nid, 0, - AC_VERB_SET_PROC_COEF, coef); + if (spec->dig_in) + return snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + return 0; } /* @@ -159,37 +906,187 @@ static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx, * HP/SPK/SPDIF */ -static void cs_automute(struct hda_codec *codec) +static void cs_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) { 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; - /* mute HPs if spdif jack (SENSE_B) is present */ - spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b); + 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]; + if (!is_jack_detectable(codec, nid)) + continue; + hp_present = snd_hda_jack_detect(codec, nid); + if (hp_present) + break; + } - snd_hda_gen_update_outputs(codec); + /* mute speakers if spdif or hp jack is plugged in */ + for (i = 0; i < cfg->speaker_outs; i++) { + int pin_ctl = hp_present ? 0 : PIN_OUT; + /* detect on spdif is specific to CS4210 */ + if (spdif_present && (spec->vendor_nid == CS4210_VENDOR_NID)) + pin_ctl = 0; + nid = cfg->speaker_pins[i]; + snd_hda_set_pin_ctl(codec, nid, pin_ctl); + } if (spec->gpio_eapd_hp) { - unsigned int gpio = spec->gen.hp_jack_present ? + unsigned int gpio = hp_present ? spec->gpio_eapd_hp : spec->gpio_eapd_speaker; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, gpio); } + + /* specific to CS4210 */ + if (spec->vendor_nid == CS4210_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_set_pin_ctl(codec, nid, + (spdif_present && spec->sense_b) ? 0 : PIN_HP); + } + + /* SPDIF TX on/off */ + if (cfg->dig_outs) { + nid = cfg->dig_out_pins[0]; + snd_hda_set_pin_ctl(codec, nid, + spdif_present ? PIN_OUT : 0); + + } + /* Update board GPIOs if neccessary ... */ + } } -static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid) +/* + * Auto-input redirect for CS421x + * Switch max 3 inputs of a single ADC (nid 3) +*/ + +static void cs_automic(struct hda_codec *codec, struct hda_jack_tbl *tbl) { - unsigned int val; - val = snd_hda_codec_get_pincfg(codec, nid); - return (get_defcfg_connect(val) != AC_JACK_PORT_NONE); + 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); + + /* specific to CS421x, single ADC */ + if (spec->vendor_nid == CS420X_VENDOR_NID) { + if (present) + change_cur_input(codec, spec->automic_idx, 0); + else + change_cur_input(codec, !spec->automic_idx, 0); + } else { + if (present) { + if (spec->cur_input != spec->automic_idx) { + spec->last_input = spec->cur_input; + spec->cur_input = spec->automic_idx; + } + } else { + spec->cur_input = spec->last_input; + } + cs_update_input_select(codec); + } +} + +/* + */ + +static void init_output(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + /* mute first */ + for (i = 0; i < spec->multiout.num_dacs; i++) + snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (spec->multiout.hp_nid) + snd_hda_codec_write(codec, spec->multiout.hp_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) { + if (!spec->multiout.extra_out_nid[i]) + break; + snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + } + + /* set appropriate pin controls */ + for (i = 0; i < cfg->line_outs; i++) + snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT); + /* HP */ + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + snd_hda_set_pin_ctl(codec, nid, PIN_HP); + if (!cfg->speaker_outs) + continue; + if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) { + snd_hda_jack_detect_enable_callback(codec, nid, HP_EVENT, cs_automute); + spec->hp_detect = 1; + } + } + + /* Speaker */ + for (i = 0; i < cfg->speaker_outs; i++) + snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT); + + /* SPDIF is enabled on presence detect for CS421x */ + if (spec->hp_detect || spec->spdif_detect) + cs_automute(codec, NULL); } -static void init_input_coef(struct hda_codec *codec) +static void init_input(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int coef; + int i; + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int ctl; + hda_nid_t pin = cfg->inputs[i].pin; + if (!spec->adc_nid[i]) + continue; + /* set appropriate pin control and mute first */ + ctl = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, pin); + snd_hda_set_pin_ctl(codec, pin, ctl); + snd_hda_codec_write(codec, spec->adc_nid[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(spec->adc_idx[i])); + if (spec->mic_detect && spec->automic_idx == i) + snd_hda_jack_detect_enable_callback(codec, pin, MIC_EVENT, cs_automic); + } /* CS420x has multiple ADC, CS421x has single ADC */ if (spec->vendor_nid == CS420X_VENDOR_NID) { + change_cur_input(codec, spec->cur_input, 1); + if (spec->mic_detect) + cs_automic(codec, NULL); + coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG); if (is_active_pin(codec, CS_DMIC2_PIN_NID)) coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */ @@ -200,6 +1097,13 @@ static void init_input_coef(struct hda_codec *codec) */ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef); + } else { + if (spec->mic_detect) + cs_automic(codec, NULL); + else { + spec->cur_adc = spec->adc_nid[spec->cur_input]; + cs_update_input_select(codec); + } } } @@ -272,7 +1176,7 @@ static const struct hda_verb cs_errata_init_verbs[] = { }; /* SPDIF setup */ -static void init_digital_coef(struct hda_codec *codec) +static void init_digital(struct hda_codec *codec) { unsigned int coef; @@ -295,7 +1199,7 @@ static int cs_init(struct hda_codec *codec) snd_hda_sequence_write(codec, cs_coef_init_verbs); - snd_hda_gen_init(codec); + snd_hda_apply_verbs(codec); if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, @@ -306,17 +1210,52 @@ static int cs_init(struct hda_codec *codec) spec->gpio_data); } - init_input_coef(codec); - init_digital_coef(codec); + init_output(codec); + init_input(codec); + init_digital(codec); + + return 0; +} + +static int cs_build_controls(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + int err; + + err = build_output(codec); + if (err < 0) + return err; + err = build_input(codec); + if (err < 0) + return err; + err = build_digital_output(codec); + if (err < 0) + return err; + err = build_digital_input(codec); + if (err < 0) + return err; + err = cs_init(codec); + if (err < 0) + return err; + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; return 0; } -#define cs_free snd_hda_gen_free +static void cs_free(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + kfree(spec->capture_bind[0]); + kfree(spec->capture_bind[1]); + kfree(codec->spec); +} static const struct hda_codec_ops cs_patch_ops = { - .build_controls = snd_hda_gen_build_controls, - .build_pcms = snd_hda_gen_build_pcms, + .build_controls = cs_build_controls, + .build_pcms = cs_build_pcms, .init = cs_init, .free = cs_free, .unsol_event = snd_hda_jack_unsol_event, @@ -327,14 +1266,22 @@ static int cs_parse_auto_config(struct hda_codec *codec) struct cs_spec *spec = codec->spec; int err; - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + err = parse_output(codec); + if (err < 0) + return err; + err = parse_input(codec); + if (err < 0) + return err; + err = parse_digital_output(codec); + if (err < 0) + return err; + err = parse_digital_input(codec); if (err < 0) return err; - return 0; } @@ -484,28 +1431,17 @@ static const struct hda_fixup cs420x_fixups[] = { }, }; -static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid) -{ - struct cs_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return NULL; - codec->spec = spec; - spec->vendor_nid = vendor_nid; - snd_hda_gen_spec_init(&spec->gen); - - return spec; -} - static int patch_cs420x(struct hda_codec *codec) { struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS420X_VENDOR_NID); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + codec->spec = spec; + + spec->vendor_nid = CS420X_VENDOR_NID; snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, cs420x_fixups); @@ -523,6 +1459,7 @@ static int patch_cs420x(struct hda_codec *codec) error: cs_free(codec); + codec->spec = NULL; return err; } @@ -681,7 +1618,7 @@ static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol, } } -static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = { +static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | @@ -726,44 +1663,20 @@ static void cs4210_pinmux_init(struct hda_codec *codec) } } -static void cs4210_spdif_automute(struct hda_codec *codec, - struct hda_jack_tbl *tbl) -{ - struct cs_spec *spec = codec->spec; - bool spdif_present = false; - hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0]; - - /* detect on spdif is specific to CS4210 */ - if (!spec->spdif_detect || - spec->vendor_nid != CS4210_VENDOR_NID) - return; - - spdif_present = snd_hda_jack_detect(codec, spdif_pin); - if (spdif_present == spec->spdif_present) - return; - - spec->spdif_present = spdif_present; - /* SPDIF TX on/off */ - if (spdif_present) - snd_hda_set_pin_ctl(codec, spdif_pin, - spdif_present ? PIN_OUT : 0); - - cs_automute(codec); -} - -static void parse_cs421x_digital(struct hda_codec *codec) +static void init_cs421x_digital(struct hda_codec *codec) { struct cs_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; + 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_jack_detect_enable_callback(codec, nid, SPDIF_EVENT, cs_automute); spec->spdif_detect = 1; - snd_hda_jack_detect_enable_callback(codec, nid, - SPDIF_EVENT, - cs4210_spdif_automute); } } } @@ -778,8 +1691,6 @@ static int cs421x_init(struct hda_codec *codec) cs4210_pinmux_init(codec); } - snd_hda_gen_init(codec); - if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, spec->gpio_mask); @@ -789,61 +1700,233 @@ static int cs421x_init(struct hda_codec *codec) spec->gpio_data); } - init_input_coef(codec); + 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); +} - cs4210_spdif_automute(codec, NULL); +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_build_controls(struct hda_codec *codec) +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 const 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 = "Master"; + + fix_volume_caps(codec, dac); - err = snd_hda_gen_build_controls(codec); + err = add_mute(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); if (err < 0) return err; - if (spec->gen.autocfg.speaker_outs && - spec->vendor_nid == CS4210_VENDOR_NID) { + err = add_volume(codec, name, 0, + HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl); + if (err < 0) + return err; + + if (cfg->speaker_outs && (spec->vendor_nid == CS4210_VENDOR_NID)) { err = snd_hda_ctl_add(codec, 0, - snd_ctl_new1(&cs421x_speaker_boost_ctl, codec)); + snd_ctl_new1(&cs421x_speaker_bost_ctl, codec)); if (err < 0) return err; } + return err; +} + +static int cs421x_build_controls(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + 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; + err = cs421x_init(codec); + if (err < 0) + return err; + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } -static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac) +static int parse_cs421x_input(struct hda_codec *codec) { - unsigned int caps; + struct cs_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; - /* set the upper-limit for mixer amp to 0dB */ - caps = query_amp_caps(codec, dac, HDA_OUTPUT); - caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT); - caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f) - << AC_AMPCAP_NUM_STEPS_SHIFT; - snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps); + 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; - hda_nid_t dac = CS4210_DAC_NID; int err; - fix_volume_caps(codec, dac); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); if (err < 0) return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); + 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; - - parse_cs421x_digital(codec); return 0; } @@ -876,7 +1959,7 @@ static int cs421x_suspend(struct hda_codec *codec) static const struct hda_codec_ops cs421x_patch_ops = { .build_controls = cs421x_build_controls, - .build_pcms = snd_hda_gen_build_pcms, + .build_pcms = cs_build_pcms, .init = cs421x_init, .free = cs_free, .unsol_event = snd_hda_jack_unsol_event, @@ -890,9 +1973,12 @@ static int patch_cs4210(struct hda_codec *codec) struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS4210_VENDOR_NID); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + codec->spec = spec; + + spec->vendor_nid = CS4210_VENDOR_NID; snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, cs421x_fixups); @@ -917,6 +2003,7 @@ static int patch_cs4210(struct hda_codec *codec) error: cs_free(codec); + codec->spec = NULL; return err; } @@ -925,9 +2012,12 @@ static int patch_cs4213(struct hda_codec *codec) struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, CS4213_VENDOR_NID); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; + codec->spec = spec; + + spec->vendor_nid = CS4213_VENDOR_NID; err = cs421x_parse_auto_config(codec); if (err < 0) @@ -938,6 +2028,7 @@ static int patch_cs4213(struct hda_codec *codec) error: cs_free(codec); + codec->spec = NULL; return err; } diff --git a/trunk/sound/pci/hda/patch_conexant.c b/trunk/sound/pci/hda/patch_conexant.c index 2f94acb16bde..a52f5662f69c 100644 --- a/trunk/sound/pci/hda/patch_conexant.c +++ b/trunk/sound/pci/hda/patch_conexant.c @@ -33,9 +33,6 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" -#include "hda_generic.h" - -#define ENABLE_CXT_STATIC_QUIRKS #define CXT_PIN_DIR_IN 0x00 #define CXT_PIN_DIR_OUT 0x01 @@ -56,19 +53,25 @@ #define AUTO_MIC_PORTB (1 << 1) #define AUTO_MIC_PORTC (1 << 2) -struct conexant_spec { - struct hda_gen_spec gen; - - unsigned int beep_amp; +struct pin_dac_pair { + hda_nid_t pin; + hda_nid_t dac; + int type; +}; - /* extra EAPD pins */ - unsigned int num_eapds; - hda_nid_t eapds[4]; +struct imux_info { + hda_nid_t pin; /* input pin NID */ + hda_nid_t adc; /* connected ADC NID */ + hda_nid_t boost; /* optional boost volume NID */ + int index; /* corresponding to autocfg.input */ +}; -#ifdef ENABLE_CXT_STATIC_QUIRKS +struct conexant_spec { const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; + bool vmaster_mute_led; const struct hda_verb *init_verbs[5]; /* initialization verbs * don't forget NULL @@ -85,6 +88,11 @@ struct conexant_spec { unsigned int hp_present; unsigned int line_present; unsigned int auto_mic; + int auto_mic_ext; /* imux_pins[] index for ext mic */ + int auto_mic_dock; /* imux_pins[] index for dock mic */ + int auto_mic_int; /* imux_pins[] index for int mic */ + unsigned int need_dac_fix; + hda_nid_t slave_dig_outs[2]; /* capture */ unsigned int num_adc_nids; @@ -112,13 +120,30 @@ struct conexant_spec { unsigned int spdif_route; + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct hda_input_mux private_imux; + struct imux_info imux_info[HDA_MAX_NUM_INPUTS]; + hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS]; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + struct pin_dac_pair dac_info[8]; + int dac_info_filled; + unsigned int port_d_mode; + unsigned int auto_mute:1; /* used in auto-parser */ + unsigned int detect_line:1; /* Line-out detection enabled */ + unsigned int automute_lines:1; /* automute line-out as well */ + unsigned int automute_hp_lo:1; /* both HP and LO available */ unsigned int dell_automute:1; unsigned int dell_vostro:1; unsigned int ideapad:1; unsigned int thinkpad:1; unsigned int hp_laptop:1; unsigned int asus:1; + unsigned int pin_eapd_ctrls:1; + unsigned int fixup_stereo_dmic:1; + + unsigned int adc_switching:1; unsigned int ext_mic_present; unsigned int recording; @@ -134,48 +159,14 @@ struct conexant_spec { unsigned int dc_enable; unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */ unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */ -#endif /* ENABLE_CXT_STATIC_QUIRKS */ -}; + unsigned int beep_amp; -#ifdef CONFIG_SND_HDA_INPUT_BEEP -#define set_beep_amp(spec, nid, idx, dir) \ - ((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[] = { - 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 */ + /* extra EAPD pins */ + unsigned int num_eapds; + hda_nid_t eapds[4]; }; -/* create beep controls if needed */ -static int add_beep_ctls(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int err; - - 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; - } - } - return 0; -} -#else -#define set_beep_amp(spec, nid, idx, dir) /* NOP */ -#define add_beep_ctls(codec) 0 -#endif - - -#ifdef ENABLE_CXT_STATIC_QUIRKS static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -344,6 +335,8 @@ static const struct hda_pcm_stream cx5051_pcm_analog_capture = { }, }; +static bool is_2_1_speaker(struct conexant_spec *spec); + static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -358,6 +351,9 @@ static int conexant_build_pcms(struct hda_codec *codec) spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + if (is_2_1_speaker(spec)) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; if (spec->capture_stream) info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream; else { @@ -388,6 +384,8 @@ static int conexant_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; } return 0; @@ -466,6 +464,15 @@ static const struct snd_kcontrol_new cxt_capture_mixers[] = { {} }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; the actual parameters are overwritten at build */ +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 */ +}; +#endif + static const char * const slave_pfxs[] = { "Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE", NULL @@ -514,9 +521,10 @@ static int conexant_build_controls(struct hda_codec *codec) } if (spec->vmaster_nid && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, slave_pfxs, - "Playback Switch"); + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", true, + &spec->vmaster_mute.sw_kctl); if (err < 0) return err; } @@ -527,9 +535,22 @@ static int conexant_build_controls(struct hda_codec *codec) return err; } - err = add_beep_ctls(codec); - 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 = 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; + } + } +#endif return 0; } @@ -542,6 +563,13 @@ static const struct hda_codec_ops conexant_patch_ops = { .set_power_state = conexant_set_power, }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +#define set_beep_amp(spec, nid, idx, dir) \ + ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif + static int patch_conexant_auto(struct hda_codec *codec); /* * EAPD control @@ -625,6 +653,8 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol, int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, spec->num_channel_mode, &spec->multiout.max_channels); + if (err >= 0 && spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; return err; } @@ -2463,6 +2493,10 @@ static void conexant_check_dig_outs(struct hda_codec *codec, continue; if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1) continue; + if (spec->slave_dig_outs[0]) + nid_loc++; + else + nid_loc = spec->slave_dig_outs; } } @@ -3104,151 +3138,1310 @@ static int patch_cxt5066(struct hda_codec *codec) return 0; } -#endif /* ENABLE_CXT_STATIC_QUIRKS */ - - /* * 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_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t adc = spec->imux_info[spec->cur_mux[0]].adc; + if (spec->adc_switching) { + spec->cur_adc = adc; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + } + snd_hda_codec_setup_stream(codec, adc, stream_tag, 0, format); + return 0; +} - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { - set_beep_amp(spec, nid, 0, HDA_OUTPUT); +static int cx_auto_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + return 0; +} + +static const struct hda_pcm_stream cx_auto_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = cx_auto_capture_pcm_prepare, + .cleanup = cx_auto_capture_pcm_cleanup + }, +}; + +static const hda_nid_t cx_auto_adc_nids[] = { 0x14 }; + +#define get_connection_index(codec, mux, nid)\ + snd_hda_get_conn_index(codec, mux, nid, 0) + +/* get an unassigned DAC from the given list. + * Return the nid if found and reduce the DAC list, or return zero if + * not found + */ +static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t *dacs, int *num_dacs) +{ + int i, nums = *num_dacs; + hda_nid_t ret = 0; + + for (i = 0; i < nums; i++) { + if (get_connection_index(codec, pin, dacs[i]) >= 0) { + ret = dacs[i]; break; } + } + if (!ret) + return 0; + if (--nums > 0) + memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t)); + *num_dacs = nums; + return ret; } -#else -#define cx_auto_parse_beep(codec) -#endif -/* parse EAPDs */ -static void cx_auto_parse_eapd(struct hda_codec *codec) +#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) { - struct conexant_spec *spec = codec->spec; hda_nid_t nid, end_nid; + int nums = 0; end_nid = codec->start_nid + codec->num_nodes; for (nid = codec->start_nid; nid < end_nid; nid++) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) { + dacs[nums++] = nid; + if (nums >= MAX_AUTO_DACS) + break; + } + } + return nums; +} + +/* 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 nums, + int type) +{ + int i, start = nums; + + 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); + if (filled[nums].dac) continue; - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) + if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) { + filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG; continue; - spec->eapds[spec->num_eapds++] = nid; - if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) + } + 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; +} + +/* parse analog output paths */ +static void cx_auto_parse_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t dacs[MAX_AUTO_DACS]; + int i, j, nums, rest; + + 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, 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 || (dac & DAC_SLAVE_FLAG)) + continue; + switch (spec->dac_info[i].type) { + case AUTO_PIN_LINE_OUT: + spec->private_dac_nids[spec->multiout.num_dacs] = dac; + spec->multiout.num_dacs++; break; + case AUTO_PIN_HP_OUT: + case AUTO_PIN_SPEAKER_OUT: + if (!spec->multiout.hp_nid) { + spec->multiout.hp_nid = dac; + break; + } + for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++) + if (!spec->multiout.extra_out_nid[j]) { + spec->multiout.extra_out_nid[j] = dac; + break; + } + break; + } } + spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; - /* NOTE: below is a wild guess; if we have more than two EAPDs, - * it's a new chip, where EAPDs are supposed to be associated to - * pins, and we can control EAPD per pin. - * OTOH, if only one or two EAPDs are found, it's an old chip, - * thus it might control over all pins. - */ - if (spec->num_eapds > 2) - spec->gen.own_eapd_ctl = 1; + for (i = 0; i < cfg->hp_outs; i++) { + if (is_jack_detectable(codec, cfg->hp_pins[i])) { + spec->auto_mute = 1; + break; + } + } + if (spec->auto_mute && + cfg->line_out_pins[0] && + cfg->line_out_type != AUTO_PIN_SPEAKER_OUT && + 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++) { + if (is_jack_detectable(codec, cfg->line_out_pins[i])) { + spec->detect_line = 1; + break; + } + } + spec->automute_lines = spec->detect_line; + } + + spec->vmaster_nid = spec->private_dac_nids[0]; } static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, - hda_nid_t *pins, bool on) + hda_nid_t *pins, bool on); + +static void do_automute(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool on) { + struct conexant_spec *spec = codec->spec; int i; + for (i = 0; i < num_pins; i++) + snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0); + if (spec->pin_eapd_ctrls) + cx_auto_turn_eapd(codec, num_pins, pins, on); +} + +static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) +{ + int i, present = 0; + for (i = 0; i < num_pins; i++) { - if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) - snd_hda_codec_write(codec, pins[i], 0, - AC_VERB_SET_EAPD_BTLENABLE, - on ? 0x02 : 0); + hda_nid_t nid = pins[i]; + if (!nid || !is_jack_detectable(codec, nid)) + break; + present |= snd_hda_jack_detect(codec, nid); } + return present; } -/* turn on/off EAPD according to Master switch */ -static void cx_auto_vmaster_hook(void *private_data, int enabled) +/* auto-mute/unmute speaker and line outs according to headphone jack */ +static void cx_auto_update_speakers(struct hda_codec *codec) { - struct hda_codec *codec = private_data; struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int on = 1; + + /* turn on HP EAPD when HP jacks are present */ + if (spec->pin_eapd_ctrls) { + if (spec->auto_mute) + on = spec->hp_present; + cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); + } - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); + /* mute speakers in auto-mode if HP or LO jacks are plugged */ + if (spec->auto_mute) + on = !(spec->hp_present || + (spec->detect_line && spec->line_present)); + do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, on); + + /* toggle line-out mutes if needed, too */ + /* if LO is a copy of either HP or Speaker, don't need to handle it */ + if (cfg->line_out_pins[0] == cfg->hp_pins[0] || + cfg->line_out_pins[0] == cfg->speaker_pins[0]) + return; + if (spec->auto_mute) { + /* mute LO in auto-mode when HP jack is present */ + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT || + spec->automute_lines) + on = !spec->hp_present; + else + on = 1; + } + do_automute(codec, cfg->line_outs, cfg->line_out_pins, on); } -static int cx_auto_build_controls(struct hda_codec *codec) +static void cx_auto_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { - int err; + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; - err = snd_hda_gen_build_controls(codec); - if (err < 0) - return err; + if (!spec->auto_mute) + return; + spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins); + cx_auto_update_speakers(codec); +} - err = add_beep_ctls(codec); - if (err < 0) - return err; +static void cx_auto_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; - return 0; + if (!spec->auto_mute || !spec->detect_line) + return; + spec->line_present = detect_jacks(codec, cfg->line_outs, + cfg->line_out_pins); + cx_auto_update_speakers(codec); } -static const struct hda_codec_ops cx_auto_patch_ops = { - .build_controls = cx_auto_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = snd_hda_gen_init, - .free = snd_hda_gen_free, - .unsol_event = snd_hda_jack_unsol_event, -}; +static int cx_automute_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + static const char * const texts3[] = { + "Disabled", "Speaker Only", "Line Out+Speaker" + }; -/* - * pin fix-up - */ -enum { - CXT_PINCFG_LENOVO_X200, - CXT_PINCFG_LENOVO_TP410, - CXT_PINCFG_LEMOTE_A1004, - CXT_PINCFG_LEMOTE_A1205, - CXT_FIXUP_STEREO_DMIC, - CXT_FIXUP_INC_MIC_BOOST, -}; + if (spec->automute_hp_lo) + return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); +} -static void cxt_fixup_stereo_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static int cx_automute_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct conexant_spec *spec = codec->spec; - spec->gen.inv_dmic_split = 1; + unsigned int val; + if (!spec->auto_mute) + val = 0; + else if (!spec->automute_lines) + val = 1; + else + val = 2; + ucontrol->value.enumerated.item[0] = val; + return 0; } -static void cxt5066_increase_mic_boost(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static int cx_automute_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; - snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, - (0x3 << AC_AMPCAP_OFFSET_SHIFT) | - (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); + switch (ucontrol->value.enumerated.item[0]) { + case 0: + if (!spec->auto_mute) + return 0; + spec->auto_mute = 0; + break; + case 1: + if (spec->auto_mute && !spec->automute_lines) + return 0; + spec->auto_mute = 1; + spec->automute_lines = 0; + break; + case 2: + if (!spec->automute_hp_lo) + return -EINVAL; + if (spec->auto_mute && spec->automute_lines) + return 0; + spec->auto_mute = 1; + spec->automute_lines = 1; + break; + default: + return -EINVAL; + } + cx_auto_update_speakers(codec); + return 1; } -/* ThinkPad X200 & co with cxt5051 */ -static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { - { 0x16, 0x042140ff }, /* HP (seq# overridden) */ - { 0x17, 0x21a11000 }, /* dock-mic */ - { 0x19, 0x2121103f }, /* dock-HP */ - { 0x1c, 0x21440100 }, /* dock SPDIF out */ - {} -}; - -/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ -static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { - { 0x19, 0x042110ff }, /* HP (seq# overridden) */ - { 0x1a, 0x21a190f0 }, /* dock-mic */ - { 0x1c, 0x212140ff }, /* dock-HP */ - {} +static const struct snd_kcontrol_new cx_automute_mode_enum[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Auto-Mute Mode", + .info = cx_automute_mode_info, + .get = cx_automute_mode_get, + .put = cx_automute_mode_put, + }, + { } }; -/* Lemote A1004/A1205 with cxt5066 */ +static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + return snd_hda_input_mux_info(&spec->private_imux, uinfo); +} + +static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_mux[0]; + return 0; +} + +/* look for the route the given pin from mux and return the index; + * if do_select is set, actually select the route. + */ +static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t pin, hda_nid_t *srcp, + bool do_select, int depth) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int startidx, i, nums; + + switch (get_wcaps_type(get_wcaps(codec, mux))) { + case AC_WID_AUD_IN: + case AC_WID_AUD_SEL: + case AC_WID_AUD_MIX: + break; + default: + return -1; + } + + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) + if (conn[i] == pin) { + if (do_select) + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, i); + if (srcp) + *srcp = mux; + return i; + } + depth++; + if (depth == 2) + return -1; + + /* Try to rotate around connections to avoid one boost controlling + another input path as well */ + startidx = 0; + for (i = 0; i < spec->private_imux.num_items; i++) + if (spec->imux_info[i].pin == pin) { + startidx = i; + break; + } + + for (i = 0; i < nums; i++) { + int j = (i + startidx) % nums; + int ret = __select_input_connection(codec, conn[j], pin, srcp, + do_select, depth); + if (ret >= 0) { + if (do_select) + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, j); + return j; + } + } + return -1; +} + +static void select_input_connection(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t pin) +{ + __select_input_connection(codec, mux, pin, NULL, true, 0); +} + +static int get_input_connection(struct hda_codec *codec, hda_nid_t mux, + hda_nid_t pin) +{ + return __select_input_connection(codec, mux, pin, NULL, false, 0); +} + +static int cx_auto_mux_enum_update(struct hda_codec *codec, + const struct hda_input_mux *imux, + unsigned int idx) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t adc; + int changed = 1; + + if (!imux->num_items) + return 0; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->cur_mux[0] == idx) + changed = 0; + adc = spec->imux_info[idx].adc; + select_input_connection(codec, spec->imux_info[idx].adc, + spec->imux_info[idx].pin); + if (spec->cur_adc && spec->cur_adc != adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = adc; + snd_hda_codec_setup_stream(codec, adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } + spec->cur_mux[0] = idx; + return changed; +} + +static int cx_auto_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + + return cx_auto_mux_enum_update(codec, &spec->private_imux, + ucontrol->value.enumerated.item[0]); +} + +static const struct snd_kcontrol_new cx_auto_capture_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = cx_auto_mux_enum_info, + .get = cx_auto_mux_enum_get, + .put = cx_auto_mux_enum_put + }, + {} +}; + +static bool select_automic(struct hda_codec *codec, int idx, bool detect) +{ + struct conexant_spec *spec = codec->spec; + if (idx < 0) + return false; + if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin)) + return false; + cx_auto_mux_enum_update(codec, &spec->private_imux, idx); + return true; +} + +/* automatic switch internal and external mic */ +static void cx_auto_automic(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct conexant_spec *spec = codec->spec; + + if (!spec->auto_mic) + return; + if (!select_automic(codec, spec->auto_mic_ext, true)) + if (!select_automic(codec, spec->auto_mic_dock, true)) + select_automic(codec, spec->auto_mic_int, false); +} + +/* check whether the pin config is suitable for auto-mic switching; + * auto-mic is enabled only when one int-mic and one ext- and/or + * one dock-mic exist + */ +static void cx_auto_check_auto_mic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int pset[INPUT_PIN_ATTR_NORMAL + 1]; + int i; + + for (i = 0; i < ARRAY_SIZE(pset); i++) + pset[i] = -1; + for (i = 0; i < spec->private_imux.num_items; i++) { + hda_nid_t pin = spec->imux_info[i].pin; + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + int type, attr; + attr = snd_hda_get_input_pin_attr(def_conf); + if (attr == INPUT_PIN_ATTR_UNUSED) + return; /* invalid entry */ + if (attr > INPUT_PIN_ATTR_NORMAL) + attr = INPUT_PIN_ATTR_NORMAL; + if (attr != INPUT_PIN_ATTR_INT && + !is_jack_detectable(codec, pin)) + return; /* non-detectable pin */ + type = get_defcfg_device(def_conf); + if (type != AC_JACK_MIC_IN && + (attr != INPUT_PIN_ATTR_DOCK || type != AC_JACK_LINE_IN)) + return; /* no valid input type */ + if (pset[attr] >= 0) + return; /* already occupied */ + pset[attr] = i; + } + if (pset[INPUT_PIN_ATTR_INT] < 0 || + (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK])) + return; /* no input to switch*/ + spec->auto_mic = 1; + spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL]; + spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK]; + spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT]; +} + +static void cx_auto_parse_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct hda_input_mux *imux; + int i, j; + + imux = &spec->private_imux; + for (i = 0; i < cfg->num_inputs; i++) { + for (j = 0; j < spec->num_adc_nids; j++) { + hda_nid_t adc = spec->adc_nids[j]; + int idx = get_input_connection(codec, adc, + cfg->inputs[i].pin); + if (idx >= 0) { + const char *label; + label = hda_get_autocfg_input_label(codec, cfg, i); + spec->imux_info[imux->num_items].index = i; + spec->imux_info[imux->num_items].boost = 0; + spec->imux_info[imux->num_items].adc = adc; + spec->imux_info[imux->num_items].pin = + cfg->inputs[i].pin; + snd_hda_add_imux_item(imux, label, idx, NULL); + break; + } + } + } + if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items) + cx_auto_check_auto_mic(codec); + 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; + break; + } + } + } +} + +/* get digital-input audio widget corresponding to the given pin */ +static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin) +{ + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(wcaps); + if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) { + if (get_connection_index(codec, nid, pin) >= 0) + return nid; + } + } + return 0; +} + +static void cx_auto_parse_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + + if (cfg->dig_outs && + snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1) + spec->multiout.dig_out_nid = nid; + if (cfg->dig_in_pin) + spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin); +} + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static void cx_auto_parse_beep(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { + set_beep_amp(spec, nid, 0, HDA_OUTPUT); + break; + } +} +#else +#define cx_auto_parse_beep(codec) +#endif + +/* parse EAPDs */ +static void cx_auto_parse_eapd(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + continue; + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) + continue; + spec->eapds[spec->num_eapds++] = nid; + if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) + break; + } + + /* NOTE: below is a wild guess; if we have more than two EAPDs, + * it's a new chip, where EAPDs are supposed to be associated to + * pins, and we can control EAPD per pin. + * OTOH, if only one or two EAPDs are found, it's an old chip, + * thus it might control over all pins. + */ + spec->pin_eapd_ctrls = spec->num_eapds > 2; +} + +static int cx_auto_parse_auto_config(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + + cx_auto_parse_output(codec); + cx_auto_parse_input(codec); + cx_auto_parse_digital(codec); + cx_auto_parse_beep(codec); + cx_auto_parse_eapd(codec); + return 0; +} + +static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool on) +{ + int i; + for (i = 0; i < num_pins; i++) { + if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_EAPD_BTLENABLE, + on ? 0x02 : 0); + } +} + +static void select_connection(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t src) +{ + int idx = get_connection_index(codec, pin, src); + if (idx >= 0) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_CONNECT_SEL, idx); +} + +static void mute_outputs(struct hda_codec *codec, int num_nids, + const hda_nid_t *nids) +{ + int i, val; + + for (i = 0; i < num_nids; i++) { + hda_nid_t nid = nids[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + continue; + if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE) + val = AMP_OUT_MUTE; + else + val = AMP_OUT_ZERO; + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } +} + +static void enable_unsol_pins(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, unsigned int action, + hda_jack_callback cb) +{ + int i; + for (i = 0; i < num_pins; i++) + snd_hda_jack_detect_enable_callback(codec, pins[i], action, cb); +} + +static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return true; + return false; +} + +/* is the given NID found in any of autocfg items? */ +static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + + if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || + found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || + found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) || + found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs)) + return true; + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == nid) + return true; + if (cfg->dig_in_pin == nid) + return true; + return false; +} + +/* clear unsol-event tags on unused pins; Conexant codecs seem to leave + * invalid unsol tags by some reason + */ +static void clear_unsol_on_unused_pins(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + if (!found_in_autocfg(cfg, pin->nid)) + snd_hda_codec_write(codec, pin->nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); + } +} + +/* turn on/off EAPD according to Master switch */ +static void cx_auto_vmaster_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + if (enabled && spec->pin_eapd_ctrls) { + cx_auto_update_speakers(codec); + return; + } + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); +} + +static void cx_auto_init_output(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid; + int i; + + mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids); + for (i = 0; i < cfg->hp_outs; i++) { + unsigned int val = PIN_OUT; + if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) & + AC_PINCAP_HP_DRV) + val |= AC_PINCTL_HP_EN; + snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val); + } + mute_outputs(codec, cfg->hp_outs, cfg->hp_pins); + mute_outputs(codec, cfg->line_outs, cfg->line_out_pins); + mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins); + for (i = 0; i < spec->dac_info_filled; i++) { + 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) { + enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins, + CONEXANT_HP_EVENT, cx_auto_hp_automute); + spec->hp_present = detect_jacks(codec, cfg->hp_outs, + cfg->hp_pins); + if (spec->detect_line) { + enable_unsol_pins(codec, cfg->line_outs, + cfg->line_out_pins, + CONEXANT_LINE_EVENT, + cx_auto_line_automute); + spec->line_present = + detect_jacks(codec, cfg->line_outs, + cfg->line_out_pins); + } + } + cx_auto_update_speakers(codec); + /* turn on all EAPDs if no individual EAPD control is available */ + if (!spec->pin_eapd_ctrls) + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); + clear_unsol_on_unused_pins(codec); +} + +static void cx_auto_init_input(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, val; + + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t nid = spec->adc_nids[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + continue; + if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE) + val = AMP_IN_MUTE(0); + else + val = AMP_IN_UNMUTE(0); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + val); + } + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int type = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + type |= snd_hda_get_default_vref(codec, pin); + snd_hda_set_pin_ctl(codec, pin, type); + } + + if (spec->auto_mic) { + if (spec->auto_mic_ext >= 0) { + snd_hda_jack_detect_enable_callback(codec, + cfg->inputs[spec->auto_mic_ext].pin, + CONEXANT_MIC_EVENT, cx_auto_automic); + } + if (spec->auto_mic_dock >= 0) { + snd_hda_jack_detect_enable_callback(codec, + cfg->inputs[spec->auto_mic_dock].pin, + CONEXANT_MIC_EVENT, cx_auto_automic); + } + cx_auto_automic(codec, NULL); + } else { + select_input_connection(codec, spec->imux_info[0].adc, + spec->imux_info[0].pin); + } +} + +static void cx_auto_init_digital(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (spec->multiout.dig_out_nid) + snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT); + if (spec->dig_in_nid) + snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN); +} + +static int cx_auto_init(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_apply_verbs(codec); + cx_auto_init_output(codec); + cx_auto_init_input(codec); + cx_auto_init_digital(codec); + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + return 0; +} + +static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, + const char *dir, int cidx, + hda_nid_t nid, int hda_dir, int amp_idx, int chs) +{ + static char name[44]; + static struct snd_kcontrol_new knew[] = { + HDA_CODEC_VOLUME(name, 0, 0, 0), + HDA_CODEC_MUTE(name, 0, 0, 0), + }; + static const char * const sfx[2] = { "Volume", "Switch" }; + int i, err; + + for (i = 0; i < 2; i++) { + struct snd_kcontrol *kctl; + knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx, + hda_dir); + knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; + knew[i].index = cidx; + snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); + kctl = snd_ctl_new1(&knew[i], codec); + if (!kctl) + return -ENOMEM; + err = snd_hda_ctl_add(codec, nid, kctl); + if (err < 0) + return err; + if (!(query_amp_caps(codec, nid, hda_dir) & + (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))) + break; + } + return 0; +} + +#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ + cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3) + +#define cx_auto_add_pb_volume(codec, nid, str, idx) \ + cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) + +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; + 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); + return 0; +} + +static bool is_2_1_speaker(struct conexant_spec *spec) +{ + int i, type, num_spk = 0; + + for (i = 0; i < spec->dac_info_filled; i++) { + type = spec->dac_info[i].type; + if (type == AUTO_PIN_LINE_OUT) + type = spec->autocfg.line_out_type; + if (type == AUTO_PIN_SPEAKER_OUT) + num_spk++; + } + return (num_spk == 2 && spec->autocfg.line_out_type != AUTO_PIN_LINE_OUT); +} + +static int cx_auto_build_output_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int i, err; + int num_line = 0, num_hp = 0, num_spk = 0; + bool speaker_2_1; + static const char * const texts[3] = { "Front", "Surround", "CLFE" }; + + if (spec->dac_info_filled == 1) + return try_add_pb_volume(codec, spec->dac_info[0].dac, + spec->dac_info[0].pin, + "Master", 0); + + speaker_2_1 = is_2_1_speaker(spec); + + for (i = 0; i < spec->dac_info_filled; i++) { + const char *label; + int idx, type; + 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; + switch (type) { + case AUTO_PIN_LINE_OUT: + default: + label = texts[num_line++]; + idx = 0; + break; + case AUTO_PIN_HP_OUT: + label = "Headphone"; + idx = num_hp++; + break; + case AUTO_PIN_SPEAKER_OUT: + if (speaker_2_1) { + label = num_spk++ ? "Bass Speaker" : "Speaker"; + idx = 0; + } else { + label = "Speaker"; + idx = num_spk++; + } + break; + } + err = try_add_pb_volume(codec, dac, + spec->dac_info[i].pin, + label, idx); + if (err < 0) + return err; + } + + if (spec->auto_mute) { + err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum); + if (err < 0) + return err; + } + + return 0; +} + +/* Returns zero if this is a normal stereo channel, and non-zero if it should + be split in two independent channels. + dest_label must be at least 44 characters. */ +static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label, + char *dest_label, int nid) +{ + struct conexant_spec *spec = codec->spec; + int i; + + if (!spec->fixup_stereo_dmic) + return 0; + + for (i = 0; i < AUTO_CFG_MAX_INS; i++) { + int def_conf; + if (spec->autocfg.inputs[i].pin != nid) + continue; + + if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC) + return 0; + def_conf = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) + return 0; + + /* Finally found the inverted internal mic! */ + snprintf(dest_label, 44, "Inverted %s", label); + return 1; + } + return 0; +} + +static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, + const char *label, const char *pfx, + int cidx) +{ + struct conexant_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_adc_nids; i++) { + char rightch_label[44]; + hda_nid_t adc_nid = spec->adc_nids[i]; + int idx = get_input_connection(codec, adc_nid, nid); + if (idx < 0) + continue; + if (codec->single_adc_amp) + idx = 0; + + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + /* Make two independent kcontrols for left and right */ + int err = cx_auto_add_volume_idx(codec, label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 2); + } + return cx_auto_add_volume_idx(codec, label, pfx, + cidx, adc_nid, HDA_INPUT, idx, 3); + } + return 0; +} + +static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx, + const char *label, int cidx) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t mux, nid; + int i, con; + + nid = spec->imux_info[idx].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + char rightch_label[44]; + if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) { + int err = cx_auto_add_volume_idx(codec, label, " Boost", + cidx, nid, HDA_INPUT, 0, 1); + if (err < 0) + return err; + return cx_auto_add_volume_idx(codec, rightch_label, " Boost", + cidx, nid, HDA_INPUT, 0, 2); + } + return cx_auto_add_volume(codec, label, " Boost", cidx, + nid, HDA_INPUT); + } + con = __select_input_connection(codec, spec->imux_info[idx].adc, nid, + &mux, false, 0); + if (con < 0) + return 0; + for (i = 0; i < idx; i++) { + if (spec->imux_info[i].boost == mux) + return 0; /* already present */ + } + + if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) { + spec->imux_info[idx].boost = mux; + return cx_auto_add_volume(codec, label, " Boost", cidx, + mux, HDA_OUTPUT); + } + return 0; +} + +static int cx_auto_build_input_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux; + const char *prev_label; + int input_conn[HDA_MAX_NUM_INPUTS]; + int i, j, err, cidx; + int multi_connection; + + if (!imux->num_items) + return 0; + + multi_connection = 0; + for (i = 0; i < imux->num_items; i++) { + cidx = get_input_connection(codec, spec->imux_info[i].adc, + spec->imux_info[i].pin); + if (cidx < 0) + continue; + input_conn[i] = spec->imux_info[i].adc; + if (!codec->single_adc_amp) + input_conn[i] |= cidx << 8; + if (i > 0 && input_conn[i] != input_conn[0]) + multi_connection = 1; + } + + prev_label = NULL; + cidx = 0; + for (i = 0; i < imux->num_items; i++) { + hda_nid_t nid = spec->imux_info[i].pin; + const char *label; + + label = hda_get_autocfg_input_label(codec, &spec->autocfg, + spec->imux_info[i].index); + if (label == prev_label) + cidx++; + else + cidx = 0; + prev_label = label; + + err = cx_auto_add_boost_volume(codec, i, label, cidx); + if (err < 0) + return err; + + if (!multi_connection) { + if (i > 0) + continue; + err = cx_auto_add_capture_volume(codec, nid, + "Capture", "", cidx); + } else { + bool dup_found = false; + for (j = 0; j < i; j++) { + if (input_conn[j] == input_conn[i]) { + dup_found = true; + break; + } + } + if (dup_found) + continue; + err = cx_auto_add_capture_volume(codec, nid, + label, " Capture", cidx); + } + if (err < 0) + return err; + } + + if (spec->private_imux.num_items > 1 && !spec->auto_mic) { + err = snd_hda_add_new_ctls(codec, cx_auto_capture_mixers); + if (err < 0) + return err; + } + + return 0; +} + +static int cx_auto_build_controls(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int err; + + err = cx_auto_build_output_controls(codec); + if (err < 0) + return err; + err = cx_auto_build_input_controls(codec); + if (err < 0) + return err; + err = conexant_build_controls(codec); + if (err < 0) + return err; + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + if (spec->vmaster_mute.sw_kctl) { + spec->vmaster_mute.hook = cx_auto_vmaster_hook; + err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, + spec->vmaster_mute_led); + if (err < 0) + return err; + } + return 0; +} + +static int cx_auto_search_adcs(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + hda_nid_t nid, end_nid; + + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int caps = get_wcaps(codec, nid); + if (get_wcaps_type(caps) != AC_WID_AUD_IN) + continue; + if (caps & AC_WCAP_DIGITAL) + continue; + if (snd_BUG_ON(spec->num_adc_nids >= + ARRAY_SIZE(spec->private_adc_nids))) + break; + spec->private_adc_nids[spec->num_adc_nids++] = nid; + } + spec->adc_nids = spec->private_adc_nids; + return 0; +} + +static const struct hda_codec_ops cx_auto_patch_ops = { + .build_controls = cx_auto_build_controls, + .build_pcms = conexant_build_pcms, + .init = cx_auto_init, + .free = conexant_free, + .unsol_event = snd_hda_jack_unsol_event, +}; + +/* + * pin fix-up + */ +enum { + CXT_PINCFG_LENOVO_X200, + CXT_PINCFG_LENOVO_TP410, + CXT_PINCFG_LEMOTE_A1004, + CXT_PINCFG_LEMOTE_A1205, + CXT_FIXUP_STEREO_DMIC, + CXT_FIXUP_INC_MIC_BOOST, +}; + +static void cxt_fixup_stereo_dmic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + spec->fixup_stereo_dmic = 1; +} + +static void cxt5066_increase_mic_boost(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT, + (0x3 << AC_AMPCAP_OFFSET_SHIFT) | + (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); +} + +/* ThinkPad X200 & co with cxt5051 */ +static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { + { 0x16, 0x042140ff }, /* HP (seq# overridden) */ + { 0x17, 0x21a11000 }, /* dock-mic */ + { 0x19, 0x2121103f }, /* dock-HP */ + { 0x1c, 0x21440100 }, /* dock SPDIF out */ + {} +}; + +/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */ +static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = { + { 0x19, 0x042110ff }, /* HP (seq# overridden) */ + { 0x1a, 0x21a190f0 }, /* dock-mic */ + { 0x1c, 0x212140ff }, /* dock-HP */ + {} +}; + +/* Lemote A1004/A1205 with cxt5066 */ static const struct hda_pintbl cxt_pincfg_lemote[] = { { 0x1a, 0x90a10020 }, /* Internal mic */ { 0x1b, 0x03a11020 }, /* External mic */ @@ -3336,14 +4529,8 @@ static int patch_conexant_auto(struct hda_codec *codec) spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) return -ENOMEM; - snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; - cx_auto_parse_beep(codec); - cx_auto_parse_eapd(codec); - if (spec->gen.own_eapd_ctl) - spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; - switch (codec->vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; @@ -3359,6 +4546,8 @@ static int patch_conexant_auto(struct hda_codec *codec) break; } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + /* Show mute-led control only on HP laptops * This is a sort of white-list: on HP laptops, EAPD corresponds * only to the mute-LED without actualy amp function. Meanwhile, @@ -3367,20 +4556,20 @@ static int patch_conexant_auto(struct hda_codec *codec) */ switch (codec->subsystem_id >> 16) { case 0x103c: - spec->gen.vmaster_mute_enum = 1; + spec->vmaster_mute_led = 1; break; } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = cx_auto_search_adcs(codec); if (err < 0) - goto error; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - goto error; - + return err; + err = cx_auto_parse_auto_config(codec); + if (err < 0) { + kfree(codec->spec); + codec->spec = NULL; + return err; + } + spec->capture_stream = &cx_auto_pcm_analog_capture; codec->patch_ops = cx_auto_patch_ops; if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); @@ -3397,19 +4586,8 @@ static int patch_conexant_auto(struct hda_codec *codec) } return 0; - - error: - snd_hda_gen_free(codec); - return err; } -#ifndef ENABLE_CXT_STATIC_QUIRKS -#define patch_cxt5045 patch_conexant_auto -#define patch_cxt5047 patch_conexant_auto -#define patch_cxt5051 patch_conexant_auto -#define patch_cxt5066 patch_conexant_auto -#endif - /* */ diff --git a/trunk/sound/pci/hda/patch_sigmatel.c b/trunk/sound/pci/hda/patch_sigmatel.c index a7eed73ab34e..9cc4cb9b4bd2 100644 --- a/trunk/sound/pci/hda/patch_sigmatel.c +++ b/trunk/sound/pci/hda/patch_sigmatel.c @@ -50,6 +50,7 @@ enum { }; enum { + STAC_AUTO, STAC_REF, STAC_9200_OQO, STAC_9200_DELL_D21, @@ -65,11 +66,11 @@ enum { STAC_9200_M4, STAC_9200_M4_2, STAC_9200_PANASONIC, - STAC_9200_EAPD_INIT, STAC_9200_MODELS }; enum { + STAC_9205_AUTO, STAC_9205_REF, STAC_9205_DELL_M42, STAC_9205_DELL_M43, @@ -79,6 +80,7 @@ enum { }; enum { + STAC_92HD73XX_AUTO, STAC_92HD73XX_NO_JD, /* no jack-detection */ STAC_92HD73XX_REF, STAC_92HD73XX_INTEL, @@ -91,6 +93,7 @@ enum { }; enum { + STAC_92HD83XXX_AUTO, STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, @@ -102,11 +105,11 @@ enum { STAC_92HD83XXX_HP_INV_LED, STAC_92HD83XXX_HP_MIC_LED, STAC_92HD83XXX_HEADSET_JACK, - STAC_92HD83XXX_HP, STAC_92HD83XXX_MODELS }; enum { + STAC_92HD71BXX_AUTO, STAC_92HD71BXX_REF, STAC_DELL_M4_1, STAC_DELL_M4_2, @@ -116,13 +119,11 @@ enum { STAC_HP_DV5, STAC_HP_HDX, STAC_HP_DV4_1222NR, - STAC_92HD71BXX_HP, - STAC_92HD71BXX_NO_DMIC, - STAC_92HD71BXX_NO_SMUX, STAC_92HD71BXX_MODELS }; enum { + STAC_925x_AUTO, STAC_925x_REF, STAC_M1, STAC_M1_2, @@ -135,6 +136,7 @@ enum { }; enum { + STAC_922X_AUTO, STAC_D945_REF, STAC_D945GTP3, STAC_D945GTP5, @@ -143,32 +145,39 @@ enum { STAC_INTEL_MAC_V3, STAC_INTEL_MAC_V4, STAC_INTEL_MAC_V5, - STAC_INTEL_MAC_AUTO, + STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter + * is given, one of the above models will be + * chosen according to the subsystem id. */ + /* for backward compatibility */ + STAC_MACMINI, + STAC_MACBOOK, + STAC_MACBOOK_PRO_V1, + STAC_MACBOOK_PRO_V2, + STAC_IMAC_INTEL, + STAC_IMAC_INTEL_20, STAC_ECS_202, STAC_922X_DELL_D81, STAC_922X_DELL_D82, STAC_922X_DELL_M81, STAC_922X_DELL_M82, - STAC_922X_INTEL_MAC_GPIO, STAC_922X_MODELS }; enum { + STAC_927X_AUTO, STAC_D965_REF_NO_JD, /* no jack-detection */ STAC_D965_REF, STAC_D965_3ST, STAC_D965_5ST, STAC_D965_5ST_NO_FP, - STAC_D965_VERBS, STAC_DELL_3ST, STAC_DELL_BIOS, - STAC_DELL_BIOS_SPDIF, - STAC_927X_DELL_DMIC, STAC_927X_VOLKNOB, STAC_927X_MODELS }; enum { + STAC_9872_AUTO, STAC_9872_VAIO, STAC_9872_MODELS }; @@ -187,6 +196,7 @@ struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; unsigned int num_mixers; + int board_config; unsigned int eapd_switch: 1; unsigned int surr_switch: 1; unsigned int alt_switch: 1; @@ -196,7 +206,6 @@ struct sigmatel_spec { unsigned int auto_mic:1; unsigned int linear_tone_beep:1; unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */ - unsigned int volknob_init:1; /* special volume-knob initialization */ /* gpio lines */ unsigned int eapd_mask; @@ -208,7 +217,6 @@ struct sigmatel_spec { unsigned int gpio_led_polarity; unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */ unsigned int vref_led; - int default_polarity; unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */ bool mic_mute_led_on; /* current mic mute state */ @@ -590,15 +598,6 @@ static const hda_nid_t stac9205_pin_nids[12] = { 0x21, 0x22, }; -static int stac_add_event(struct hda_codec *codec, hda_nid_t nid, - unsigned char type, int data); -static int stac_add_hp_bass_switch(struct hda_codec *codec); -static void stac92xx_auto_set_pinctl(struct hda_codec *codec, - hda_nid_t nid, int pin_type); -static int hp_bnb2011_with_dock(struct hda_codec *codec); -static int hp_blike_system(u32 subsystem_id); -static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity); - static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -943,6 +942,8 @@ static const struct hda_verb stac922x_core_init[] = { }; static const struct hda_verb d965_core_init[] = { + /* set master volume and direct control */ + { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, /* unmute node 0x1b */ { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, /* select node 0x03 as DAC */ @@ -1196,40 +1197,18 @@ static int stac92xx_build_controls(struct hda_codec *codec) return 0; } -static const struct hda_pintbl ref9200_pin_configs[] = { - { 0x08, 0x01c47010 }, - { 0x09, 0x01447010 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01114010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, - {} +static const unsigned int ref9200_pin_configs[8] = { + 0x01c47010, 0x01447010, 0x0221401f, 0x01114010, + 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, }; -static const struct hda_pintbl gateway9200_m4_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, - {} +static const unsigned int gateway9200_m4_pin_configs[8] = { + 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, + 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, }; - -static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = { - { 0x08, 0x400000fe }, - { 0x09, 0x404500f4 }, - { 0x0d, 0x400100f0 }, - { 0x0e, 0x90110010 }, - { 0x0f, 0x400100f1 }, - { 0x10, 0x02a1902e }, - { 0x11, 0x500000f2 }, - { 0x12, 0x500000f3 }, - {} +static const unsigned int gateway9200_m4_2_pin_configs[8] = { + 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010, + 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3, }; /* @@ -1238,16 +1217,9 @@ static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = { 102801DE 102801E8 */ -static const struct hda_pintbl dell9200_d21_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x02214030 }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x02a19020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x01813122 }, - {} +static const unsigned int dell9200_d21_pin_configs[8] = { + 0x400001f0, 0x400001f1, 0x02214030, 0x01014010, + 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, }; /* @@ -1255,16 +1227,9 @@ static const struct hda_pintbl dell9200_d21_pin_configs[] = { 102801C0 102801C1 */ -static const struct hda_pintbl dell9200_d22_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x01813020 }, - { 0x10, 0x02a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x400001f2 }, - {} +static const unsigned int dell9200_d22_pin_configs[8] = { + 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, + 0x01813020, 0x02a19021, 0x90100140, 0x400001f2, }; /* @@ -1276,16 +1241,9 @@ static const struct hda_pintbl dell9200_d22_pin_configs[] = { 102801DA 102801E3 */ -static const struct hda_pintbl dell9200_d23_pin_configs[] = { - { 0x08, 0x400001f0 }, - { 0x09, 0x400001f1 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01014010 }, - { 0x0f, 0x01813020 }, - { 0x10, 0x01a19021 }, - { 0x11, 0x90100140 }, - { 0x12, 0x400001f2 }, - {} +static const unsigned int dell9200_d23_pin_configs[8] = { + 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010, + 0x01813020, 0x01a19021, 0x90100140, 0x400001f2, }; @@ -1294,16 +1252,9 @@ static const struct hda_pintbl dell9200_d23_pin_configs[] = { 102801B5 (Dell Inspiron 630m) 102801D8 (Dell Inspiron 640m) */ -static const struct hda_pintbl dell9200_m21_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x03441340 }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fc }, - { 0x12, 0x403003fd }, - {} +static const unsigned int dell9200_m21_pin_configs[8] = { + 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310, + 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd, }; /* @@ -1314,16 +1265,9 @@ static const struct hda_pintbl dell9200_m21_pin_configs[] = { 102801D4 102801D6 */ -static const struct hda_pintbl dell9200_m22_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x0144131f }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x90a70321 }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fb }, - { 0x12, 0x40f000fc }, - {} +static const unsigned int dell9200_m22_pin_configs[8] = { + 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310, + 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc, }; /* @@ -1331,16 +1275,9 @@ static const struct hda_pintbl dell9200_m22_pin_configs[] = { 102801CE (Dell XPS M1710) 102801CF (Dell Precision M90) */ -static const struct hda_pintbl dell9200_m23_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421421f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x04a1102e }, - { 0x11, 0x90170311 }, - { 0x12, 0x403003fc }, - {} +static const unsigned int dell9200_m23_pin_configs[8] = { + 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310, + 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc, }; /* @@ -1350,16 +1287,9 @@ static const struct hda_pintbl dell9200_m23_pin_configs[] = { 102801CB (Dell Latitude 120L) 102801D3 */ -static const struct hda_pintbl dell9200_m24_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x404003fb }, - { 0x0d, 0x0321121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fc }, - { 0x10, 0x03a11020 }, - { 0x11, 0x401003fd }, - { 0x12, 0x403003fe }, - {} +static const unsigned int dell9200_m24_pin_configs[8] = { + 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310, + 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe, }; /* @@ -1368,16 +1298,9 @@ static const struct hda_pintbl dell9200_m24_pin_configs[] = { 102801EE 102801EF */ -static const struct hda_pintbl dell9200_m25_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fb }, - { 0x10, 0x04a11020 }, - { 0x11, 0x401003fc }, - { 0x12, 0x403003fd }, - {} +static const unsigned int dell9200_m25_pin_configs[8] = { + 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, + 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd, }; /* @@ -1385,163 +1308,64 @@ static const struct hda_pintbl dell9200_m25_pin_configs[] = { 102801F5 (Dell Inspiron 1501) 102801F6 */ -static const struct hda_pintbl dell9200_m26_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x404003fb }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x408003fc }, - { 0x10, 0x04a11020 }, - { 0x11, 0x401003fd }, - { 0x12, 0x403003fe }, - {} +static const unsigned int dell9200_m26_pin_configs[8] = { + 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310, + 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe, }; /* STAC 9200-32 102801CD (Dell Inspiron E1705/9400) */ -static const struct hda_pintbl dell9200_m27_pin_configs[] = { - { 0x08, 0x40c003fa }, - { 0x09, 0x01441340 }, - { 0x0d, 0x0421121f }, - { 0x0e, 0x90170310 }, - { 0x0f, 0x90170310 }, - { 0x10, 0x04a11020 }, - { 0x11, 0x90170310 }, - { 0x12, 0x40f003fc }, - {} -}; - -static const struct hda_pintbl oqo9200_pin_configs[] = { - { 0x08, 0x40c000f0 }, - { 0x09, 0x404000f1 }, - { 0x0d, 0x0221121f }, - { 0x0e, 0x02211210 }, - { 0x0f, 0x90170111 }, - { 0x10, 0x90a70120 }, - { 0x11, 0x400000f2 }, - { 0x12, 0x400000f3 }, - {} -}; - - -static void stac9200_fixup_panasonic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - spec->gpio_mask = spec->gpio_dir = 0x09; - spec->gpio_data = 0x00; - break; - case HDA_FIXUP_ACT_PROBE: - /* CF-74 has no headphone detection, and the driver should *NOT* - * do detection and HP/speaker toggle because the hardware does it. - */ - spec->hp_detect = 0; - break; - } -} - - -static const struct hda_fixup stac9200_fixups[] = { - [STAC_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref9200_pin_configs, - }, - [STAC_9200_OQO] = { - .type = HDA_FIXUP_PINS, - .v.pins = oqo9200_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_DELL_D21] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d21_pin_configs, - }, - [STAC_9200_DELL_D22] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d22_pin_configs, - }, - [STAC_9200_DELL_D23] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_d23_pin_configs, - }, - [STAC_9200_DELL_M21] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m21_pin_configs, - }, - [STAC_9200_DELL_M22] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m22_pin_configs, - }, - [STAC_9200_DELL_M23] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m23_pin_configs, - }, - [STAC_9200_DELL_M24] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m24_pin_configs, - }, - [STAC_9200_DELL_M25] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m25_pin_configs, - }, - [STAC_9200_DELL_M26] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m26_pin_configs, - }, - [STAC_9200_DELL_M27] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell9200_m27_pin_configs, - }, - [STAC_9200_M4] = { - .type = HDA_FIXUP_PINS, - .v.pins = gateway9200_m4_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_M4_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = gateway9200_m4_2_pin_configs, - .chained = true, - .chain_id = STAC_9200_EAPD_INIT, - }, - [STAC_9200_PANASONIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9200_fixup_panasonic, - }, - [STAC_9200_EAPD_INIT] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - {} - }, - }, -}; - -static const struct hda_model_fixup stac9200_models[] = { - { .id = STAC_REF, .name = "ref" }, - { .id = STAC_9200_OQO, .name = "oqo" }, - { .id = STAC_9200_DELL_D21, .name = "dell-d21" }, - { .id = STAC_9200_DELL_D22, .name = "dell-d22" }, - { .id = STAC_9200_DELL_D23, .name = "dell-d23" }, - { .id = STAC_9200_DELL_M21, .name = "dell-m21" }, - { .id = STAC_9200_DELL_M22, .name = "dell-m22" }, - { .id = STAC_9200_DELL_M23, .name = "dell-m23" }, - { .id = STAC_9200_DELL_M24, .name = "dell-m24" }, - { .id = STAC_9200_DELL_M25, .name = "dell-m25" }, - { .id = STAC_9200_DELL_M26, .name = "dell-m26" }, - { .id = STAC_9200_DELL_M27, .name = "dell-m27" }, - { .id = STAC_9200_M4, .name = "gateway-m4" }, - { .id = STAC_9200_M4_2, .name = "gateway-m4-2" }, - { .id = STAC_9200_PANASONIC, .name = "panasonic" }, - {} -}; - -static const struct snd_pci_quirk stac9200_fixup_tbl[] = { +static const unsigned int dell9200_m27_pin_configs[8] = { + 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310, + 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc, +}; + +static const unsigned int oqo9200_pin_configs[8] = { + 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210, + 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3, +}; + + +static const unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { + [STAC_REF] = ref9200_pin_configs, + [STAC_9200_OQO] = oqo9200_pin_configs, + [STAC_9200_DELL_D21] = dell9200_d21_pin_configs, + [STAC_9200_DELL_D22] = dell9200_d22_pin_configs, + [STAC_9200_DELL_D23] = dell9200_d23_pin_configs, + [STAC_9200_DELL_M21] = dell9200_m21_pin_configs, + [STAC_9200_DELL_M22] = dell9200_m22_pin_configs, + [STAC_9200_DELL_M23] = dell9200_m23_pin_configs, + [STAC_9200_DELL_M24] = dell9200_m24_pin_configs, + [STAC_9200_DELL_M25] = dell9200_m25_pin_configs, + [STAC_9200_DELL_M26] = dell9200_m26_pin_configs, + [STAC_9200_DELL_M27] = dell9200_m27_pin_configs, + [STAC_9200_M4] = gateway9200_m4_pin_configs, + [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs, + [STAC_9200_PANASONIC] = ref9200_pin_configs, +}; + +static const char * const stac9200_models[STAC_9200_MODELS] = { + [STAC_AUTO] = "auto", + [STAC_REF] = "ref", + [STAC_9200_OQO] = "oqo", + [STAC_9200_DELL_D21] = "dell-d21", + [STAC_9200_DELL_D22] = "dell-d22", + [STAC_9200_DELL_D23] = "dell-d23", + [STAC_9200_DELL_M21] = "dell-m21", + [STAC_9200_DELL_M22] = "dell-m22", + [STAC_9200_DELL_M23] = "dell-m23", + [STAC_9200_DELL_M24] = "dell-m24", + [STAC_9200_DELL_M25] = "dell-m25", + [STAC_9200_DELL_M26] = "dell-m26", + [STAC_9200_DELL_M27] = "dell-m27", + [STAC_9200_M4] = "gateway-m4", + [STAC_9200_M4_2] = "gateway-m4-2", + [STAC_9200_PANASONIC] = "panasonic", +}; + +static const struct snd_pci_quirk stac9200_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), @@ -1617,159 +1441,70 @@ static const struct snd_pci_quirk stac9200_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref925x_pin_configs[] = { - { 0x07, 0x40c003f0 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x01813022 }, - { 0x0b, 0x02a19021 }, - { 0x0c, 0x90a70320 }, - { 0x0d, 0x02214210 }, - { 0x10, 0x01019020 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int ref925x_pin_configs[8] = { + 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, + 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, }; -static const struct hda_pintbl stac925xM1_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM1_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM1_2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM1_2_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM2_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM2_2_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM2_2_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM3_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x503303f3 }, - {} +static const unsigned int stac925xM3_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3, }; -static const struct hda_pintbl stac925xM5_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x9033032e }, - {} +static const unsigned int stac925xM5_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e, }; -static const struct hda_pintbl stac925xM6_pin_configs[] = { - { 0x07, 0x40c003f4 }, - { 0x08, 0x424503f2 }, - { 0x0a, 0x400000f3 }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x40a000f0 }, - { 0x0d, 0x90100210 }, - { 0x10, 0x400003f1 }, - { 0x11, 0x90330320 }, - {} +static const unsigned int stac925xM6_pin_configs[8] = { + 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020, + 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320, }; -static const struct hda_fixup stac925x_fixups[] = { - [STAC_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref925x_pin_configs, - }, - [STAC_M1] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM1_pin_configs, - }, - [STAC_M1_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM1_2_pin_configs, - }, - [STAC_M2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM2_pin_configs, - }, - [STAC_M2_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM2_2_pin_configs, - }, - [STAC_M3] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM3_pin_configs, - }, - [STAC_M5] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM5_pin_configs, - }, - [STAC_M6] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac925xM6_pin_configs, - }, +static const unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = { + [STAC_REF] = ref925x_pin_configs, + [STAC_M1] = stac925xM1_pin_configs, + [STAC_M1_2] = stac925xM1_2_pin_configs, + [STAC_M2] = stac925xM2_pin_configs, + [STAC_M2_2] = stac925xM2_2_pin_configs, + [STAC_M3] = stac925xM3_pin_configs, + [STAC_M5] = stac925xM5_pin_configs, + [STAC_M6] = stac925xM6_pin_configs, }; -static const struct hda_model_fixup stac925x_models[] = { - { .id = STAC_REF, .name = "ref" }, - { .id = STAC_M1, .name = "m1" }, - { .id = STAC_M1_2, .name = "m1-2" }, - { .id = STAC_M2, .name = "m2" }, - { .id = STAC_M2_2, .name = "m2-2" }, - { .id = STAC_M3, .name = "m3" }, - { .id = STAC_M5, .name = "m5" }, - { .id = STAC_M6, .name = "m6" }, - {} +static const char * const stac925x_models[STAC_925x_MODELS] = { + [STAC_925x_AUTO] = "auto", + [STAC_REF] = "ref", + [STAC_M1] = "m1", + [STAC_M1_2] = "m1-2", + [STAC_M2] = "m2", + [STAC_M2_2] = "m2-2", + [STAC_M3] = "m3", + [STAC_M5] = "m5", + [STAC_M6] = "m6", }; -static const struct snd_pci_quirk stac925x_fixup_tbl[] = { - /* SigmaTel reference board */ - SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), - SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), - - /* Default table for unknown ID */ - SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), - - /* gateway machines are checked via codec ssid */ +static const struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = { SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2), SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5), SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1), @@ -1783,217 +1518,67 @@ static const struct snd_pci_quirk stac925x_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref92hd73xx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02a19040 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x02214030 }, - { 0x0e, 0x0181302e }, - { 0x0f, 0x01014010 }, - { 0x10, 0x01014020 }, - { 0x11, 0x01014030 }, - { 0x12, 0x02319040 }, - { 0x13, 0x90a000f0 }, - { 0x14, 0x90a000f0 }, - { 0x22, 0x01452050 }, - { 0x23, 0x01452050 }, - {} -}; +static const struct snd_pci_quirk stac925x_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF), + SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF), -static const struct hda_pintbl dell_m6_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x4f00000f }, - { 0x0c, 0x4f0000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x03a11020 }, - { 0x0f, 0x0321101f }, - { 0x10, 0x4f0000f0 }, - { 0x11, 0x4f0000f0 }, - { 0x12, 0x4f0000f0 }, - { 0x13, 0x90a60160 }, - { 0x14, 0x4f0000f0 }, - { 0x22, 0x4f0000f0 }, - { 0x23, 0x4f0000f0 }, - {} -}; + /* Default table for unknown ID */ + SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2), -static const struct hda_pintbl alienware_m17x_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x0321101f }, - { 0x0c, 0x03a11020 }, - { 0x0d, 0x03014020 }, - { 0x0e, 0x90170110 }, - { 0x0f, 0x4f0000f0 }, - { 0x10, 0x4f0000f0 }, - { 0x11, 0x4f0000f0 }, - { 0x12, 0x4f0000f0 }, - { 0x13, 0x90a60160 }, - { 0x14, 0x4f0000f0 }, - { 0x22, 0x4f0000f0 }, - { 0x23, 0x904601b0 }, - {} + {} /* terminator */ }; -static const struct hda_pintbl intel_dg45id_pin_configs[] = { - { 0x0a, 0x02214230 }, - { 0x0b, 0x02A19240 }, - { 0x0c, 0x01013214 }, - { 0x0d, 0x01014210 }, - { 0x0e, 0x01A19250 }, - { 0x0f, 0x01011212 }, - { 0x10, 0x01016211 }, - {} +static const unsigned int ref92hd73xx_pin_configs[13] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, + 0x0181302e, 0x01014010, 0x01014020, 0x01014030, + 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, + 0x01452050, }; -static void stac92hd73xx_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs); - spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; -} - -static void stac92hd73xx_fixup_dell(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - snd_hda_apply_pincfgs(codec, dell_m6_pin_configs); - spec->num_smuxes = 0; - spec->eapd_switch = 0; -} - -static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_add_verbs(codec, dell_eq_core_init); - spec->volknob_init = 1; -} - -/* Analog Mics */ -static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - spec->num_dmics = 0; -} - -/* Digital Mics */ -static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; -} - -/* Both */ -static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - stac92hd73xx_fixup_dell(codec); - snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); - snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); - spec->num_dmics = 1; -} - -static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs); - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); - spec->eapd_switch = 0; -} +static const unsigned int dell_m6_pin_configs[13] = { + 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110, + 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, +}; -static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; +static const unsigned int alienware_m17x_pin_configs[13] = { + 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020, + 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0, + 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0, + 0x904601b0, +}; - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->hp_detect = 0; -} +static const unsigned int intel_dg45id_pin_configs[13] = { + 0x02214230, 0x02A19240, 0x01013214, 0x01014210, + 0x01A19250, 0x01011212, 0x01016211 +}; -static const struct hda_fixup stac92hd73xx_fixups[] = { - [STAC_92HD73XX_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_ref, - }, - [STAC_DELL_M6_AMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_amic, - }, - [STAC_DELL_M6_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_dmic, - }, - [STAC_DELL_M6_BOTH] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_m6_both, - }, - [STAC_DELL_EQ] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_dell_eq, - }, - [STAC_ALIENWARE_M17X] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_alienware_m17x, - }, - [STAC_92HD73XX_INTEL] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_dg45id_pin_configs, - }, - [STAC_92HD73XX_NO_JD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd73xx_fixup_no_jd, - } +static const unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, + [STAC_DELL_M6_AMIC] = dell_m6_pin_configs, + [STAC_DELL_M6_DMIC] = dell_m6_pin_configs, + [STAC_DELL_M6_BOTH] = dell_m6_pin_configs, + [STAC_DELL_EQ] = dell_m6_pin_configs, + [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs, + [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs, }; -static const struct hda_model_fixup stac92hd73xx_models[] = { - { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" }, - { .id = STAC_92HD73XX_REF, .name = "ref" }, - { .id = STAC_92HD73XX_INTEL, .name = "intel" }, - { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" }, - { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" }, - { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" }, - { .id = STAC_DELL_EQ, .name = "dell-eq" }, - { .id = STAC_ALIENWARE_M17X, .name = "alienware" }, - {} +static const char * const stac92hd73xx_models[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_AUTO] = "auto", + [STAC_92HD73XX_NO_JD] = "no-jd", + [STAC_92HD73XX_REF] = "ref", + [STAC_92HD73XX_INTEL] = "intel", + [STAC_DELL_M6_AMIC] = "dell-m6-amic", + [STAC_DELL_M6_DMIC] = "dell-m6-dmic", + [STAC_DELL_M6_BOTH] = "dell-m6", + [STAC_DELL_EQ] = "dell-eq", + [STAC_ALIENWARE_M17X] = "alienware", }; -static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { +static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD73XX_REF), @@ -2031,7 +1616,10 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { "Dell Studio XPS 1645", STAC_DELL_M6_DMIC), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, "Dell Studio 1558", STAC_DELL_M6_DMIC), - /* codec SSID matching */ + {} /* terminator */ +}; + +static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1, "Alienware M17x", STAC_ALIENWARE_M17X), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a, @@ -2041,232 +1629,68 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref92hd83xxx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02211010 }, - { 0x0c, 0x02a19020 }, - { 0x0d, 0x02170130 }, - { 0x0e, 0x01014050 }, - { 0x0f, 0x01819040 }, - { 0x10, 0x01014020 }, - { 0x11, 0x90a3014e }, - { 0x1f, 0x01451160 }, - { 0x20, 0x98560170 }, - {} +static const unsigned int ref92hd83xxx_pin_configs[10] = { + 0x02214030, 0x02211010, 0x02a19020, 0x02170130, + 0x01014050, 0x01819040, 0x01014020, 0x90a3014e, + 0x01451160, 0x98560170, }; -static const struct hda_pintbl dell_s14_pin_configs[] = { - { 0x0a, 0x0221403f }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x02a19020 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x10, 0x40f000f0 }, - { 0x11, 0x90a60160 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} +static const unsigned int dell_s14_pin_configs[10] = { + 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160, + 0x40f000f0, 0x40f000f0, }; -static const struct hda_pintbl dell_vostro_3500_pin_configs[] = { - { 0x0a, 0x02a11020 }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x400000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x400000f1 }, - { 0x0f, 0x400000f2 }, - { 0x10, 0x400000f3 }, - { 0x11, 0x90a60160 }, - { 0x1f, 0x400000f4 }, - { 0x20, 0x400000f5 }, - {} +static const unsigned int dell_vostro_3500_pin_configs[10] = { + 0x02a11020, 0x0221101f, 0x400000f0, 0x90170110, + 0x400000f1, 0x400000f2, 0x400000f3, 0x90a60160, + 0x400000f4, 0x400000f5, }; -static const struct hda_pintbl hp_dv7_4000_pin_configs[] = { - { 0x0a, 0x03a12050 }, - { 0x0b, 0x0321201f }, - { 0x0c, 0x40f000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x10, 0x90170110 }, - { 0x11, 0xd5a30140 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} +static const unsigned int hp_dv7_4000_pin_configs[10] = { + 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140, + 0x40f000f0, 0x40f000f0, }; -static const struct hda_pintbl hp_zephyr_pin_configs[] = { - { 0x0a, 0x01813050 }, - { 0x0b, 0x0421201f }, - { 0x0c, 0x04a1205e }, - { 0x0d, 0x96130310 }, - { 0x0e, 0x96130310 }, - { 0x0f, 0x0101401f }, - { 0x10, 0x1111611f }, - { 0x11, 0xd5a30130 }, - {} +static const unsigned int hp_zephyr_pin_configs[10] = { + 0x01813050, 0x0421201f, 0x04a1205e, 0x96130310, + 0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130, + 0, 0, }; -static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = { - { 0x0a, 0x40f000f0 }, - { 0x0b, 0x0221101f }, - { 0x0c, 0x02a11020 }, - { 0x0d, 0x92170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x92170110 }, - { 0x10, 0x40f000f0 }, - { 0x11, 0xd5a30130 }, - { 0x1f, 0x40f000f0 }, - { 0x20, 0x40f000f0 }, - {} +static const unsigned int hp_cNB11_intquad_pin_configs[10] = { + 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110, + 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130, + 0x40f000f0, 0x40f000f0, }; -static void stac92hd83xxx_fixup_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (hp_bnb2011_with_dock(codec)) { - snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); - snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); - } - - if (find_mute_led_cfg(codec, spec->default_polarity)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); -} - -static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs); - snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init); -} - -static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->default_polarity = 0; -} - -static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->default_polarity = 1; -} - -static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ -} - -static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->headset_jack = 1; -} - -static const struct hda_fixup stac92hd83xxx_fixups[] = { - [STAC_92HD83XXX_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref92hd83xxx_pin_configs, - }, - [STAC_92HD83XXX_PWR_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref92hd83xxx_pin_configs, - }, - [STAC_DELL_S14] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_s14_pin_configs, - }, - [STAC_DELL_VOSTRO_3500] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_vostro_3500_pin_configs, - }, - [STAC_92HD83XXX_HP_cNB11_INTQUAD] = { - .type = HDA_FIXUP_PINS, - .v.pins = hp_cNB11_intquad_pin_configs, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp, - }, - [STAC_HP_DV7_4000] = { - .type = HDA_FIXUP_PINS, - .v.pins = hp_dv7_4000_pin_configs, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_HP_ZEPHYR] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_zephyr, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_INV_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_inv_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HP_MIC_LED] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_hp_mic_led, - .chained = true, - .chain_id = STAC_92HD83XXX_HP, - }, - [STAC_92HD83XXX_HEADSET_JACK] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd83xxx_fixup_headset_jack, - }, +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_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs, + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs, + [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, + [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs, }; -static const struct hda_model_fixup stac92hd83xxx_models[] = { - { .id = STAC_92HD83XXX_REF, .name = "ref" }, - { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" }, - { .id = STAC_DELL_S14, .name = "dell-s14" }, - { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" }, - { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" }, - { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" }, - { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" }, - { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" }, - { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" }, - { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" }, - { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" }, - {} +static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { + [STAC_92HD83XXX_AUTO] = "auto", + [STAC_92HD83XXX_REF] = "ref", + [STAC_92HD83XXX_PWR_REF] = "mic-ref", + [STAC_DELL_S14] = "dell-s14", + [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500", + [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad", + [STAC_HP_DV7_4000] = "hp-dv7-4000", + [STAC_HP_ZEPHYR] = "hp-zephyr", + [STAC_92HD83XXX_HP_LED] = "hp-led", + [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led", + [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led", + [STAC_92HD83XXX_HEADSET_JACK] = "headset-jack", }; -static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { +static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD83XXX_REF), @@ -2342,311 +1766,69 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP Mini", STAC_92HD83XXX_HP_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E, "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED), - SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a, - "HP Mini", STAC_92HD83XXX_HP_LED), - SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP), {} /* terminator */ }; -static const struct hda_pintbl ref92hd71bxx_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x02a19040 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x0181302e }, - { 0x0f, 0x01014010 }, - { 0x14, 0x01019020 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x90a000f0 }, - { 0x1e, 0x01452050 }, - { 0x1f, 0x01452050 }, - {} +static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = { + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561, + "HP", STAC_HP_ZEPHYR), + {} /* terminator */ }; -static const struct hda_pintbl dell_m4_1_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x40f000f0 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x23a1902e }, - { 0x0f, 0x23014250 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x4f0000f0 }, - { 0x1f, 0x4f0000f0 }, - {} +static const unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, + 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, + 0x90a000f0, 0x01452050, 0x01452050, 0x00000000, + 0x00000000 }; -static const struct hda_pintbl dell_m4_2_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x23a1902e }, - { 0x0f, 0x23014250 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x40f000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x044413b0 }, - { 0x1f, 0x044413b0 }, - {} +static const unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, + 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000, + 0x00000000 }; -static const struct hda_pintbl dell_m4_3_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11221 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170110 }, - { 0x0e, 0x40f000f0 }, - { 0x0f, 0x40f000f0 }, - { 0x14, 0x40f000f0 }, - { 0x18, 0x90a000f0 }, - { 0x19, 0x40f000f0 }, - { 0x1e, 0x044413b0 }, - { 0x1f, 0x044413b0 }, - {} +static const unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, + 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, + 0x00000000 }; -static void stac92hd71bxx_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs); - spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0; -} - -static void stac92hd71bxx_fixup_no_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->num_dmics = 0; - spec->num_smuxes = 0; - spec->num_dmuxes = 0; -} - -static void stac92hd71bxx_fixup_no_smux(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; +static const unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = { + 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110, + 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0, + 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000, + 0x00000000 +}; - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->num_dmics = 1; - spec->num_smuxes = 0; - spec->num_dmuxes = 1; -} +static const unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, + [STAC_DELL_M4_1] = dell_m4_1_pin_configs, + [STAC_DELL_M4_2] = dell_m4_2_pin_configs, + [STAC_DELL_M4_3] = dell_m4_3_pin_configs, + [STAC_HP_M4] = NULL, + [STAC_HP_DV4] = NULL, + [STAC_HP_DV5] = NULL, + [STAC_HP_HDX] = NULL, + [STAC_HP_DV4_1222NR] = NULL, +}; -static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; +static const char * const stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_AUTO] = "auto", + [STAC_92HD71BXX_REF] = "ref", + [STAC_DELL_M4_1] = "dell-m4-1", + [STAC_DELL_M4_2] = "dell-m4-2", + [STAC_DELL_M4_3] = "dell-m4-3", + [STAC_HP_M4] = "hp-m4", + [STAC_HP_DV4] = "hp-dv4", + [STAC_HP_DV5] = "hp-dv5", + [STAC_HP_HDX] = "hp-hdx", + [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr", +}; - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - /* Enable VREF power saving on GPIO1 detect */ - stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x02); - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - snd_hda_jack_detect_enable(codec, codec->afg, 0); - spec->gpio_mask |= 0x02; - - /* enable internal microphone */ - snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); - stac92xx_auto_set_pinctl(codec, 0x0e, - AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); - - stac92hd71bxx_fixup_no_dmic(codec, fix, action); -} - -static void stac92hd71bxx_fixup_hp_dv4_1222nr(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->num_dmics = 1; - /* I don't know if it needs 1 or 2 smuxes - will wait for - * bug reports to fix if needed - */ - spec->num_smuxes = 1; - spec->num_dmuxes = 1; -} - -static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->gpio_led = 0x01; -} - -static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - unsigned int cap; - - switch (action) { - case HDA_FIXUP_ACT_PRE_PROBE: - snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); - stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); - /* HP dv6 gives the headphone pin as a line-out. Thus we - * need to set hp_detect flag here to force to enable HP - * detection. - */ - spec->hp_detect = 1; - break; - - case HDA_FIXUP_ACT_PROBE: - /* enable bass on HP dv7 */ - cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); - cap &= AC_GPIO_IO_COUNT; - if (cap >= 6) - stac_add_hp_bass_switch(codec); - break; - } -} - -static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - spec->gpio_led = 0x08; - spec->num_dmics = 1; - spec->num_smuxes = 1; - spec->num_dmuxes = 1; -} - - -static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (hp_blike_system(codec->subsystem_id)) { - unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); - if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || - get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || - get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) { - /* It was changed in the BIOS to just satisfy MS DTM. - * Lets turn it back into slaved HP - */ - pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) - | (AC_JACK_HP_OUT << - AC_DEFCFG_DEVICE_SHIFT); - pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC - | AC_DEFCFG_SEQUENCE))) - | 0x1f; - snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg); - } - } - - if (find_mute_led_cfg(codec, 1)) - snd_printd("mute LED gpio %d polarity %d\n", - spec->gpio_led, - spec->gpio_led_polarity); - -} - -static const struct hda_fixup stac92hd71bxx_fixups[] = { - [STAC_92HD71BXX_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_ref, - }, - [STAC_DELL_M4_1] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_1_pin_configs, - .chained = true, - .chain_id = STAC_92HD71BXX_NO_SMUX, - }, - [STAC_DELL_M4_2] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_2_pin_configs, - .chained = true, - .chain_id = STAC_92HD71BXX_NO_DMIC, - }, - [STAC_DELL_M4_3] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_m4_3_pin_configs, - .chained = true, - .chain_id = STAC_92HD71BXX_NO_SMUX, - }, - [STAC_HP_M4] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_m4, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_HP_DV4] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv4, - .chained = true, - .chain_id = STAC_HP_DV5, - }, - [STAC_HP_DV5] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv5, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_HP_HDX] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_hdx, - .chained = true, - .chain_id = STAC_92HD71BXX_HP, - }, - [STAC_HP_DV4_1222NR] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp_dv4_1222nr, - .chained = true, - .chain_id = STAC_HP_DV4, - }, - [STAC_92HD71BXX_NO_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_no_dmic, - }, - [STAC_92HD71BXX_NO_SMUX] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_no_smux, - }, - [STAC_92HD71BXX_HP] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac92hd71bxx_fixup_hp, - }, -}; - -static const struct hda_model_fixup stac92hd71bxx_models[] = { - { .id = STAC_92HD71BXX_REF, .name = "ref" }, - { .id = STAC_DELL_M4_1, .name = "dell-m4-1" }, - { .id = STAC_DELL_M4_2, .name = "dell-m4-2" }, - { .id = STAC_DELL_M4_3, .name = "dell-m4-3" }, - { .id = STAC_HP_M4, .name = "hp-m4" }, - { .id = STAC_HP_DV4, .name = "hp-dv4" }, - { .id = STAC_HP_DV5, .name = "hp-dv5" }, - { .id = STAC_HP_HDX, .name = "hp-hdx" }, - { .id = STAC_HP_DV4_1222NR, .name = "hp-dv4-1222nr" }, - {} -}; - -static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { +static const struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_92HD71BXX_REF), @@ -2676,7 +1858,6 @@ static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { "HP DV6", STAC_HP_DV5), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010, "HP", STAC_HP_DV5), - SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233, "unknown Dell", STAC_DELL_M4_1), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, @@ -2704,18 +1885,10 @@ static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref922x_pin_configs[] = { - { 0x0a, 0x01014010 }, - { 0x0b, 0x01016011 }, - { 0x0c, 0x01012012 }, - { 0x0d, 0x0221401f }, - { 0x0e, 0x01813122 }, - { 0x0f, 0x01011014 }, - { 0x10, 0x01441030 }, - { 0x11, 0x01c41030 }, - { 0x15, 0x40000100 }, - { 0x1b, 0x40000100 }, - {} +static const unsigned int ref922x_pin_configs[10] = { + 0x01014010, 0x01016011, 0x01012012, 0x0221401f, + 0x01813122, 0x01011014, 0x01441030, 0x01c41030, + 0x40000100, 0x40000100, }; /* @@ -2726,18 +1899,10 @@ static const struct hda_pintbl ref922x_pin_configs[] = { 102801D1 102801D2 */ -static const struct hda_pintbl dell_922x_d81_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x01a19021 }, - { 0x0c, 0x01111012 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x02a19020 }, - { 0x0f, 0x01117011 }, - { 0x10, 0x400001f0 }, - { 0x11, 0x400001f1 }, - { 0x15, 0x01813122 }, - { 0x1b, 0x400001f2 }, - {} +static const unsigned int dell_922x_d81_pin_configs[10] = { + 0x02214030, 0x01a19021, 0x01111012, 0x01114010, + 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1, + 0x01813122, 0x400001f2, }; /* @@ -2745,311 +1910,130 @@ static const struct hda_pintbl dell_922x_d81_pin_configs[] = { 102801AC 102801D0 */ -static const struct hda_pintbl dell_922x_d82_pin_configs[] = { - { 0x0a, 0x02214030 }, - { 0x0b, 0x01a19021 }, - { 0x0c, 0x01111012 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x02a19020 }, - { 0x0f, 0x01117011 }, - { 0x10, 0x01451140 }, - { 0x11, 0x400001f0 }, - { 0x15, 0x01813122 }, - { 0x1b, 0x400001f1 }, - {} +static const unsigned int dell_922x_d82_pin_configs[10] = { + 0x02214030, 0x01a19021, 0x01111012, 0x01114010, + 0x02a19020, 0x01117011, 0x01451140, 0x400001f0, + 0x01813122, 0x400001f1, }; /* STAC 922X pin configs for 102801BF */ -static const struct hda_pintbl dell_922x_m81_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x01112024 }, - { 0x0c, 0x01111222 }, - { 0x0d, 0x91174220 }, - { 0x0e, 0x03a11050 }, - { 0x0f, 0x01116221 }, - { 0x10, 0x90a70330 }, - { 0x11, 0x01452340 }, - { 0x15, 0x40C003f1 }, - { 0x1b, 0x405003f0 }, - {} +static const unsigned int dell_922x_m81_pin_configs[10] = { + 0x0321101f, 0x01112024, 0x01111222, 0x91174220, + 0x03a11050, 0x01116221, 0x90a70330, 0x01452340, + 0x40C003f1, 0x405003f0, }; /* STAC 9221 A1 pin configs for 102801D7 (Dell XPS M1210) */ -static const struct hda_pintbl dell_922x_m82_pin_configs[] = { - { 0x0a, 0x02211211 }, - { 0x0b, 0x408103ff }, - { 0x0c, 0x02a1123e }, - { 0x0d, 0x90100310 }, - { 0x0e, 0x408003f1 }, - { 0x0f, 0x0221121f }, - { 0x10, 0x03451340 }, - { 0x11, 0x40c003f2 }, - { 0x15, 0x508003f3 }, - { 0x1b, 0x405003f4 }, - {} -}; - -static const struct hda_pintbl d945gtp3_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x01a19022 }, - { 0x0c, 0x01813021 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x40000100 }, - { 0x0f, 0x40000100 }, - { 0x10, 0x40000100 }, - { 0x11, 0x40000100 }, - { 0x15, 0x02a19120 }, - { 0x1b, 0x40000100 }, - {} +static const unsigned int dell_922x_m82_pin_configs[10] = { + 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, + 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, + 0x508003f3, 0x405003f4, }; -static const struct hda_pintbl d945gtp5_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x01011012 }, - { 0x0c, 0x01813024 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19021 }, - { 0x0f, 0x01016011 }, - { 0x10, 0x01452130 }, - { 0x11, 0x40000100 }, - { 0x15, 0x02a19320 }, - { 0x1b, 0x40000100 }, - {} -}; - -static const struct hda_pintbl intel_mac_v1_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x400000ff }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e030 }, - { 0x11, 0x11c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int d945gtp3_pin_configs[10] = { + 0x0221401f, 0x01a19022, 0x01813021, 0x01014010, + 0x40000100, 0x40000100, 0x40000100, 0x40000100, + 0x02a19120, 0x40000100, }; -static const struct hda_pintbl intel_mac_v2_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x90a7012e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e230 }, - { 0x11, 0x500000fa }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int d945gtp5_pin_configs[10] = { + 0x0221401f, 0x01011012, 0x01813024, 0x01014010, + 0x01a19021, 0x01016011, 0x01452130, 0x40000100, + 0x02a19320, 0x40000100, }; -static const struct hda_pintbl intel_mac_v3_pin_configs[] = { - { 0x0a, 0x0121e21f }, - { 0x0b, 0x90a7012e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x400000fd }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0181e020 }, - { 0x10, 0x1145e230 }, - { 0x11, 0x11c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int intel_mac_v1_pin_configs[10] = { + 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240, + 0x400000fc, 0x400000fb, }; -static const struct hda_pintbl intel_mac_v4_pin_configs[] = { - { 0x0a, 0x0321e21f }, - { 0x0b, 0x03a1e02e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x9017e11f }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0381e020 }, - { 0x10, 0x1345e230 }, - { 0x11, 0x13c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int intel_mac_v2_pin_configs[10] = { + 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa, + 0x400000fc, 0x400000fb, }; -static const struct hda_pintbl intel_mac_v5_pin_configs[] = { - { 0x0a, 0x0321e21f }, - { 0x0b, 0x03a1e02e }, - { 0x0c, 0x9017e110 }, - { 0x0d, 0x9017e11f }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x0381e020 }, - { 0x10, 0x1345e230 }, - { 0x11, 0x13c5e240 }, - { 0x15, 0x400000fc }, - { 0x1b, 0x400000fb }, - {} +static const unsigned int intel_mac_v3_pin_configs[10] = { + 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240, + 0x400000fc, 0x400000fb, }; -static const struct hda_pintbl ecs202_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x02a19020 }, - { 0x0c, 0x01a19020 }, - { 0x0d, 0x01114010 }, - { 0x0e, 0x408000f0 }, - { 0x0f, 0x01813022 }, - { 0x10, 0x074510a0 }, - { 0x11, 0x40c400f1 }, - { 0x15, 0x9037012e }, - { 0x1b, 0x40e000f2 }, - {} +static const unsigned int intel_mac_v4_pin_configs[10] = { + 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, + 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, + 0x400000fc, 0x400000fb, }; -/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */ -static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = { - SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1), - SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2), - SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2), - SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3), - SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4), - SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5), - SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5), - {} +static const unsigned int intel_mac_v5_pin_configs[10] = { + 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, + 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, + 0x400000fc, 0x400000fb, }; -static const struct hda_fixup stac922x_fixups[]; - -/* remap the fixup from codec SSID and apply it */ -static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl, - stac922x_fixups); - if (codec->fixup_id != STAC_INTEL_MAC_AUTO) - snd_hda_apply_fixup(codec, action); -} - -static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec, - const struct hda_fixup *fix, - int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gpio_mask = spec->gpio_dir = 0x03; - spec->gpio_data = 0x03; - } -} - -static const struct hda_fixup stac922x_fixups[] = { - [STAC_D945_REF] = { - .type = HDA_FIXUP_PINS, - .v.pins = ref922x_pin_configs, - }, - [STAC_D945GTP3] = { - .type = HDA_FIXUP_PINS, - .v.pins = d945gtp3_pin_configs, - }, - [STAC_D945GTP5] = { - .type = HDA_FIXUP_PINS, - .v.pins = d945gtp5_pin_configs, - }, - [STAC_INTEL_MAC_AUTO] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac922x_fixup_intel_mac_auto, - }, - [STAC_INTEL_MAC_V1] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v1_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V2] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v2_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V3] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v3_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V4] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v4_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_INTEL_MAC_V5] = { - .type = HDA_FIXUP_PINS, - .v.pins = intel_mac_v5_pin_configs, - .chained = true, - .chain_id = STAC_922X_INTEL_MAC_GPIO, - }, - [STAC_922X_INTEL_MAC_GPIO] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac922x_fixup_intel_mac_gpio, - }, - [STAC_ECS_202] = { - .type = HDA_FIXUP_PINS, - .v.pins = ecs202_pin_configs, - }, - [STAC_922X_DELL_D81] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_d81_pin_configs, - }, - [STAC_922X_DELL_D82] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_d82_pin_configs, - }, - [STAC_922X_DELL_M81] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_m81_pin_configs, - }, - [STAC_922X_DELL_M82] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_922x_m82_pin_configs, - }, +static const unsigned int ecs202_pin_configs[10] = { + 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010, + 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1, + 0x9037012e, 0x40e000f2, }; -static const struct hda_model_fixup stac922x_models[] = { - { .id = STAC_D945_REF, .name = "ref" }, - { .id = STAC_D945GTP5, .name = "5stack" }, - { .id = STAC_D945GTP3, .name = "3stack" }, - { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" }, - { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" }, - { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" }, - { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" }, - { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" }, - { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" }, - { .id = STAC_ECS_202, .name = "ecs202" }, - { .id = STAC_922X_DELL_D81, .name = "dell-d81" }, - { .id = STAC_922X_DELL_D82, .name = "dell-d82" }, - { .id = STAC_922X_DELL_M81, .name = "dell-m81" }, - { .id = STAC_922X_DELL_M82, .name = "dell-m82" }, +static const unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { + [STAC_D945_REF] = ref922x_pin_configs, + [STAC_D945GTP3] = d945gtp3_pin_configs, + [STAC_D945GTP5] = d945gtp5_pin_configs, + [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs, + [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs, + [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs, + [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs, + [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs, + [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs, /* for backward compatibility */ - { .id = STAC_INTEL_MAC_V3, .name = "macmini" }, - { .id = STAC_INTEL_MAC_V5, .name = "macbook" }, - { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" }, - { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" }, - { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" }, - { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" }, - {} -}; - -static const struct snd_pci_quirk stac922x_fixup_tbl[] = { + [STAC_MACMINI] = intel_mac_v3_pin_configs, + [STAC_MACBOOK] = intel_mac_v5_pin_configs, + [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs, + [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs, + [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs, + [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs, + [STAC_ECS_202] = ecs202_pin_configs, + [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs, + [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs, + [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs, + [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs, +}; + +static const char * const stac922x_models[STAC_922X_MODELS] = { + [STAC_922X_AUTO] = "auto", + [STAC_D945_REF] = "ref", + [STAC_D945GTP5] = "5stack", + [STAC_D945GTP3] = "3stack", + [STAC_INTEL_MAC_V1] = "intel-mac-v1", + [STAC_INTEL_MAC_V2] = "intel-mac-v2", + [STAC_INTEL_MAC_V3] = "intel-mac-v3", + [STAC_INTEL_MAC_V4] = "intel-mac-v4", + [STAC_INTEL_MAC_V5] = "intel-mac-v5", + [STAC_INTEL_MAC_AUTO] = "intel-mac-auto", + /* for backward compatibility */ + [STAC_MACMINI] = "macmini", + [STAC_MACBOOK] = "macbook", + [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", + [STAC_MACBOOK_PRO_V2] = "macbook-pro", + [STAC_IMAC_INTEL] = "imac-intel", + [STAC_IMAC_INTEL_20] = "imac-intel-20", + [STAC_ECS_202] = "ecs202", + [STAC_922X_DELL_D81] = "dell-d81", + [STAC_922X_DELL_D82] = "dell-d82", + [STAC_922X_DELL_M81] = "dell-m81", + [STAC_922X_DELL_M82] = "dell-m82", +}; + +static const struct snd_pci_quirk stac922x_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D945_REF), @@ -3112,10 +2096,9 @@ static const struct snd_pci_quirk stac922x_fixup_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204, "Intel D945", STAC_D945_REF), /* other systems */ - /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */ - SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO), - + SND_PCI_QUIRK(0x8384, 0x7680, + "Mac", STAC_INTEL_MAC_AUTO), /* Dell systems */ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7, "unknown Dell", STAC_922X_DELL_D81), @@ -3141,235 +2124,65 @@ static const struct snd_pci_quirk stac922x_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref927x_pin_configs[] = { - { 0x0a, 0x02214020 }, - { 0x0b, 0x02a19080 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x0101201f }, - { 0x12, 0x183301f0 }, - { 0x13, 0x18a001f0 }, - { 0x14, 0x18a001f0 }, - { 0x21, 0x01442070 }, - { 0x22, 0x01c42190 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int ref927x_pin_configs[14] = { + 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x0101201f, + 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070, + 0x01c42190, 0x40000100, }; -static const struct hda_pintbl d965_3st_pin_configs[] = { - { 0x0a, 0x0221401f }, - { 0x0b, 0x02a19120 }, - { 0x0c, 0x40000100 }, - { 0x0d, 0x01014011 }, - { 0x0e, 0x01a19021 }, - { 0x0f, 0x01813024 }, - { 0x10, 0x40000100 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x40000100 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int d965_3st_pin_configs[14] = { + 0x0221401f, 0x02a19120, 0x40000100, 0x01014011, + 0x01a19021, 0x01813024, 0x40000100, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x40000100, + 0x40000100, 0x40000100 }; -static const struct hda_pintbl d965_5st_pin_configs[] = { - { 0x0a, 0x02214020 }, - { 0x0b, 0x02a19080 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x01442070 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int d965_5st_pin_configs[14] = { + 0x02214020, 0x02a19080, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x01442070, + 0x40000100, 0x40000100 }; -static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = { - { 0x0a, 0x40000100 }, - { 0x0b, 0x40000100 }, - { 0x0c, 0x0181304e }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01a19040 }, - { 0x0f, 0x01011012 }, - { 0x10, 0x01016011 }, - { 0x11, 0x40000100 }, - { 0x12, 0x40000100 }, - { 0x13, 0x40000100 }, - { 0x14, 0x40000100 }, - { 0x21, 0x01442070 }, - { 0x22, 0x40000100 }, - { 0x23, 0x40000100 }, - {} +static const unsigned int d965_5st_no_fp_pin_configs[14] = { + 0x40000100, 0x40000100, 0x0181304e, 0x01014010, + 0x01a19040, 0x01011012, 0x01016011, 0x40000100, + 0x40000100, 0x40000100, 0x40000100, 0x01442070, + 0x40000100, 0x40000100 }; -static const struct hda_pintbl dell_3st_pin_configs[] = { - { 0x0a, 0x02211230 }, - { 0x0b, 0x02a11220 }, - { 0x0c, 0x01a19040 }, - { 0x0d, 0x01114210 }, - { 0x0e, 0x01111212 }, - { 0x0f, 0x01116211 }, - { 0x10, 0x01813050 }, - { 0x11, 0x01112214 }, - { 0x12, 0x403003fa }, - { 0x13, 0x90a60040 }, - { 0x14, 0x90a60040 }, - { 0x21, 0x404003fb }, - { 0x22, 0x40c003fc }, - { 0x23, 0x40000100 }, - {} +static const unsigned int dell_3st_pin_configs[14] = { + 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, + 0x01111212, 0x01116211, 0x01813050, 0x01112214, + 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, + 0x40c003fc, 0x40000100 }; -static void stac927x_fixup_ref_no_jd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - /* no jack detecion for ref-no-jd model */ - if (action == HDA_FIXUP_ACT_PROBE) - spec->hp_detect = 0; -} - -static void stac927x_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, ref927x_pin_configs); - spec->eapd_mask = spec->gpio_mask = 0; - spec->gpio_dir = spec->gpio_data = 0; - } -} - -static void stac927x_fixup_dell_dmic(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action != HDA_FIXUP_ACT_PRE_PROBE) - return; - - if (codec->subsystem_id != 0x1028022f) { - /* GPIO2 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x04; - spec->gpio_dir = spec->gpio_data = 0x04; - } - spec->dmic_nids = stac927x_dmic_nids; - spec->num_dmics = STAC927X_NUM_DMICS; - - snd_hda_add_verbs(codec, dell_3st_core_init); - spec->volknob_init = 1; - spec->dmux_nids = stac927x_dmux_nids; - spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); -} - -static void stac927x_fixup_volknob(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_add_verbs(codec, stac927x_volknob_core_init); - spec->volknob_init = 1; - } -} - -static const struct hda_fixup stac927x_fixups[] = { - [STAC_D965_REF_NO_JD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_ref_no_jd, - .chained = true, - .chain_id = STAC_D965_REF, - }, - [STAC_D965_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_ref, - }, - [STAC_D965_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_3st_pin_configs, - .chained = true, - .chain_id = STAC_D965_VERBS, - }, - [STAC_D965_5ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_5st_pin_configs, - .chained = true, - .chain_id = STAC_D965_VERBS, - }, - [STAC_D965_VERBS] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = d965_core_init, - }, - [STAC_D965_5ST_NO_FP] = { - .type = HDA_FIXUP_PINS, - .v.pins = d965_5st_no_fp_pin_configs, - }, - [STAC_DELL_3ST] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_3st_pin_configs, - .chained = true, - .chain_id = STAC_927X_DELL_DMIC, - }, - [STAC_DELL_BIOS] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* configure the analog microphone on some laptops */ - { 0x0c, 0x90a79130 }, - /* correct the front output jack as a hp out */ - { 0x0f, 0x0227011f }, - /* correct the front input jack as a mic */ - { 0x0e, 0x02a79130 }, - {} - }, - .chained = true, - .chain_id = STAC_927X_DELL_DMIC, - }, - [STAC_DELL_BIOS_SPDIF] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - /* correct the device field to SPDIF out */ - { 0x21, 0x01442070 }, - {} - }, - .chained = true, - .chain_id = STAC_DELL_BIOS, - }, - [STAC_927X_DELL_DMIC] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_dell_dmic, - }, - [STAC_927X_VOLKNOB] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac927x_fixup_volknob, - }, +static const unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { + [STAC_D965_REF_NO_JD] = ref927x_pin_configs, + [STAC_D965_REF] = ref927x_pin_configs, + [STAC_D965_3ST] = d965_3st_pin_configs, + [STAC_D965_5ST] = d965_5st_pin_configs, + [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs, + [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_DELL_BIOS] = NULL, + [STAC_927X_VOLKNOB] = NULL, }; -static const struct hda_model_fixup stac927x_models[] = { - { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" }, - { .id = STAC_D965_REF, .name = "ref" }, - { .id = STAC_D965_3ST, .name = "3stack" }, - { .id = STAC_D965_5ST, .name = "5stack" }, - { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, - { .id = STAC_DELL_3ST, .name = "dell-3stack" }, - { .id = STAC_DELL_BIOS, .name = "dell-bios" }, - { .id = STAC_927X_VOLKNOB, .name = "volknob" }, - {} +static const char * const stac927x_models[STAC_927X_MODELS] = { + [STAC_927X_AUTO] = "auto", + [STAC_D965_REF_NO_JD] = "ref-no-jd", + [STAC_D965_REF] = "ref", + [STAC_D965_3ST] = "3stack", + [STAC_D965_5ST] = "5stack", + [STAC_D965_5ST_NO_FP] = "5stack-no-fp", + [STAC_DELL_3ST] = "dell-3stack", + [STAC_DELL_BIOS] = "dell-bios", + [STAC_927X_VOLKNOB] = "volknob", }; -static const struct snd_pci_quirk stac927x_fixup_tbl[] = { +static const struct snd_pci_quirk stac927x_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_D965_REF), @@ -3391,12 +2204,12 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), /* 965 based 5 stack systems */ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300, "Intel D965", STAC_D965_5ST), @@ -3407,20 +2220,10 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = { {} /* terminator */ }; -static const struct hda_pintbl ref9205_pin_configs[] = { - { 0x0a, 0x40000100 }, - { 0x0b, 0x40000100 }, - { 0x0c, 0x01016011 }, - { 0x0d, 0x01014010 }, - { 0x0e, 0x01813122 }, - { 0x0f, 0x01a19021 }, - { 0x14, 0x01019020 }, - { 0x16, 0x40000100 }, - { 0x17, 0x90a000f0 }, - { 0x18, 0x90a000f0 }, - { 0x21, 0x01441030 }, - { 0x22, 0x01c41030 }, - {} +static const unsigned int ref9205_pin_configs[12] = { + 0x40000100, 0x40000100, 0x01016011, 0x01014010, + 0x01813122, 0x01a19021, 0x01019020, 0x40000100, + 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; /* @@ -3434,20 +2237,10 @@ static const struct hda_pintbl ref9205_pin_configs[] = { 10280228 (Dell Vostro 1500) 10280229 (Dell Vostro 1700) */ -static const struct hda_pintbl dell_9205_m42_pin_configs[] = { - { 0x0a, 0x0321101F }, - { 0x0b, 0x03A11020 }, - { 0x0c, 0x400003FA }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400003FB }, - { 0x0f, 0x400003FC }, - { 0x14, 0x400003FD }, - { 0x16, 0x40F000F9 }, - { 0x17, 0x90A60330 }, - { 0x18, 0x400003FF }, - { 0x21, 0x0144131F }, - { 0x22, 0x40C003FE }, - {} +static const unsigned int dell_9205_m42_pin_configs[12] = { + 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, + 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9, + 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE, }; /* @@ -3460,124 +2253,36 @@ static const struct hda_pintbl dell_9205_m42_pin_configs[] = { 10280200 10280201 */ -static const struct hda_pintbl dell_9205_m43_pin_configs[] = { - { 0x0a, 0x0321101f }, - { 0x0b, 0x03a11020 }, - { 0x0c, 0x90a70330 }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400000fe }, - { 0x0f, 0x400000ff }, - { 0x14, 0x400000fd }, - { 0x16, 0x40f000f9 }, - { 0x17, 0x400000fa }, - { 0x18, 0x400000fc }, - { 0x21, 0x0144131f }, - { 0x22, 0x40c003f8 }, - /* Enable SPDIF in/out */ - { 0x1f, 0x01441030 }, - { 0x20, 0x1c410030 }, - {} +static const unsigned int dell_9205_m43_pin_configs[12] = { + 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310, + 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9, + 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8, }; -static const struct hda_pintbl dell_9205_m44_pin_configs[] = { - { 0x0a, 0x0421101f }, - { 0x0b, 0x04a11020 }, - { 0x0c, 0x400003fa }, - { 0x0d, 0x90170310 }, - { 0x0e, 0x400003fb }, - { 0x0f, 0x400003fc }, - { 0x14, 0x400003fd }, - { 0x16, 0x400003f9 }, - { 0x17, 0x90a60330 }, - { 0x18, 0x400003ff }, - { 0x21, 0x01441340 }, - { 0x22, 0x40c003fe }, - {} +static const unsigned int dell_9205_m44_pin_configs[12] = { + 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310, + 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9, + 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe, }; -static void stac9205_fixup_ref(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, ref9205_pin_configs); - /* SPDIF-In enabled */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0; - } -} - -static void stac9205_fixup_dell_m43(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - int err; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); - - /* Enable unsol response for GPIO4/Dock HP connection */ - err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01); - if (err < 0) - return; - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - snd_hda_jack_detect_enable(codec, codec->afg, 0); - - spec->gpio_dir = 0x0b; - spec->eapd_mask = 0x01; - spec->gpio_mask = 0x1b; - spec->gpio_mute = 0x10; - /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, - * GPIO3 Low = DRM - */ - spec->gpio_data = 0x01; - } -} - -static void stac9205_fixup_eapd(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct sigmatel_spec *spec = codec->spec; - - if (action == HDA_FIXUP_ACT_PRE_PROBE) - spec->eapd_switch = 0; -} - -static const struct hda_fixup stac9205_fixups[] = { - [STAC_9205_REF] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_ref, - }, - [STAC_9205_DELL_M42] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_9205_m42_pin_configs, - }, - [STAC_9205_DELL_M43] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_dell_m43, - }, - [STAC_9205_DELL_M44] = { - .type = HDA_FIXUP_PINS, - .v.pins = dell_9205_m44_pin_configs, - }, - [STAC_9205_EAPD] = { - .type = HDA_FIXUP_FUNC, - .v.func = stac9205_fixup_eapd, - }, - {} +static const unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { + [STAC_9205_REF] = ref9205_pin_configs, + [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs, + [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs, + [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs, + [STAC_9205_EAPD] = NULL, }; -static const struct hda_model_fixup stac9205_models[] = { - { .id = STAC_9205_REF, .name = "ref" }, - { .id = STAC_9205_DELL_M42, .name = "dell-m42" }, - { .id = STAC_9205_DELL_M43, .name = "dell-m43" }, - { .id = STAC_9205_DELL_M44, .name = "dell-m44" }, - { .id = STAC_9205_EAPD, .name = "eapd" }, - {} +static const char * const stac9205_models[STAC_9205_MODELS] = { + [STAC_9205_AUTO] = "auto", + [STAC_9205_REF] = "ref", + [STAC_9205_DELL_M42] = "dell-m42", + [STAC_9205_DELL_M43] = "dell-m43", + [STAC_9205_DELL_M44] = "dell-m44", + [STAC_9205_EAPD] = "eapd", }; -static const struct snd_pci_quirk stac9205_fixup_tbl[] = { +static const struct snd_pci_quirk stac9205_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_9205_REF), @@ -3624,6 +2329,21 @@ static const struct snd_pci_quirk stac9205_fixup_tbl[] = { {} /* terminator */ }; +static void stac92xx_set_config_regs(struct hda_codec *codec, + const unsigned int *pincfgs) +{ + int i; + struct sigmatel_spec *spec = codec->spec; + + if (!pincfgs) + return; + + for (i = 0; i < spec->num_pins; i++) + if (spec->pin_nids[i] && pincfgs[i]) + snd_hda_codec_set_pincfg(codec, spec->pin_nids[i], + pincfgs[i]); +} + /* * Analog playback callbacks */ @@ -5638,8 +4358,6 @@ static int stac92xx_init(struct hda_codec *codec) if (spec->init) snd_hda_sequence_write(codec, spec->init); - snd_hda_apply_verbs(codec); - /* power down adcs initially */ if (spec->powerdown_adcs) for (i = 0; i < spec->num_adcs; i++) @@ -6173,6 +4891,8 @@ static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) handle_unsol_event(codec, event); } +static int hp_blike_system(u32 subsystem_id); + static void set_hp_led_gpio(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -6219,48 +4939,54 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) &spec->gpio_led_polarity); return 1; } + 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_%x", + &spec->gpio_led_polarity, + &spec->gpio_led) == 2) { + unsigned int max_gpio; + max_gpio = snd_hda_param_read(codec, codec->afg, + AC_PAR_GPIO_CAP); + max_gpio &= AC_GPIO_IO_COUNT; + if (spec->gpio_led < max_gpio) + spec->gpio_led = 1 << spec->gpio_led; + else + spec->vref_mute_led_nid = spec->gpio_led; + return 1; + } + if (sscanf(dev->name, "HP_Mute_LED_%d", + &spec->gpio_led_polarity) == 1) { + set_hp_led_gpio(codec); + return 1; + } + /* BIOS bug: unfilled OEM string */ + if (strstr(dev->name, "HP_Mute_LED_P_G")) { + set_hp_led_gpio(codec); + switch (codec->subsystem_id) { + case 0x103c148a: + spec->gpio_led_polarity = 0; + break; + default: + spec->gpio_led_polarity = 1; + break; + } + return 1; + } + } - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, "HP_Mute_LED_%d_%x", - &spec->gpio_led_polarity, - &spec->gpio_led) == 2) { - unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, - AC_PAR_GPIO_CAP); - max_gpio &= AC_GPIO_IO_COUNT; - if (spec->gpio_led < max_gpio) - spec->gpio_led = 1 << spec->gpio_led; - else - spec->vref_mute_led_nid = spec->gpio_led; - return 1; - } - if (sscanf(dev->name, "HP_Mute_LED_%d", - &spec->gpio_led_polarity) == 1) { - set_hp_led_gpio(codec); - return 1; - } - /* BIOS bug: unfilled OEM string */ - if (strstr(dev->name, "HP_Mute_LED_P_G")) { + /* + * Fallback case - if we don't find the DMI strings, + * we statically set the GPIO - if not a B-series system + * and default polarity is provided + */ + if (!hp_blike_system(codec->subsystem_id) && + (default_polarity == 0 || default_polarity == 1)) { set_hp_led_gpio(codec); - if (default_polarity >= 0) - spec->gpio_led_polarity = default_polarity; - else - spec->gpio_led_polarity = 1; + spec->gpio_led_polarity = default_polarity; return 1; } } - - /* - * Fallback case - if we don't find the DMI strings, - * we statically set the GPIO - if not a B-series system - * and default polarity is provided - */ - if (!hp_blike_system(codec->subsystem_id) && - (default_polarity == 0 || default_polarity == 1)) { - set_hp_led_gpio(codec); - spec->gpio_led_polarity = default_polarity; - return 1; - } return 0; } @@ -6451,9 +5177,15 @@ static int patch_stac9200(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - - snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl, - stac9200_fixups); + spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, + stac9200_models, + stac9200_cfg_tbl); + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac9200_brd_tbl[spec->board_config]); spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; @@ -6464,11 +5196,19 @@ static int patch_stac9200(struct hda_codec *codec) spec->num_dmics = 0; spec->num_adcs = 1; spec->num_pwrs = 0; - snd_hda_add_verbs(codec, stac9200_eapd_init); + if (spec->board_config == STAC_9200_M4 || + spec->board_config == STAC_9200_M4_2 || + spec->board_config == STAC_9200_OQO) + spec->init = stac9200_eapd_init; + else + spec->init = stac9200_core_init; spec->mixer = stac9200_mixer; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + if (spec->board_config == STAC_9200_PANASONIC) { + spec->gpio_mask = spec->gpio_dir = 0x09; + spec->gpio_data = 0x00; + } err = stac9200_parse_auto_config(codec); if (err < 0) { @@ -6476,9 +5216,13 @@ static int patch_stac9200(struct hda_codec *codec) return err; } - codec->patch_ops = stac92xx_patch_ops; + /* CF-74 has no headphone detection, and the driver should *NOT* + * do detection and HP/speaker toggle because the hardware does it. + */ + if (spec->board_config == STAC_9200_PANASONIC) + spec->hp_detect = 0; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + codec->patch_ops = stac92xx_patch_ops; return 0; } @@ -6496,8 +5240,25 @@ static int patch_stac925x(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl, - stac925x_fixups); + /* Check first for codec ID */ + spec->board_config = snd_hda_check_board_codec_sid_config(codec, + STAC_925x_MODELS, + stac925x_models, + stac925x_codec_id_cfg_tbl); + + /* Now checks for PCI ID, if codec ID is not found */ + if (spec->board_config < 0) + spec->board_config = snd_hda_check_board_config(codec, + STAC_925x_MODELS, + stac925x_models, + stac925x_cfg_tbl); + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac925x_brd_tbl[spec->board_config]); spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; @@ -6522,17 +5283,22 @@ static int patch_stac925x(struct hda_codec *codec) break; } - snd_hda_add_verbs(codec, stac925x_core_init); + spec->init = stac925x_core_init; spec->mixer = stac925x_mixer; spec->num_caps = 1; spec->capvols = stac925x_capvols; spec->capsws = stac925x_capsws; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac92xx_parse_auto_config(codec); - if (!err) + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_925x_REF; + goto again; + } err = -EINVAL; + } if (err < 0) { stac92xx_free(codec); return err; @@ -6540,8 +5306,6 @@ static int patch_stac925x(struct hda_codec *codec) codec->patch_ops = stac92xx_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; } @@ -6560,9 +5324,23 @@ static int patch_stac92hd73xx(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 0; codec->slave_dig_outs = stac92hd73xx_slave_dig_outs; - - snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl, - stac92hd73xx_fixups); + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD73XX_MODELS, + stac92hd73xx_models, + stac92hd73xx_cfg_tbl); + /* check codec subsystem id if not found */ + if (spec->board_config < 0) + spec->board_config = + snd_hda_check_board_codec_sid_config(codec, + STAC_92HD73XX_MODELS, stac92hd73xx_models, + stac92hd73xx_codec_id_cfg_tbl); +again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac92hd73xx_brd_tbl[spec->board_config]); num_dacs = snd_hda_get_connections(codec, 0x0a, conn, STAC92HD73_DAC_COUNT + 2) - 1; @@ -6572,7 +5350,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) "number of channels defaulting to DAC count\n"); num_dacs = STAC92HD73_DAC_COUNT; } - + spec->init = stac92hd73xx_core_init; switch (num_dacs) { case 0x3: /* 6 Channel */ spec->aloopback_ctl = stac92hd73xx_6ch_loopback; @@ -6604,37 +5382,76 @@ static int patch_stac92hd73xx(struct hda_codec *codec) spec->capvols = stac92hd73xx_capvols; spec->capsws = stac92hd73xx_capsws; - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; + switch (spec->board_config) { + case STAC_DELL_EQ: + spec->init = dell_eq_core_init; + /* fallthru */ + case STAC_DELL_M6_AMIC: + case STAC_DELL_M6_DMIC: + case STAC_DELL_M6_BOTH: + spec->num_smuxes = 0; + spec->eapd_switch = 0; - spec->num_dmics = STAC92HD73XX_NUM_DMICS; - spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); - spec->eapd_switch = 1; + switch (spec->board_config) { + case STAC_DELL_M6_AMIC: /* Analog Mics */ + snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); + spec->num_dmics = 0; + break; + case STAC_DELL_M6_DMIC: /* Digital Mics */ + snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + case STAC_DELL_M6_BOTH: /* Both */ + snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170); + snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160); + spec->num_dmics = 1; + break; + } + break; + case STAC_ALIENWARE_M17X: + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 0; + break; + default: + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids); + spec->eapd_switch = 1; + break; + } + if (spec->board_config != STAC_92HD73XX_REF) { + /* GPIO0 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; + } spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); spec->pwr_nids = stac92hd73xx_pwr_nids; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - - if (!spec->volknob_init) - snd_hda_add_verbs(codec, stac92hd73xx_core_init); - err = stac92xx_parse_auto_config(codec); - if (!err) + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD73XX_REF; + goto again; + } err = -EINVAL; + } + if (err < 0) { stac92xx_free(codec); return err; } + if (spec->board_config == STAC_92HD73XX_NO_JD) + spec->hp_detect = 0; + codec->patch_ops = stac92xx_patch_ops; codec->proc_widget_hook = stac92hd7x_proc_hook; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; } @@ -6777,38 +5594,21 @@ static void stac92hd8x_fill_auto_spec(struct hda_codec *codec) spec->num_dmics = spec->auto_dmic_cnt; } -static void stac_setup_gpio(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - - if (spec->gpio_led) { - if (!spec->vref_mute_led_nid) { - 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; - } - } - - if (spec->mic_mute_led_gpio) { - spec->gpio_mask |= spec->mic_mute_led_gpio; - spec->gpio_dir |= spec->mic_mute_led_gpio; - spec->mic_mute_led_on = true; - spec->gpio_data |= spec->mic_mute_led_gpio; - } -} - static int patch_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; + int default_polarity = -1; /* no default cfg */ int err; err = alloc_stac_spec(codec, 0, NULL); /* pins filled later */ if (err < 0) return err; + if (hp_bnb2011_with_dock(codec)) { + snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f); + snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e); + } + codec->epss = 0; /* longer delay needed for D3 */ stac92hd8x_fill_auto_spec(codec); @@ -6819,22 +5619,80 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) spec->pwr_nids = stac92hd83xxx_pwr_nids; spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); spec->multiout.dac_nids = spec->dac_nids; + spec->init = stac92hd83xxx_core_init; + + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD83XXX_MODELS, + stac92hd83xxx_models, + stac92hd83xxx_cfg_tbl); + /* check codec subsystem id if not found */ + if (spec->board_config < 0) + spec->board_config = + snd_hda_check_board_codec_sid_config(codec, + STAC_92HD83XXX_MODELS, stac92hd83xxx_models, + stac92hd83xxx_codec_id_cfg_tbl); +again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac92hd83xxx_brd_tbl[spec->board_config]); - snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, - stac92hd83xxx_fixups); + codec->patch_ops = stac92xx_patch_ops; - snd_hda_add_verbs(codec, stac92hd83xxx_core_init); - spec->default_polarity = -1; /* no default cfg */ + switch (spec->board_config) { + case STAC_HP_ZEPHYR: + spec->init = stac92hd83xxx_hp_zephyr_init; + break; + case STAC_92HD83XXX_HP_LED: + default_polarity = 0; + break; + case STAC_92HD83XXX_HP_INV_LED: + default_polarity = 1; + break; + case STAC_92HD83XXX_HP_MIC_LED: + spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ + break; + case STAC_92HD83XXX_HEADSET_JACK: + spec->headset_jack = 1; + break; + } - codec->patch_ops = stac92xx_patch_ops; + if (find_mute_led_cfg(codec, default_polarity)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + if (spec->gpio_led) { + if (!spec->vref_mute_led_nid) { + 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; + } + } - stac_setup_gpio(codec); + if (spec->mic_mute_led_gpio) { + spec->gpio_mask |= spec->mic_mute_led_gpio; + spec->gpio_dir |= spec->mic_mute_led_gpio; + spec->mic_mute_led_on = true; + spec->gpio_data |= spec->mic_mute_led_gpio; + } err = stac92xx_parse_auto_config(codec); - if (!err) + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD83XXX_REF; + goto again; + } err = -EINVAL; + } + if (err < 0) { stac92xx_free(codec); return err; @@ -6842,8 +5700,6 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) codec->proc_widget_hook = stac92hd_proc_hook; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; } @@ -6924,6 +5780,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; + unsigned int pin_cfg; int err; err = alloc_stac_spec(codec, STAC92HD71BXX_NUM_PINS, @@ -6947,14 +5804,24 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->pin_nids = stac92hd71bxx_pin_nids_6port; } spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD71BXX_MODELS, + stac92hd71bxx_models, + stac92hd71bxx_cfg_tbl); +again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac92hd71bxx_brd_tbl[spec->board_config]); - snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl, - stac92hd71bxx_fixups); - - /* GPIO0 = EAPD */ - spec->gpio_mask = 0x01; - spec->gpio_dir = 0x01; - spec->gpio_data = 0x01; + if (spec->board_config != STAC_92HD71BXX_REF) { + /* GPIO0 = EAPD */ + spec->gpio_mask = 0x01; + spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; + } spec->dmic_nids = stac92hd71bxx_dmic_nids; spec->dmux_nids = stac92hd71bxx_dmux_nids; @@ -6976,6 +5843,19 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) STAC92HD71BXX_NUM_DMICS); break; case 0x111d7608: /* 5 Port with Analog Mixer */ + switch (spec->board_config) { + case STAC_HP_M4: + /* Enable VREF power saving on GPIO1 detect */ + err = stac_add_event(codec, codec->afg, + STAC_VREF_EVENT, 0x02); + if (err < 0) + return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); + snd_hda_jack_detect_enable(codec, codec->afg, 0); + spec->gpio_mask |= 0x02; + break; + } if ((codec->revision_id & 0xf) == 0 || (codec->revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ @@ -7003,7 +5883,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) } if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB) - snd_hda_add_verbs(codec, stac92hd71bxx_core_init); + spec->init = stac92hd71bxx_core_init; if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) snd_hda_sequence_write_cache(codec, unmute_init); @@ -7024,23 +5904,117 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + snd_printdd("Found board config: %d\n", spec->board_config); - stac_setup_gpio(codec); + switch (spec->board_config) { + case STAC_HP_M4: + /* enable internal microphone */ + snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040); + stac92xx_auto_set_pinctl(codec, 0x0e, + AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); + /* fallthru */ + case STAC_DELL_M4_2: + spec->num_dmics = 0; + spec->num_smuxes = 0; + spec->num_dmuxes = 0; + break; + case STAC_DELL_M4_1: + case STAC_DELL_M4_3: + spec->num_dmics = 1; + spec->num_smuxes = 0; + spec->num_dmuxes = 1; + break; + case STAC_HP_DV4_1222NR: + spec->num_dmics = 1; + /* I don't know if it needs 1 or 2 smuxes - will wait for + * bug reports to fix if needed + */ + spec->num_smuxes = 1; + spec->num_dmuxes = 1; + /* fallthrough */ + case STAC_HP_DV4: + spec->gpio_led = 0x01; + /* fallthrough */ + case STAC_HP_DV5: + snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010); + stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN); + /* HP dv6 gives the headphone pin as a line-out. Thus we + * need to set hp_detect flag here to force to enable HP + * detection. + */ + spec->hp_detect = 1; + break; + case STAC_HP_HDX: + spec->num_dmics = 1; + spec->num_dmuxes = 1; + spec->num_smuxes = 1; + spec->gpio_led = 0x08; + break; + } + + if (hp_blike_system(codec->subsystem_id)) { + pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); + if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || + get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || + get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) { + /* It was changed in the BIOS to just satisfy MS DTM. + * Lets turn it back into slaved HP + */ + pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) + | (AC_JACK_HP_OUT << + AC_DEFCFG_DEVICE_SHIFT); + pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC + | AC_DEFCFG_SEQUENCE))) + | 0x1f; + snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg); + } + } + + if (find_mute_led_cfg(codec, 1)) + snd_printd("mute LED gpio %d polarity %d\n", + spec->gpio_led, + spec->gpio_led_polarity); + + if (spec->gpio_led) { + if (!spec->vref_mute_led_nid) { + 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; + } + } spec->multiout.dac_nids = spec->dac_nids; err = stac92xx_parse_auto_config(codec); - if (!err) + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD71BXX_REF; + goto again; + } err = -EINVAL; + } + if (err < 0) { stac92xx_free(codec); return err; } - codec->proc_widget_hook = stac92hd7x_proc_hook; + /* enable bass on HP dv7 */ + if (spec->board_config == STAC_HP_DV4 || + spec->board_config == STAC_HP_DV5) { + unsigned int cap; + cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP); + cap &= AC_GPIO_IO_COUNT; + if (cap >= 6) + stac_add_hp_bass_switch(codec); + } - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + codec->proc_widget_hook = stac92hd7x_proc_hook; return 0; } @@ -7057,9 +6031,54 @@ static int patch_stac922x(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; + spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, + stac922x_models, + stac922x_cfg_tbl); + if (spec->board_config == STAC_INTEL_MAC_AUTO) { + spec->gpio_mask = spec->gpio_dir = 0x03; + spec->gpio_data = 0x03; + /* Intel Macs have all same PCI SSID, so we need to check + * codec SSID to distinguish the exact models + */ + printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); + switch (codec->subsystem_id) { + + case 0x106b0800: + spec->board_config = STAC_INTEL_MAC_V1; + break; + case 0x106b0600: + case 0x106b0700: + spec->board_config = STAC_INTEL_MAC_V2; + break; + case 0x106b0e00: + case 0x106b0f00: + case 0x106b1600: + case 0x106b1700: + case 0x106b0200: + case 0x106b1e00: + spec->board_config = STAC_INTEL_MAC_V3; + break; + case 0x106b1a00: + case 0x00000100: + spec->board_config = STAC_INTEL_MAC_V4; + break; + case 0x106b0a00: + case 0x106b2200: + spec->board_config = STAC_INTEL_MAC_V5; + break; + default: + spec->board_config = STAC_INTEL_MAC_V3; + break; + } + } - snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl, - stac922x_fixups); + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac922x_brd_tbl[spec->board_config]); spec->adc_nids = stac922x_adc_nids; spec->mux_nids = stac922x_mux_nids; @@ -7068,19 +6087,24 @@ static int patch_stac922x(struct hda_codec *codec) spec->num_dmics = 0; spec->num_pwrs = 0; + spec->init = stac922x_core_init; + spec->num_caps = STAC922X_NUM_CAPS; spec->capvols = stac922x_capvols; spec->capsws = stac922x_capsws; spec->multiout.dac_nids = spec->dac_nids; - snd_hda_add_verbs(codec, stac922x_core_init); - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac92xx_parse_auto_config(codec); - if (!err) + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_D945_REF; + goto again; + } err = -EINVAL; + } if (err < 0) { stac92xx_free(codec); return err; @@ -7095,8 +6119,6 @@ static int patch_stac922x(struct hda_codec *codec) (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | (0 << AC_AMPCAP_MUTE_SHIFT)); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; } @@ -7113,9 +6135,16 @@ static int patch_stac927x(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; codec->slave_dig_outs = stac927x_slave_dig_outs; - - snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl, - stac927x_fixups); + spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, + stac927x_models, + stac927x_cfg_tbl); + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac927x_brd_tbl[spec->board_config]); spec->digbeep_nid = 0x23; spec->adc_nids = stac927x_adc_nids; @@ -7128,11 +6157,56 @@ static int patch_stac927x(struct hda_codec *codec) spec->dac_list = stac927x_dac_nids; spec->multiout.dac_nids = spec->dac_nids; - /* GPIO0 High = Enable EAPD */ - spec->eapd_mask = spec->gpio_mask = 0x01; - spec->gpio_dir = spec->gpio_data = 0x01; + if (spec->board_config != STAC_D965_REF) { + /* GPIO0 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x01; + spec->gpio_dir = spec->gpio_data = 0x01; + } - spec->num_dmics = 0; + switch (spec->board_config) { + case STAC_D965_3ST: + case STAC_D965_5ST: + /* GPIO0 High = Enable EAPD */ + spec->num_dmics = 0; + spec->init = d965_core_init; + break; + case STAC_DELL_BIOS: + switch (codec->subsystem_id) { + case 0x10280209: + case 0x1028022e: + /* correct the device field to SPDIF out */ + snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070); + break; + } + /* configure the analog microphone on some laptops */ + snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130); + /* correct the front output jack as a hp out */ + snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f); + /* correct the front input jack as a mic */ + snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130); + /* fallthru */ + case STAC_DELL_3ST: + if (codec->subsystem_id != 0x1028022f) { + /* GPIO2 High = Enable EAPD */ + spec->eapd_mask = spec->gpio_mask = 0x04; + spec->gpio_dir = spec->gpio_data = 0x04; + } + spec->dmic_nids = stac927x_dmic_nids; + spec->num_dmics = STAC927X_NUM_DMICS; + + spec->init = dell_3st_core_init; + spec->dmux_nids = stac927x_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); + break; + case STAC_927X_VOLKNOB: + spec->num_dmics = 0; + spec->init = stac927x_volknob_core_init; + break; + default: + spec->num_dmics = 0; + spec->init = stac927x_core_init; + break; + } spec->num_caps = STAC927X_NUM_CAPS; spec->capvols = stac927x_capvols; @@ -7144,14 +6218,16 @@ static int patch_stac927x(struct hda_codec *codec) spec->aloopback_shift = 0; spec->eapd_switch = 1; - if (!spec->volknob_init) - snd_hda_add_verbs(codec, stac927x_core_init); - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = stac92xx_parse_auto_config(codec); - if (!err) + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_D965_REF; + goto again; + } err = -EINVAL; + } if (err < 0) { stac92xx_free(codec); return err; @@ -7173,7 +6249,9 @@ static int patch_stac927x(struct hda_codec *codec) */ codec->bus->needs_damn_long_delay = 1; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + /* no jack detecion for ref-no-jd model */ + if (spec->board_config == STAC_D965_REF_NO_JD) + spec->hp_detect = 0; return 0; } @@ -7190,9 +6268,16 @@ static int patch_stac9205(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - - snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl, - stac9205_fixups); + spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS, + stac9205_models, + stac9205_cfg_tbl); + again: + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac9205_brd_tbl[spec->board_config]); spec->digbeep_nid = 0x23; spec->adc_nids = stac9205_adc_nids; @@ -7207,7 +6292,7 @@ static int patch_stac9205(struct hda_codec *codec) spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); spec->num_pwrs = 0; - snd_hda_add_verbs(codec, stac9205_core_init); + spec->init = stac9205_core_init; spec->aloopback_ctl = stac9205_loopback; spec->num_caps = STAC9205_NUM_CAPS; @@ -7216,20 +6301,54 @@ static int patch_stac9205(struct hda_codec *codec) spec->aloopback_mask = 0x40; spec->aloopback_shift = 0; + /* Turn on/off EAPD per HP plugging */ + if (spec->board_config != STAC_9205_EAPD) + spec->eapd_switch = 1; spec->multiout.dac_nids = spec->dac_nids; - /* GPIO0 High = EAPD */ - spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; - spec->gpio_data = 0x01; + switch (spec->board_config){ + case STAC_9205_DELL_M43: + /* Enable SPDIF in/out */ + snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030); + snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030); - /* Turn on/off EAPD per HP plugging */ - spec->eapd_switch = 1; + /* Enable unsol response for GPIO4/Dock HP connection */ + err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01); + if (err < 0) + return err; + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_jack_detect_enable(codec, codec->afg, 0); - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + spec->gpio_dir = 0x0b; + spec->eapd_mask = 0x01; + spec->gpio_mask = 0x1b; + spec->gpio_mute = 0x10; + /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, + * GPIO3 Low = DRM + */ + spec->gpio_data = 0x01; + break; + case STAC_9205_REF: + /* SPDIF-In enabled */ + break; + default: + /* GPIO0 High = EAPD */ + spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; + break; + } err = stac92xx_parse_auto_config(codec); - if (!err) + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_9205_REF; + goto again; + } err = -EINVAL; + } if (err < 0) { stac92xx_free(codec); return err; @@ -7239,8 +6358,6 @@ static int patch_stac9205(struct hda_codec *codec) codec->proc_widget_hook = stac9205_proc_hook; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; } @@ -7272,32 +6389,22 @@ static const unsigned long stac9872_capvols[] = { }; #define stac9872_capsws stac9872_capvols -static const struct hda_pintbl stac9872_vaio_pin_configs[] = { - { 0x0a, 0x03211020 }, - { 0x0b, 0x411111f0 }, - { 0x0c, 0x411111f0 }, - { 0x0d, 0x03a15030 }, - { 0x0e, 0x411111f0 }, - { 0x0f, 0x90170110 }, - { 0x11, 0x411111f0 }, - { 0x13, 0x411111f0 }, - { 0x14, 0x90a7013e }, - {} +static const unsigned int stac9872_vaio_pin_configs[9] = { + 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030, + 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0, + 0x90a7013e }; -static const struct hda_model_fixup stac9872_models[] = { - { .id = STAC_9872_VAIO, .name = "vaio" }, - {} +static const char * const stac9872_models[STAC_9872_MODELS] = { + [STAC_9872_AUTO] = "auto", + [STAC_9872_VAIO] = "vaio", }; -static const struct hda_fixup stac9872_fixups[] = { - [STAC_9872_VAIO] = { - .type = HDA_FIXUP_PINS, - .v.pins = stac9872_vaio_pin_configs, - }, +static const unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = { + [STAC_9872_VAIO] = stac9872_vaio_pin_configs, }; -static const struct snd_pci_quirk stac9872_fixup_tbl[] = { +static const struct snd_pci_quirk stac9872_cfg_tbl[] = { SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0, "Sony VAIO F/S", STAC_9872_VAIO), {} /* terminator */ @@ -7316,20 +6423,25 @@ static int patch_stac9872(struct hda_codec *codec) spec = codec->spec; spec->linear_tone_beep = 1; - snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl, - stac9872_fixups); + spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS, + stac9872_models, + stac9872_cfg_tbl); + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); + else + stac92xx_set_config_regs(codec, + stac9872_brd_tbl[spec->board_config]); spec->multiout.dac_nids = spec->dac_nids; spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids); spec->adc_nids = stac9872_adc_nids; spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids); spec->mux_nids = stac9872_mux_nids; + spec->init = stac9872_core_init; spec->num_caps = 1; spec->capvols = stac9872_capvols; spec->capsws = stac9872_capsws; - snd_hda_add_verbs(codec, stac9872_core_init); - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = stac92xx_parse_auto_config(codec); if (err < 0) { @@ -7338,9 +6450,6 @@ static int patch_stac9872(struct hda_codec *codec) } spec->input_mux = &spec->private_imux; codec->patch_ops = stac92xx_patch_ops; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); - return 0; } diff --git a/trunk/sound/pci/hda/patch_via.c b/trunk/sound/pci/hda/patch_via.c index eade21c3e0b1..d3c852ab105e 100644 --- a/trunk/sound/pci/hda/patch_via.c +++ b/trunk/sound/pci/hda/patch_via.c @@ -56,7 +56,6 @@ #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" -#include "hda_generic.h" /* Pin Widget NID */ #define VT1708_HP_PIN_NID 0x20 @@ -87,9 +86,40 @@ enum VIA_HDA_CODEC { (spec)->codec_type == VT1812 ||\ (spec)->codec_type == VT1802) -struct via_spec { - struct hda_gen_spec gen; +#define MAX_NID_PATH_DEPTH 5 + +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int vol_ctl; + unsigned int mute_ctl; +}; + +/* input-path */ +struct via_input { + hda_nid_t pin; /* input-pin or aa-mix */ + int adc_idx; /* ADC index to be used */ + int mux_idx; /* MUX index (if any) */ + const char *label; /* input-source label */ +}; + +#define VIA_MAX_ADCS 3 + +enum { + STREAM_MULTI_OUT = (1 << 0), + STREAM_INDEP_HP = (1 << 1), +}; +struct via_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; unsigned int num_mixers; @@ -97,7 +127,77 @@ struct via_spec { const struct hda_verb *init_verbs[5]; unsigned int num_iverbs; + char stream_name_analog[32]; + char stream_name_hp[32]; + const struct hda_pcm_stream *stream_analog_playback; + const struct hda_pcm_stream *stream_analog_capture; + + char stream_name_digital[32]; + const struct hda_pcm_stream *stream_digital_playback; + const struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; + hda_nid_t slave_dig_outs[2]; + hda_nid_t hp_dac_nid; + hda_nid_t speaker_dac_nid; + int hp_indep_shared; /* indep HP-DAC is shared with side ch */ + int opened_streams; /* STREAM_* bits */ + int active_streams; /* STREAM_* bits */ + int aamix_mode; /* loopback is enabled for output-path? */ + + /* Output-paths: + * There are different output-paths depending on the setup. + * out_path, hp_path and speaker_path are primary paths. If both + * direct DAC and aa-loopback routes are available, these contain + * the former paths. Meanwhile *_mix_path contain the paths with + * loopback mixer. (Since the loopback is only for front channel, + * no out_mix_path for surround channels.) + * The HP output has another path, hp_indep_path, which is used in + * the independent-HP mode. + */ + struct nid_path out_path[HDA_SIDE + 1]; + struct nid_path out_mix_path; + struct nid_path hp_path; + struct nid_path hp_mix_path; + struct nid_path hp_indep_path; + struct nid_path speaker_path; + struct nid_path speaker_mix_path; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t adc_nids[VIA_MAX_ADCS]; + hda_nid_t mux_nids[VIA_MAX_ADCS]; + hda_nid_t aa_mix_nid; + hda_nid_t dig_in_nid; + + /* capture source */ + bool dyn_adc_switch; + int num_inputs; + struct via_input inputs[AUTO_CFG_MAX_INS + 1]; + unsigned int cur_mux[VIA_MAX_ADCS]; + + /* dynamic DAC switching */ + unsigned int cur_dac_stream_tag; + unsigned int cur_dac_format; + unsigned int cur_hp_stream_tag; + unsigned int cur_hp_format; + + /* dynamic ADC switching */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + + /* PCM information */ + struct hda_pcm pcm_rec[3]; + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + /* HP mode source */ + unsigned int hp_independent_mode; unsigned int dmic_enabled; unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; @@ -105,22 +205,36 @@ struct via_spec { /* analog low-power control */ bool alc_mode; + /* smart51 setup */ + unsigned int smart51_nums; + hda_nid_t smart51_pins[2]; + int smart51_idxs[2]; + const char *smart51_labels[2]; + unsigned int smart51_enabled; + /* work to check hp jack state */ + struct hda_codec *codec; + struct delayed_work vt1708_hp_work; int hp_work_active; int vt1708_jack_detect; + int vt1708_hp_present; void (*set_widgets_power_state)(struct hda_codec *codec); unsigned int dac_stream_tag[4]; + + struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; + + /* bind capture-volume */ + struct hda_bind_ctls *bind_cap_vol; + struct hda_bind_ctls *bind_cap_sw; + + struct mutex config_mutex; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); -static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action); -static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl); - -static struct via_spec *via_new_spec(struct hda_codec *codec) +static struct via_spec * via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -128,14 +242,14 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) if (spec == NULL) return NULL; + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + mutex_init(&spec->config_mutex); codec->spec = spec; - snd_hda_gen_spec_init(&spec->gen); + spec->codec = codec; spec->codec_type = get_codec_type(codec); /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; - spec->no_pin_power_ctl = 1; - spec->gen.pcm_playback_hook = via_playback_pcm_hook; return spec; } @@ -191,6 +305,16 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) return codec_type; }; +#define VIA_JACK_EVENT 0x20 +#define VIA_HP_EVENT 0x01 +#define VIA_LINE_EVENT 0x03 + +enum { + VIA_CTL_WIDGET_VOL, + VIA_CTL_WIDGET_MUTE, + VIA_CTL_WIDGET_ANALOG_MUTE, +}; + static void analog_low_current_mode(struct hda_codec *codec); static bool is_aa_path_mute(struct hda_codec *codec); @@ -198,35 +322,31 @@ static bool is_aa_path_mute(struct hda_codec *codec); (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ !is_aa_path_mute(codec)) -static void vt1708_stop_hp_work(struct hda_codec *codec) +static void vt1708_stop_hp_work(struct via_spec *spec) { - struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) return; if (spec->hp_work_active) { - snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); - cancel_delayed_work_sync(&codec->jackpoll_work); - spec->hp_work_active = false; - codec->jackpoll_interval = 0; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1); + cancel_delayed_work_sync(&spec->vt1708_hp_work); + spec->hp_work_active = 0; } } -static void vt1708_update_hp_work(struct hda_codec *codec) +static void vt1708_update_hp_work(struct via_spec *spec) { - struct via_spec *spec = codec->spec; - if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) + if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) return; if (spec->vt1708_jack_detect && - (spec->gen.active_streams || hp_detect_with_aa(codec))) { + (spec->active_streams || hp_detect_with_aa(spec->codec))) { if (!spec->hp_work_active) { - codec->jackpoll_interval = msecs_to_jiffies(100); - snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); - queue_delayed_work(codec->bus->workq, - &codec->jackpoll_work, 0); - spec->hp_work_active = true; + snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0); + schedule_delayed_work(&spec->vt1708_hp_work, + msecs_to_jiffies(100)); + spec->hp_work_active = 1; } - } else if (!hp_detect_with_aa(codec)) - vt1708_stop_hp_work(codec); + } else if (!hp_detect_with_aa(spec->codec)) + vt1708_stop_hp_work(spec); } static void set_widgets_power_state(struct hda_codec *codec) @@ -236,6 +356,356 @@ static void set_widgets_power_state(struct hda_codec *codec) spec->set_widgets_power_state(codec); } +static int analog_input_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + set_widgets_power_state(codec); + analog_low_current_mode(snd_kcontrol_chip(kcontrol)); + vt1708_update_hp_work(codec->spec); + return change; +} + +/* modify .put = snd_hda_mixer_amp_switch_put */ +#define ANALOG_INPUT_MUTE \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = NULL, \ + .index = 0, \ + .info = snd_hda_mixer_amp_switch_info, \ + .get = snd_hda_mixer_amp_switch_get, \ + .put = analog_input_switch_put, \ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } + +static const struct snd_kcontrol_new via_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + ANALOG_INPUT_MUTE, +}; + + +/* add dynamic controls */ +static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, + const struct snd_kcontrol_new *tmpl, + const char *name) +{ + struct snd_kcontrol_new *knew; + + knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *tmpl; + if (!name) + name = tmpl->name; + if (name) { + knew->name = kstrdup(name, GFP_KERNEL); + if (!knew->name) + return NULL; + } + return knew; +} + +static int __via_add_control(struct via_spec *spec, int type, const char *name, + int idx, unsigned long val) +{ + struct snd_kcontrol_new *knew; + + knew = __via_clone_ctl(spec, &via_control_templates[type], name); + if (!knew) + return -ENOMEM; + knew->index = idx; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + knew->private_value = val; + return 0; +} + +#define via_add_control(spec, type, name, val) \ + __via_add_control(spec, type, name, 0, val) + +#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) + +static void via_free_kctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +} + +/* create input playback/capture controls for the given pin */ +static int via_new_analog_input(struct via_spec *spec, const char *ctlname, + int type_idx, int idx, int mix_nid) +{ + char name[32]; + int err; + + sprintf(name, "%s Playback Volume", ctlname); + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", ctlname); + err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, + HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + if (err < 0) + return err; + return 0; +} + +#define get_connection_index(codec, mux, nid) \ + snd_hda_get_conn_index(codec, mux, nid, 0) + +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int mask) +{ + unsigned int caps; + if (!nid) + return false; + caps = get_wcaps(codec, nid); + if (dir == HDA_INPUT) + caps &= AC_WCAP_IN_AMP; + else + caps &= AC_WCAP_OUT_AMP; + if (!caps) + return false; + if (query_amp_caps(codec, nid, dir) & mask) + return true; + return false; +} + +#define have_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) + +/* enable/disable the output-route mixers */ +static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, + hda_nid_t mix_nid, int idx, bool enable) +{ + int i, num, val; + + if (!path) + return; + num = snd_hda_get_num_conns(codec, mix_nid); + for (i = 0; i < num; i++) { + if (i == idx) + val = AMP_IN_UNMUTE(i); + else + val = AMP_IN_MUTE(i); + snd_hda_codec_write(codec, mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } +} + +/* enable/disable the output-route */ +static void activate_output_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool force) +{ + struct via_spec *spec = codec->spec; + int i; + for (i = 0; i < path->depth; i++) { + hda_nid_t src, dst; + int idx = path->idx[i]; + src = path->path[i]; + if (i < path->depth - 1) + dst = path->path[i + 1]; + else + dst = 0; + if (enable && path->multi[i]) + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_CONNECT_SEL, idx); + if (!force && (dst == spec->aa_mix_nid)) + continue; + if (have_mute(codec, dst, HDA_INPUT)) + activate_output_mix(codec, path, dst, idx, enable); + if (!force && (src == path->vol_ctl || src == path->mute_ctl)) + continue; + if (have_mute(codec, src, HDA_OUTPUT)) { + int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; + snd_hda_codec_write(codec, src, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + } +} + +/* set the given pin as output */ +static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, + int pin_type) +{ + if (!pin) + return; + snd_hda_set_pin_ctl(codec, pin, pin_type); + if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); +} + +static void via_auto_init_output(struct hda_codec *codec, + struct nid_path *path, int pin_type) +{ + unsigned int caps; + hda_nid_t pin; + + if (!path->depth) + return; + pin = path->path[path->depth - 1]; + + init_output_pin(codec, pin, pin_type); + if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP) + caps = query_amp_caps(codec, pin, HDA_OUTPUT); + else + caps = 0; + if (caps & AC_AMPCAP_MUTE) { + unsigned int val; + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE | val); + } + activate_output_path(codec, path, true, true); /* force on */ +} + +static void via_auto_init_multi_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct nid_path *path; + int i; + + for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) { + path = &spec->out_path[i]; + if (!i && spec->aamix_mode && spec->out_mix_path.depth) + path = &spec->out_mix_path; + via_auto_init_output(codec, path, PIN_OUT); + } +} + +/* deactivate the inactive headphone-paths */ +static void deactivate_hp_paths(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int shared = spec->hp_indep_shared; + + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_path, false, false); + activate_output_path(codec, &spec->hp_mix_path, false, false); + if (shared) + activate_output_path(codec, &spec->out_path[shared], + false, false); + } else if (spec->aamix_mode || !spec->hp_path.depth) { + activate_output_path(codec, &spec->hp_indep_path, false, false); + activate_output_path(codec, &spec->hp_path, false, false); + } else { + activate_output_path(codec, &spec->hp_indep_path, false, false); + activate_output_path(codec, &spec->hp_mix_path, false, false); + } +} + +static void via_auto_init_hp_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->hp_path.depth) { + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); + return; + } + deactivate_hp_paths(codec); + if (spec->hp_independent_mode) + via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP); + else if (spec->aamix_mode) + via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP); + else + via_auto_init_output(codec, &spec->hp_path, PIN_HP); +} + +static void via_auto_init_speaker_out(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->autocfg.speaker_outs) + return; + if (!spec->speaker_path.depth) { + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); + return; + } + if (!spec->aamix_mode) { + activate_output_path(codec, &spec->speaker_mix_path, + false, false); + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT); + } else { + activate_output_path(codec, &spec->speaker_path, false, false); + via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT); + } +} + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); +static void via_hp_automute(struct hda_codec *codec); + +static void via_auto_init_analog_input(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; + unsigned int ctl; + int i, num_conns; + + /* init ADCs */ + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t nid = spec->adc_nids[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) || + !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)) + continue; + snd_hda_codec_write(codec, spec->adc_nids[i], 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + } + + /* init pins */ + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (spec->smart51_enabled && is_smart51_pins(codec, nid)) + ctl = PIN_OUT; + else { + ctl = PIN_IN; + if (cfg->inputs[i].type == AUTO_PIN_MIC) + ctl |= snd_hda_get_default_vref(codec, nid); + } + snd_hda_set_pin_ctl(codec, nid, ctl); + } + + /* init input-src */ + for (i = 0; i < spec->num_adc_nids; i++) { + int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; + /* secondary ADCs must have the unique MUX */ + if (i > 0 && !spec->mux_nids[i]) + break; + if (spec->mux_nids[adc_idx]) { + int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; + snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + } + if (spec->dyn_adc_switch) + break; /* only one input-src */ + } + + /* init aa-mixer */ + if (!spec->aa_mix_nid) + return; + num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, + ARRAY_SIZE(conn)); + for (i = 0; i < num_conns; i++) { + unsigned int caps = get_wcaps(codec, conn[i]); + if (get_wcaps_type(caps) == AC_WID_PIN) + snd_hda_codec_write(codec, spec->aa_mix_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(i)); + } +} + static void update_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int parm) { @@ -267,23 +737,6 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, } } -static bool smart51_enabled(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - return spec->gen.ext_channel_count > 2; -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->gen.multi_ios; i++) - if (spec->gen.multi_io[i].pin == pin) - return true; - return false; -} - static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, unsigned int *affected_parm) { @@ -298,7 +751,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, no_presence |= spec->no_pin_power_ctl; if (!no_presence) present = snd_hda_jack_detect(codec, nid); - if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) + if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) || ((no_presence || present) && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { *affected_parm = AC_PWRST_D0; /* if it's connected */ @@ -339,185 +792,1801 @@ 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 */ }; -/* check AA path's mute status */ -static bool is_aa_path_mute(struct hda_codec *codec) +static int via_independent_hp_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[] = { "OFF", "ON" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int via_independent_hp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - const struct hda_amp_list *p; - int i, ch, v; - for (i = 0; i < spec->gen.num_loopbacks; i++) { - p = &spec->gen.loopback_list[i]; - for (ch = 0; ch < 2; ch++) { - v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, - p->idx); - if (!(v & HDA_AMP_MUTE) && v > 0) - return false; - } + ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; + return 0; +} + +/* adjust spec->multiout setup according to the current flags */ +static void setup_playback_multi_pcm(struct via_spec *spec) +{ + const struct auto_pin_cfg *cfg = &spec->autocfg; + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + spec->multiout.hp_nid = 0; + if (!spec->hp_independent_mode) { + if (!spec->hp_indep_shared) + spec->multiout.hp_nid = spec->hp_dac_nid; + } else { + if (spec->hp_indep_shared) + spec->multiout.num_dacs = cfg->line_outs - 1; } - return true; } -/* enter/exit analog low-current mode */ -static void __analog_low_current_mode(struct hda_codec *codec, bool force) +/* update DAC setups according to indep-HP switch; + * this function is called only when indep-HP is modified + */ +static void switch_indep_hp_dacs(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - bool enable; - unsigned int verb, parm; + int shared = spec->hp_indep_shared; + hda_nid_t shared_dac, hp_dac; - if (spec->no_pin_power_ctl) - enable = false; - else - enable = is_aa_path_mute(codec) && !spec->gen.active_streams; - if (enable == spec->alc_mode && !force) + if (!spec->opened_streams) return; - spec->alc_mode = enable; - /* decide low current mode's verb & parameter */ - switch (spec->codec_type) { - case VT1708B_8CH: - case VT1708B_4CH: - verb = 0xf70; - parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ - break; - case VT1708S: - case VT1718S: - case VT1716S: - verb = 0xf73; - parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ - break; - case VT1702: - verb = 0xf73; - parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ - break; - case VT2002P: - case VT1812: - case VT1802: - verb = 0xf93; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - case VT1705CF: - case VT1808: - verb = 0xf82; - parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ - break; - default: - return; /* other codecs are not supported */ + shared_dac = shared ? spec->multiout.dac_nids[shared] : 0; + hp_dac = spec->hp_dac_nid; + if (spec->hp_independent_mode) { + /* switch to indep-HP mode */ + if (spec->active_streams & STREAM_MULTI_OUT) { + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + __snd_hda_codec_cleanup_stream(codec, shared_dac, 1); + } + if (spec->active_streams & STREAM_INDEP_HP) + snd_hda_codec_setup_stream(codec, hp_dac, + spec->cur_hp_stream_tag, 0, + spec->cur_hp_format); + } else { + /* back to HP or shared-DAC */ + if (spec->active_streams & STREAM_INDEP_HP) + __snd_hda_codec_cleanup_stream(codec, hp_dac, 1); + if (spec->active_streams & STREAM_MULTI_OUT) { + hda_nid_t dac; + int ch; + if (shared_dac) { /* reset mutli-ch DAC */ + dac = shared_dac; + ch = shared * 2; + } else { /* reset HP DAC */ + dac = hp_dac; + ch = 0; + } + snd_hda_codec_setup_stream(codec, dac, + spec->cur_dac_stream_tag, ch, + spec->cur_dac_format); + } } - /* send verb */ - snd_hda_codec_write(codec, codec->afg, 0, verb, parm); + setup_playback_multi_pcm(spec); +} + +static int via_independent_hp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int cur, shared; + + mutex_lock(&spec->config_mutex); + cur = !!ucontrol->value.enumerated.item[0]; + if (spec->hp_independent_mode == cur) { + mutex_unlock(&spec->config_mutex); + return 0; + } + spec->hp_independent_mode = cur; + shared = spec->hp_indep_shared; + deactivate_hp_paths(codec); + if (cur) + activate_output_path(codec, &spec->hp_indep_path, true, false); + else { + if (shared) + activate_output_path(codec, &spec->out_path[shared], + true, false); + if (spec->aamix_mode || !spec->hp_path.depth) + activate_output_path(codec, &spec->hp_mix_path, + true, false); + else + activate_output_path(codec, &spec->hp_path, + true, false); + } + + switch_indep_hp_dacs(codec); + mutex_unlock(&spec->config_mutex); + + /* update jack power state */ + set_widgets_power_state(codec); + via_hp_automute(codec); + return 1; +} + +static const struct snd_kcontrol_new via_hp_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = via_independent_hp_info, + .get = via_independent_hp_get, + .put = via_independent_hp_put, +}; + +static int via_hp_build(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + hda_nid_t nid; + + nid = spec->autocfg.hp_pins[0]; + knew = via_clone_control(spec, &via_hp_mixer); + if (knew == NULL) + return -ENOMEM; + + knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; + + return 0; +} + +static void notify_aa_path_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->smart51_nums; i++) { + struct snd_kcontrol *ctl; + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); + ctl = snd_hda_find_mixer_ctl(codec, id.name); + if (ctl) + snd_ctl_notify(codec->bus->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &ctl->id); + } +} + +static void mute_aa_path(struct hda_codec *codec, int mute) +{ + struct via_spec *spec = codec->spec; + int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; + int i; + + /* check AA path's mute status */ + for (i = 0; i < spec->smart51_nums; i++) { + if (spec->smart51_idxs[i] < 0) + continue; + snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, + HDA_INPUT, spec->smart51_idxs[i], + HDA_AMP_MUTE, val); + } +} + +static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->smart51_nums; i++) + if (spec->smart51_pins[i] == pin) + return true; + return false; +} + +static int via_smart51_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + *ucontrol->value.integer.value = spec->smart51_enabled; + return 0; +} + +static int via_smart51_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + int out_in = *ucontrol->value.integer.value + ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; + int i; + + for (i = 0; i < spec->smart51_nums; i++) { + hda_nid_t nid = spec->smart51_pins[i]; + unsigned int parm; + + parm = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); + parm |= out_in; + snd_hda_set_pin_ctl(codec, nid, parm); + if (out_in == AC_PINCTL_OUT_EN) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + } + spec->smart51_enabled = *ucontrol->value.integer.value; + set_widgets_power_state(codec); + return 1; +} + +static const struct snd_kcontrol_new via_smart51_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Smart 5.1", + .count = 1, + .info = snd_ctl_boolean_mono_info, + .get = via_smart51_get, + .put = via_smart51_put, +}; + +static int via_smart51_build(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->smart51_nums) + return 0; + if (!via_clone_control(spec, &via_smart51_mixer)) + return -ENOMEM; + return 0; +} + +/* check AA path's mute status */ +static bool is_aa_path_mute(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct hda_amp_list *p; + int i, ch, v; + + for (i = 0; i < spec->num_loopbacks; i++) { + p = &spec->loopback_list[i]; + for (ch = 0; ch < 2; ch++) { + v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, + p->idx); + if (!(v & HDA_AMP_MUTE) && v > 0) + return false; + } + } + return true; +} + +/* enter/exit analog low-current mode */ +static void __analog_low_current_mode(struct hda_codec *codec, bool force) +{ + struct via_spec *spec = codec->spec; + bool enable; + unsigned int verb, parm; + + if (spec->no_pin_power_ctl) + enable = false; + else + enable = is_aa_path_mute(codec) && !spec->opened_streams; + if (enable == spec->alc_mode && !force) + return; + spec->alc_mode = enable; + + /* decide low current mode's verb & parameter */ + switch (spec->codec_type) { + case VT1708B_8CH: + case VT1708B_4CH: + verb = 0xf70; + parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ + break; + case VT1708S: + case VT1718S: + case VT1716S: + verb = 0xf73; + parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ + break; + case VT1702: + verb = 0xf73; + parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ + break; + case VT2002P: + case VT1812: + case VT1802: + verb = 0xf93; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; + case VT1705CF: + case VT1808: + verb = 0xf82; + parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ + break; + default: + return; /* other codecs are not supported */ + } + /* send verb */ + snd_hda_codec_write(codec, codec->afg, 0, verb, parm); +} + +static void analog_low_current_mode(struct hda_codec *codec) +{ + return __analog_low_current_mode(codec, false); +} + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static const struct hda_verb vt1708_init_verbs[] = { + /* power down jack detect function */ + {0x1, 0xf81, 0x1}, + { } +}; + +static void set_stream_open(struct hda_codec *codec, int bit, bool active) +{ + struct via_spec *spec = codec->spec; + + if (active) + spec->opened_streams |= bit; + else + spec->opened_streams &= ~bit; + analog_low_current_mode(codec); +} + +static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int err; + + spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + set_stream_open(codec, STREAM_MULTI_OUT, true); + err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); + if (err < 0) { + set_stream_open(codec, STREAM_MULTI_OUT, false); + return err; + } + return 0; +} + +static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_open(codec, STREAM_MULTI_OUT, false); + return 0; +} + +static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + if (snd_BUG_ON(!spec->hp_dac_nid)) + return -EINVAL; + set_stream_open(codec, STREAM_INDEP_HP, true); + return 0; +} + +static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + set_stream_open(codec, STREAM_INDEP_HP, false); + return 0; +} + +static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + setup_playback_multi_pcm(spec); + snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + format, substream); + /* remember for dynamic DAC switch with indep-HP */ + spec->active_streams |= STREAM_MULTI_OUT; + spec->cur_dac_stream_tag = stream_tag; + spec->cur_dac_format = format; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, + stream_tag, 0, format); + spec->active_streams |= STREAM_INDEP_HP; + spec->cur_hp_stream_tag = stream_tag; + spec->cur_hp_format = format; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + spec->active_streams &= ~STREAM_MULTI_OUT; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + if (spec->hp_independent_mode) + snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); + spec->active_streams &= ~STREAM_INDEP_HP; + mutex_unlock(&spec->config_mutex); + vt1708_update_hp_work(spec); + return 0; +} + +/* + * Digital out + */ +static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); + return 0; +} + +/* + * Analog capture + */ +static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + return 0; +} + +static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); + return 0; +} + +/* analog capture with dynamic ADC switching */ +static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; + + mutex_lock(&spec->config_mutex); + spec->cur_adc = spec->adc_nids[adc_idx]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + mutex_unlock(&spec->config_mutex); + return 0; +} + +static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct via_spec *spec = codec->spec; + + mutex_lock(&spec->config_mutex); + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; + mutex_unlock(&spec->config_mutex); + return 0; +} + +/* re-setup the stream if running; called from input-src put */ +static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) +{ + struct via_spec *spec = codec->spec; + int adc_idx = spec->inputs[cur].adc_idx; + hda_nid_t adc = spec->adc_nids[adc_idx]; + bool ret = false; + + mutex_lock(&spec->config_mutex); + if (spec->cur_adc && spec->cur_adc != adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = adc; + snd_hda_codec_setup_stream(codec, adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + ret = true; + } + mutex_unlock(&spec->config_mutex); + return ret; +} + +static const struct hda_pcm_stream via_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_hp_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_playback_hp_pcm_open, + .close = via_playback_hp_pcm_close, + .prepare = via_playback_hp_pcm_prepare, + .cleanup = via_playback_hp_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + /* NID is set in via_build_pcms */ + /* We got noisy outputs on the right channel on VT1708 when + * 24bit samples are used. Until any workaround is found, + * disable the 24bit format, so far. + */ + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .ops = { + .open = via_playback_multi_pcm_open, + .close = via_playback_multi_pcm_close, + .prepare = via_playback_multi_pcm_prepare, + .cleanup = via_playback_multi_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_analog_capture = { + .substreams = 1, /* will be changed in via_build_pcms() */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .prepare = via_dyn_adc_capture_pcm_prepare, + .cleanup = via_dyn_adc_capture_pcm_cleanup, + }, +}; + +static const struct hda_pcm_stream via_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare, + .cleanup = via_dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream via_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +/* + * slave controls for virtual master + */ +static const char * const via_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Bass Speaker", + NULL, +}; + +static int via_build_controls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol *kctl; + int err, i; + + spec->no_pin_power_ctl = 1; + if (spec->set_widgets_power_state) + if (!via_clone_control(spec, &via_pin_power_ctl_enum)) + return -ENOMEM; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid); + if (err < 0) + return err; + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; + } + if (spec->dig_in_nid) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); + if (err < 0) + return err; + } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; + snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], + HDA_OUTPUT, vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, via_slave_pfxs, + "Playback Volume"); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, via_slave_pfxs, + "Playback Switch"); + if (err < 0) + return err; + } + + /* assign Capture Source enums to NID */ + kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); + for (i = 0; kctl && i < kctl->count; i++) { + if (!spec->mux_nids[i]) + continue; + err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); + if (err < 0) + return err; + } + + via_free_kctls(codec); /* no longer needed */ + + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + + return 0; +} + +static int via_build_pcms(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + + codec->num_pcms = 0; + codec->pcm_info = info; + + if (spec->multiout.num_dacs || spec->num_adc_nids) { + snprintf(spec->stream_name_analog, + sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); + info->name = spec->stream_name_analog; + + if (spec->multiout.num_dacs) { + if (!spec->stream_analog_playback) + spec->stream_analog_playback = + &via_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT + && spec->autocfg.line_outs == 2) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + } + + if (!spec->stream_analog_capture) { + if (spec->dyn_adc_switch) + spec->stream_analog_capture = + &via_pcm_dyn_adc_analog_capture; + else + spec->stream_analog_capture = + &via_pcm_analog_capture; + } + if (spec->num_adc_nids) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nids[0]; + if (!spec->dyn_adc_switch) + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids; + } + codec->num_pcms++; + info++; + } + + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); + info->name = spec->stream_name_digital; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + if (!spec->stream_digital_playback) + spec->stream_digital_playback = + &via_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + if (!spec->stream_digital_capture) + spec->stream_digital_capture = + &via_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->dig_in_nid; + } + codec->num_pcms++; + info++; + } + + if (spec->hp_dac_nid) { + snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), + "%s HP", codec->chip_name); + info->name = spec->stream_name_hp; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->hp_dac_nid; + codec->num_pcms++; + info++; + } + return 0; +} + +static void via_free(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec) + return; + + via_free_kctls(codec); + vt1708_stop_hp_work(spec); + kfree(spec->bind_cap_vol); + kfree(spec->bind_cap_sw); + kfree(spec); +} + +/* mute/unmute outputs */ +static void toggle_output_mutes(struct hda_codec *codec, int num_pins, + hda_nid_t *pins, bool mute) +{ + int i; + for (i = 0; i < num_pins; i++) { + unsigned int parm = snd_hda_codec_read(codec, pins[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (parm & AC_PINCTL_IN_EN) + continue; + if (mute) + parm &= ~AC_PINCTL_OUT_EN; + else + parm |= AC_PINCTL_OUT_EN; + snd_hda_set_pin_ctl(codec, pins[i], parm); + } +} + +/* mute internal speaker if line-out is plugged */ +static void via_line_automute(struct hda_codec *codec, int present) +{ + struct via_spec *spec = codec->spec; + + if (!spec->autocfg.speaker_outs) + return; + if (!present) + present = snd_hda_jack_detect(codec, + spec->autocfg.line_out_pins[0]); + toggle_output_mutes(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + present); +} + +/* mute internal speaker if HP is plugged */ +static void via_hp_automute(struct hda_codec *codec) +{ + int present = 0; + int nums; + struct via_spec *spec = codec->spec; + + if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] && + (spec->codec_type != VT1708 || spec->vt1708_jack_detect) && + is_jack_detectable(codec, spec->autocfg.hp_pins[0])) + present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); + + if (spec->smart51_enabled) + nums = spec->autocfg.line_outs + spec->smart51_nums; + else + nums = spec->autocfg.line_outs; + toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); + + via_line_automute(codec, present); +} + +#ifdef CONFIG_PM +static int via_suspend(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + vt1708_stop_hp_work(spec); + + if (spec->codec_type == VT1802) { + /* Fix pop noise on headphones */ + int i; + for (i = 0; i < spec->autocfg.hp_outs; i++) + snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0); + } + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + struct via_spec *spec = codec->spec; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); +} +#endif + +/* + */ + +static int via_init(struct hda_codec *codec); + +static const struct hda_codec_ops via_patch_ops = { + .build_controls = via_build_controls, + .build_pcms = via_build_pcms, + .init = via_init, + .free = via_free, + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .suspend = via_suspend, + .check_power_status = via_check_power_status, +#endif +}; + +static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->multiout.num_dacs; i++) { + if (spec->multiout.dac_nids[i] == dac) + return false; + } + if (spec->hp_dac_nid == dac) + return false; + return true; +} + +static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path, int depth) +{ + struct via_spec *spec = codec->spec; + hda_nid_t conn[8]; + int i, nums; + + if (nid == spec->aa_mix_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ + } + + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) + continue; + if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { + /* aa-mix is requested but not included? */ + if (!(spec->aa_mix_nid && with_aa_mix == 1)) + goto found; + } + } + if (depth >= MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT) + continue; + if (__parse_output_path(codec, conn[i], target_dac, + with_aa_mix, path, depth + 1)) + goto found; + } + return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + path->multi[path->depth] = 1; + path->depth++; + return true; +} + +static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path) +{ + if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { + path->path[path->depth] = nid; + path->depth++; + snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); + return true; + } + return false; +} + +static int via_auto_fill_dac_nids(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + hda_nid_t nid; + + spec->multiout.num_dacs = 0; + spec->multiout.dac_nids = spec->private_dac_nids; + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t dac = 0; + nid = cfg->line_out_pins[i]; + if (!nid) + continue; + if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i])) + dac = spec->out_path[i].path[0]; + if (!i && parse_output_path(codec, nid, dac, 1, + &spec->out_mix_path)) + dac = spec->out_mix_path.path[0]; + if (dac) + spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + } + if (!spec->out_path[0].depth && spec->out_mix_path.depth) { + spec->out_path[0] = spec->out_mix_path; + spec->out_mix_path.depth = 0; + } + return 0; +} + +static int create_ch_ctls(struct hda_codec *codec, const char *pfx, + int chs, bool check_dac, struct nid_path *path) +{ + struct via_spec *spec = codec->spec; + char name[32]; + hda_nid_t dac, pin, sel, nid; + int err; + + dac = check_dac ? path->path[0] : 0; + pin = path->path[path->depth - 1]; + sel = path->depth > 1 ? path->path[1] : 0; + + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = dac; + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) + nid = sel; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Volume", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + path->vol_ctl = nid; + } + + if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = dac; + else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = pin; + else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) + nid = sel; + else + nid = 0; + if (nid) { + sprintf(name, "%s Playback Switch", pfx); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); + if (err < 0) + return err; + path->mute_ctl = nid; + } + return 0; +} + +static void mangle_smart51(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg_item *ins = cfg->inputs; + int i, j, nums, attr; + int pins[AUTO_CFG_MAX_INS]; + + for (attr = INPUT_PIN_ATTR_LAST; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { + nums = 0; + for (i = 0; i < cfg->num_inputs; i++) { + unsigned int def; + if (ins[i].type > AUTO_PIN_LINE_IN) + continue; + def = snd_hda_codec_get_pincfg(codec, ins[i].pin); + if (snd_hda_get_input_pin_attr(def) != attr) + continue; + for (j = 0; j < nums; j++) + if (ins[pins[j]].type < ins[i].type) { + memmove(pins + j + 1, pins + j, + (nums - j) * sizeof(int)); + break; + } + pins[j] = i; + nums++; + } + if (cfg->line_outs + nums < 3) + continue; + for (i = 0; i < nums; i++) { + hda_nid_t pin = ins[pins[i]].pin; + spec->smart51_pins[spec->smart51_nums++] = pin; + cfg->line_out_pins[cfg->line_outs++] = pin; + if (cfg->line_outs == 3) + break; + } + return; + } +} + +static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src) +{ + dst->vol_ctl = src->vol_ctl; + dst->mute_ctl = src->mute_ctl; +} + +/* add playback controls from the parsed DAC table */ +static int via_auto_create_multi_out_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct nid_path *path; + static const char * const chname[4] = { + "Front", "Surround", NULL /* "CLFE" */, "Side" + }; + int i, idx, err; + int old_line_outs; + + /* check smart51 */ + old_line_outs = cfg->line_outs; + if (cfg->line_outs == 1) + mangle_smart51(codec); + + err = via_auto_fill_dac_nids(codec); + if (err < 0) + return err; + + if (spec->multiout.num_dacs < 3) { + spec->smart51_nums = 0; + cfg->line_outs = old_line_outs; + } + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t pin, dac; + pin = cfg->line_out_pins[i]; + dac = spec->multiout.dac_nids[i]; + if (!pin || !dac) + continue; + path = spec->out_path + i; + if (i == HDA_CLFE) { + err = create_ch_ctls(codec, "Center", 1, true, path); + if (err < 0) + return err; + err = create_ch_ctls(codec, "LFE", 2, true, path); + if (err < 0) + return err; + } else { + const char *pfx = chname[i]; + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->line_outs <= 2) + pfx = i ? "Bass Speaker" : "Speaker"; + err = create_ch_ctls(codec, pfx, 3, true, path); + if (err < 0) + return err; + } + if (path != spec->out_path + i) + copy_path_mixer_ctls(&spec->out_path[i], path); + if (path == spec->out_path && spec->out_mix_path.depth) + copy_path_mixer_ctls(&spec->out_mix_path, path); + } + + idx = get_connection_index(codec, spec->aa_mix_nid, + spec->multiout.dac_nids[0]); + if (idx >= 0) { + /* add control to mixer */ + const char *name; + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Volume" : "PCM Playback Volume"; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; + name = spec->out_mix_path.depth ? + "PCM Loopback Playback Switch" : "PCM Playback Switch"; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, + idx, HDA_INPUT)); + if (err < 0) + return err; + } + + cfg->line_outs = old_line_outs; + + return 0; +} + +static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + struct nid_path *path; + bool check_dac; + int i, err; + + if (!pin) + return 0; + + if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) { + for (i = HDA_SIDE; i >= HDA_CLFE; i--) { + if (i < spec->multiout.num_dacs && + parse_output_path(codec, pin, + spec->multiout.dac_nids[i], 0, + &spec->hp_indep_path)) { + spec->hp_indep_shared = i; + break; + } + } + } + if (spec->hp_indep_path.depth) { + spec->hp_dac_nid = spec->hp_indep_path.path[0]; + if (!spec->hp_indep_shared) + spec->hp_path = spec->hp_indep_path; + } + /* optionally check front-path w/o AA-mix */ + if (!spec->hp_path.depth) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->hp_path); + + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + 1, &spec->hp_mix_path) && !spec->hp_path.depth) + return 0; + + if (spec->hp_path.depth) { + path = &spec->hp_path; + check_dac = true; + } else { + path = &spec->hp_mix_path; + check_dac = false; + } + err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); + if (err < 0) + return err; + if (check_dac) + copy_path_mixer_ctls(&spec->hp_mix_path, path); + else + copy_path_mixer_ctls(&spec->hp_path, path); + if (spec->hp_indep_path.depth) + copy_path_mixer_ctls(&spec->hp_indep_path, path); + return 0; +} + +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 = 0; + int err; + + pin = spec->autocfg.speaker_pins[0]; + if (!spec->autocfg.speaker_outs || !pin) + return 0; + + if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path)) + dac = spec->speaker_path.path[0]; + if (!dac) + parse_output_path(codec, pin, + spec->multiout.dac_nids[HDA_FRONT], 0, + &spec->speaker_path); + if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], + 1, &spec->speaker_mix_path) && !dac) + return 0; + + /* no AA-path for front? */ + if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth) + dac = 0; + + spec->speaker_dac_nid = dac; + spec->multiout.extra_out_nid[0] = dac; + if (dac) { + path = &spec->speaker_path; + check_dac = true; + } else { + path = &spec->speaker_mix_path; + check_dac = false; + } + err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); + if (err < 0) + return err; + if (check_dac) + copy_path_mixer_ctls(&spec->speaker_mix_path, path); + else + copy_path_mixer_ctls(&spec->speaker_path, path); + return 0; +} + +#define via_aamix_ctl_info via_pin_power_ctl_info + +static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->aamix_mode; + return 0; +} + +static void update_aamix_paths(struct hda_codec *codec, int do_mix, + struct nid_path *nomix, struct nid_path *mix) +{ + if (do_mix) { + activate_output_path(codec, nomix, false, false); + activate_output_path(codec, mix, true, false); + } else { + activate_output_path(codec, mix, false, false); + activate_output_path(codec, nomix, true, false); + } +} + +static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val == spec->aamix_mode) + return 0; + spec->aamix_mode = val; + /* update front path */ + update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path); + /* update HP path */ + if (!spec->hp_independent_mode) { + update_aamix_paths(codec, val, &spec->hp_path, + &spec->hp_mix_path); + } + /* update speaker path */ + update_aamix_paths(codec, val, &spec->speaker_path, + &spec->speaker_mix_path); + return 1; +} + +static const struct snd_kcontrol_new via_aamix_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Loopback Mixing", + .info = via_aamix_ctl_info, + .get = via_aamix_ctl_get, + .put = via_aamix_ctl_put, +}; + +static int via_auto_create_loopback_switch(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + + if (!spec->aa_mix_nid) + return 0; /* no loopback switching available */ + if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth || + spec->speaker_path.depth)) + return 0; /* no loopback switching available */ + if (!via_clone_control(spec, &via_aamix_ctl_enum)) + return -ENOMEM; + return 0; +} + +/* look for ADCs */ +static int via_fill_adcs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid = codec->start_nid; + int i; + + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int wcaps = get_wcaps(codec, nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (wcaps & AC_WCAP_DIGITAL) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) + return -ENOMEM; + spec->adc_nids[spec->num_adc_nids++] = nid; + } + return 0; +} + +/* input-src control */ +static int via_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_inputs; + if (uinfo->value.enumerated.item >= spec->num_inputs) + uinfo->value.enumerated.item = spec->num_inputs - 1; + strcpy(uinfo->value.enumerated.name, + spec->inputs[uinfo->value.enumerated.item].label); + return 0; +} + +static int via_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; + return 0; +} + +static int via_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct via_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + hda_nid_t mux; + int cur; + + cur = ucontrol->value.enumerated.item[0]; + if (cur < 0 || cur >= spec->num_inputs) + return -EINVAL; + if (spec->cur_mux[idx] == cur) + return 0; + spec->cur_mux[idx] = cur; + if (spec->dyn_adc_switch) { + int adc_idx = spec->inputs[cur].adc_idx; + mux = spec->mux_nids[adc_idx]; + via_dyn_adc_pcm_resetup(codec, cur); + } else { + mux = spec->mux_nids[idx]; + if (snd_BUG_ON(!mux)) + return -EINVAL; + } + + if (mux) { + /* switch to D0 beofre change index */ + update_power_state(codec, mux, AC_PWRST_D0); + snd_hda_codec_write(codec, mux, 0, + AC_VERB_SET_CONNECT_SEL, + spec->inputs[cur].mux_idx); + } + + /* update jack power state */ + set_widgets_power_state(codec); + return 0; +} + +static const struct snd_kcontrol_new via_input_src_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, +}; + +static int create_input_src_ctls(struct hda_codec *codec, int count) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (spec->num_inputs <= 1 || !count) + return 0; /* no need for single src */ + + knew = via_clone_control(spec, &via_input_src_ctl); + if (!knew) + return -ENOMEM; + knew->count = count; + return 0; +} + +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) + return; + list = spec->loopback_list + spec->num_loopbacks; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->num_loopbacks++; + spec->loopback.amplist = spec->loopback_list; +} + +static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, + hda_nid_t dst) +{ + return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; +} + +/* add the input-route to the given pin */ +static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) +{ + struct via_spec *spec = codec->spec; + int c, idx; + + spec->inputs[spec->num_inputs].adc_idx = -1; + spec->inputs[spec->num_inputs].pin = pin; + for (c = 0; c < spec->num_adc_nids; c++) { + if (spec->mux_nids[c]) { + idx = get_connection_index(codec, spec->mux_nids[c], + pin); + if (idx < 0) + continue; + spec->inputs[spec->num_inputs].mux_idx = idx; + } else { + if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) + continue; + } + spec->inputs[spec->num_inputs].adc_idx = c; + /* Can primary ADC satisfy all inputs? */ + if (!spec->dyn_adc_switch && + spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { + snd_printd(KERN_INFO + "via: dynamic ADC switching enabled\n"); + spec->dyn_adc_switch = 1; + } + return true; + } + return false; } -static void analog_low_current_mode(struct hda_codec *codec) -{ - return __analog_low_current_mode(codec, false); -} +static int get_mux_nids(struct hda_codec *codec); -static int via_build_controls(struct hda_codec *codec) +/* parse input-routes; fill ADCs, MUXs and input-src entries */ +static int parse_analog_inputs(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - int err, i; + const struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; - err = snd_hda_gen_build_controls(codec); + err = via_fill_adcs(codec); + if (err < 0) + return err; + err = get_mux_nids(codec); if (err < 0) return err; - if (spec->set_widgets_power_state) - 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; + /* fill all input-routes */ + for (i = 0; i < cfg->num_inputs; i++) { + if (add_input_route(codec, cfg->inputs[i].pin)) + spec->inputs[spec->num_inputs++].label = + hda_get_autocfg_input_label(codec, cfg, i); } + /* check for internal loopback recording */ + if (spec->aa_mix_nid && + add_input_route(codec, spec->aa_mix_nid)) + spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; + return 0; } -static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream, - int action) +/* create analog-loopback volume/switch controls */ +static int create_loopback_ctls(struct hda_codec *codec) { - analog_low_current_mode(codec); - vt1708_update_hp_work(codec); + struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + const char *prev_label = NULL; + int type_idx = 0; + int i, j, err, idx; + + if (!spec->aa_mix_nid) + return 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + const char *label = hda_get_autocfg_input_label(codec, cfg, i); + + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + idx = get_connection_index(codec, spec->aa_mix_nid, pin); + if (idx >= 0) { + err = via_new_analog_input(spec, label, type_idx, + idx, spec->aa_mix_nid); + if (err < 0) + return err; + add_loopback_list(spec, spec->aa_mix_nid, idx); + } + + /* remember the label for smart51 control */ + for (j = 0; j < spec->smart51_nums; j++) { + if (spec->smart51_pins[j] == pin) { + spec->smart51_idxs[j] = idx; + spec->smart51_labels[j] = label; + break; + } + } + } + return 0; } -static void via_free(struct hda_codec *codec) +/* create mic-boost controls (if present) */ +static int create_mic_boost_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + const char *prev_label = NULL; + int type_idx = 0; + int i, err; - if (!spec) - return; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin = cfg->inputs[i].pin; + unsigned int caps; + const char *label; + char name[32]; - vt1708_stop_hp_work(codec); - snd_hda_gen_spec_free(&spec->gen); - kfree(spec); + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + caps = query_amp_caps(codec, pin, HDA_INPUT); + if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) + continue; + label = hda_get_autocfg_input_label(codec, cfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + snprintf(name, sizeof(name), "%s Boost Volume", label); + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + return 0; } -#ifdef CONFIG_PM -static int via_suspend(struct hda_codec *codec) +/* create capture and input-src controls for multiple streams */ +static int create_multi_adc_ctls(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - vt1708_stop_hp_work(codec); + int i, err; - if (spec->codec_type == VT1802) { - /* Fix pop noise on headphones */ - int i; - for (i = 0; i < spec->gen.autocfg.hp_outs; i++) - snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0); + /* create capture mixer elements */ + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t adc = spec->adc_nids[i]; + err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Capture Volume", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Capture Switch", i, + HDA_COMPOSE_AMP_VAL(adc, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; } + /* input-source control */ + for (i = 0; i < spec->num_adc_nids; i++) + if (!spec->mux_nids[i]) + break; + err = create_input_src_ctls(codec, i); + if (err < 0) + return err; return 0; } -#endif -#ifdef CONFIG_PM -static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) +/* bind capture volume/switch */ +static struct snd_kcontrol_new via_bind_cap_vol_ctl = + HDA_BIND_VOL("Capture Volume", 0); +static struct snd_kcontrol_new via_bind_cap_sw_ctl = + HDA_BIND_SW("Capture Switch", 0); + +static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, + struct hda_ctl_ops *ops) { - struct via_spec *spec = codec->spec; - set_widgets_power_state(codec); - analog_low_current_mode(codec); - vt1708_update_hp_work(codec); - return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); + struct hda_bind_ctls *ctl; + int i; + + ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); + if (!ctl) + return -ENOMEM; + ctl->ops = ops; + for (i = 0; i < spec->num_adc_nids; i++) + ctl->values[i] = + HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); + *ctl_ret = ctl; + return 0; } -#endif -/* - */ +/* create capture and input-src controls for dynamic ADC-switch case */ +static int create_dyn_adc_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + int err; -static int via_init(struct hda_codec *codec); + /* set up the bind capture ctls */ + err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); + if (err < 0) + return err; + err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); + if (err < 0) + return err; -static const struct hda_codec_ops via_patch_ops = { - .build_controls = via_build_controls, - .build_pcms = snd_hda_gen_build_pcms, - .init = via_init, - .free = via_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .suspend = via_suspend, - .check_power_status = via_check_power_status, -#endif -}; + /* create capture mixer elements */ + knew = via_clone_control(spec, &via_bind_cap_vol_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_vol; + knew = via_clone_control(spec, &via_bind_cap_sw_ctl); + if (!knew) + return -ENOMEM; + knew->private_value = (long)spec->bind_cap_sw; + + /* input-source control */ + err = create_input_src_ctls(codec, 1); + if (err < 0) + return err; + return 0; +} + +/* parse and create capture-related stuff */ +static int via_auto_create_analog_input_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = parse_analog_inputs(codec); + if (err < 0) + return err; + if (spec->dyn_adc_switch) + err = create_dyn_adc_ctls(codec); + else + err = create_multi_adc_ctls(codec); + if (err < 0) + return err; + err = create_loopback_ctls(codec); + if (err < 0) + return err; + err = create_mic_boost_ctls(codec); + if (err < 0) + return err; + return 0; +} -static const struct hda_verb vt1708_init_verbs[] = { - /* power down jack detect function */ - {0x1, 0xf81, 0x1}, - { } -}; static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) { unsigned int def_conf; @@ -560,32 +2629,102 @@ static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, if (spec->vt1708_jack_detect == val) return 0; spec->vt1708_jack_detect = val; - vt1708_update_hp_work(codec); + if (spec->vt1708_jack_detect && + snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) { + mute_aa_path(codec, 1); + notify_aa_path_ctls(codec); + } + via_hp_automute(codec); + vt1708_update_hp_work(spec); 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 void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void fill_dig_outs(struct hda_codec *codec); +static void fill_dig_in(struct hda_codec *codec); + +static int via_parse_auto_config(struct hda_codec *codec) { - set_widgets_power_state(codec); - snd_hda_gen_hp_automute(codec, tbl); + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return -EINVAL; + + err = via_auto_create_multi_out_ctls(codec); + if (err < 0) + return err; + err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = via_auto_create_speaker_ctls(codec); + if (err < 0) + return err; + err = via_auto_create_loopback_switch(codec); + if (err < 0) + return err; + err = via_auto_create_analog_input_ctls(codec); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + fill_dig_outs(codec); + fill_dig_in(codec); + + if (spec->kctls.list) + spec->mixers[spec->num_mixers++] = spec->kctls.list; + + + if (spec->hp_dac_nid && spec->hp_mix_path.depth) { + err = via_hp_build(codec); + if (err < 0) + return err; + } + + err = via_smart51_build(codec); + if (err < 0) + return err; + + /* assign slave outs */ + if (spec->slave_dig_outs[0]) + codec->slave_dig_outs = spec->slave_dig_outs; + + return 1; +} + +static void via_auto_init_dig_outs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + if (spec->multiout.dig_out_nid) + init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); + if (spec->slave_dig_outs[0]) + init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); +} + +static void via_auto_init_dig_in(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + if (!spec->dig_in_nid) + return; + snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN); } -static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) +static void via_jack_output_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) { set_widgets_power_state(codec); - snd_hda_gen_line_automute(codec, tbl); + via_hp_automute(codec); } static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) @@ -593,55 +2732,41 @@ static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_t set_widgets_power_state(codec); } -#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1) - -static void via_set_jack_unsol_events(struct hda_codec *codec) +/* initialize the unsolicited events */ +static void via_auto_init_unsol_event(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - hda_nid_t pin; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int ev; int i; + hda_jack_callback cb; + + if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) + snd_hda_jack_detect_enable_callback(codec, cfg->hp_pins[0], + VIA_HP_EVENT | VIA_JACK_EVENT, + via_jack_output_event); - spec->gen.hp_automute_hook = via_hp_automute; if (cfg->speaker_pins[0]) - spec->gen.line_automute_hook = via_line_automute; + ev = VIA_LINE_EVENT; + else + ev = 0; + cb = ev ? via_jack_output_event : via_jack_powerstate_event; for (i = 0; i < cfg->line_outs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && !snd_hda_jack_tbl_get(codec, pin) && - is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, - VIA_JACK_EVENT, - via_jack_powerstate_event); + if (cfg->line_out_pins[i] && + is_jack_detectable(codec, cfg->line_out_pins[i])) + snd_hda_jack_detect_enable_callback(codec, cfg->line_out_pins[i], + ev | VIA_JACK_EVENT, cb); } for (i = 0; i < cfg->num_inputs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && !snd_hda_jack_tbl_get(codec, pin) && - is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, + if (is_jack_detectable(codec, cfg->inputs[i].pin)) + snd_hda_jack_detect_enable_callback(codec, cfg->inputs[i].pin, VIA_JACK_EVENT, via_jack_powerstate_event); } } -static int via_parse_auto_config(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int err; - - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); - if (err < 0) - return err; - - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); - if (err < 0) - return err; - - via_set_jack_unsol_events(codec); - return 0; -} - static int via_init(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -654,35 +2779,63 @@ static int via_init(struct hda_codec *codec) set_widgets_power_state(codec); __analog_low_current_mode(codec, true); - snd_hda_gen_init(codec); + via_auto_init_multi_out(codec); + via_auto_init_hp_out(codec); + via_auto_init_speaker_out(codec); + via_auto_init_analog_input(codec); + via_auto_init_dig_outs(codec); + via_auto_init_dig_in(codec); + + via_auto_init_unsol_event(codec); - vt1708_update_hp_work(codec); + via_hp_automute(codec); + vt1708_update_hp_work(spec); return 0; } -static int vt1708_build_pcms(struct hda_codec *codec) +static void vt1708_update_hp_jack_state(struct work_struct *work) { - struct via_spec *spec = codec->spec; - int i, err; - - err = snd_hda_gen_build_pcms(codec); - if (err < 0 || codec->vendor_id != 0x11061708) - return err; - - /* We got noisy outputs on the right channel on VT1708 when - * 24bit samples are used. Until any workaround is found, - * disable the 24bit format, so far. - */ - for (i = 0; i < codec->num_pcms; i++) { - struct hda_pcm *info = &spec->gen.pcm_rec[i]; - if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || - info->pcm_type != HDA_PCM_TYPE_AUDIO) - continue; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = - SNDRV_PCM_FMTBIT_S16_LE; + struct via_spec *spec = container_of(work, struct via_spec, + vt1708_hp_work.work); + if (spec->codec_type != VT1708) + return; + snd_hda_jack_set_dirty_all(spec->codec); + /* if jack state toggled */ + if (spec->vt1708_hp_present + != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { + spec->vt1708_hp_present ^= 1; + via_hp_automute(spec->codec); } + if (spec->vt1708_jack_detect) + schedule_delayed_work(&spec->vt1708_hp_work, + msecs_to_jiffies(100)); +} +static int get_mux_nids(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid, conn[8]; + unsigned int type; + int i, n; + + for (i = 0; i < spec->num_adc_nids; i++) { + nid = spec->adc_nids[i]; + while (nid) { + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type == AC_WID_PIN) + break; + n = snd_hda_get_connections(codec, nid, conn, + ARRAY_SIZE(conn)); + if (n <= 0) + break; + if (n > 1) { + spec->mux_nids[i] = nid; + break; + } + nid = conn[0]; + } + } return 0; } @@ -696,15 +2849,7 @@ static int patch_vt1708(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x17; - - /* set jackpoll_interval while parsing the codec */ - codec->jackpoll_interval = msecs_to_jiffies(100); - spec->vt1708_jack_detect = 1; - - /* don't support the input jack switching due to lack of unsol event */ - /* (it may work with polling, though, but it needs testing) */ - spec->gen.suppress_auto_mic = 1; + spec->aa_mix_nid = 0x17; /* Add HP and CD pin config connect bit re-config action */ vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); @@ -718,16 +2863,18 @@ static int patch_vt1708(struct hda_codec *codec) } /* add jack detect on/off control */ - spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; + if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) + return -ENOMEM; + + /* disable 32bit format on VT1708 */ + if (codec->vendor_id == 0x11061708) + spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; codec->patch_ops = via_patch_ops; - codec->patch_ops.build_pcms = vt1708_build_pcms; - - /* clear jackpoll_interval again; it's set dynamically */ - codec->jackpoll_interval = 0; + INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); return 0; } @@ -741,7 +2888,7 @@ static int patch_vt1709(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x18; + spec->aa_mix_nid = 0x18; err = via_parse_auto_config(codec); if (err < 0) { @@ -785,7 +2932,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) /* PW0 (19h), SW1 (18h), AOW1 (11h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x19, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); update_power_state(codec, 0x18, parm); update_power_state(codec, 0x11, parm); @@ -794,7 +2941,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (is_8ch) { parm = AC_PWRST_D3; set_pin_power_state(codec, 0x22, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x26, parm); update_power_state(codec, 0x24, parm); @@ -802,7 +2949,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) /* PW7(23h), SW2(27h), AOW2(25h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x27, parm); update_power_state(codec, 0x25, parm); @@ -822,7 +2969,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec) if (is_8ch) { update_power_state(codec, 0x25, parm); update_power_state(codec, 0x27, parm); - } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) + } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) update_power_state(codec, 0x25, parm); } @@ -840,7 +2987,7 @@ static int patch_vt1708B(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x16; + spec->aa_mix_nid = 0x16; /* automatic parse from the BIOS config */ err = via_parse_auto_config(codec); @@ -865,6 +3012,58 @@ static const struct hda_verb vt1708S_init_verbs[] = { { } }; +/* fill out digital output widgets; one for master and one for slave outputs */ +static void fill_dig_outs(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t nid; + int conn; + + nid = spec->autocfg.dig_out_pins[i]; + if (!nid) + continue; + conn = snd_hda_get_connections(codec, nid, &nid, 1); + if (conn < 1) + continue; + if (!spec->multiout.dig_out_nid) + spec->multiout.dig_out_nid = nid; + else { + spec->slave_dig_outs[0] = nid; + break; /* at most two dig outs */ + } + } +} + +static void fill_dig_in(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t dig_nid; + int i, err; + + if (!spec->autocfg.dig_in_pin) + return; + + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + if (!(wcaps & AC_WCAP_CONN_LIST)) + continue; + err = get_connection_index(codec, dig_nid, + spec->autocfg.dig_in_pin); + if (err >= 0) { + spec->dig_in_nid = dig_nid; + break; + } + } +} + static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, int offset, int num_steps, int step_size) { @@ -885,10 +3084,21 @@ static int patch_vt1708S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x16; + spec->aa_mix_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); + /* 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; + + codec->patch_ops = via_patch_ops; + /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { kfree(codec->chip_name); @@ -905,18 +3115,6 @@ static int patch_vt1708S(struct hda_codec *codec) sizeof(codec->bus->card->mixername), "%s %s", codec->vendor_name, codec->chip_name); } - - /* 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; - - codec->patch_ops = via_patch_ops; - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; } @@ -971,7 +3169,7 @@ static int patch_vt1702(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x1a; + spec->aa_mix_nid = 0x1a; /* limit AA path volume to 0 dB */ snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, @@ -1038,17 +3236,17 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* PW2 (26h), AOW2 (ah) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x2b, &parm); update_power_state(codec, 0xa, parm); /* PW0 (24h), AOW0 (8h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); - if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ + if (!spec->hp_independent_mode) /* check for redirected HP */ set_pin_power_state(codec, 0x28, &parm); update_power_state(codec, 0x8, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) + if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3) parm = parm2; update_power_state(codec, 0xb, parm); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ @@ -1057,11 +3255,11 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec) /* PW1 (25h), AOW1 (9h) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x2a, &parm); update_power_state(codec, 0x9, parm); - if (spec->gen.indep_hp_enabled) { + if (spec->hp_independent_mode) { /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ parm = AC_PWRST_D3; set_pin_power_state(codec, 0x28, &parm); @@ -1081,9 +3279,9 @@ static int add_secret_dac_path(struct hda_codec *codec) hda_nid_t conn[8]; hda_nid_t nid; - if (!spec->gen.mixer_nid) + if (!spec->aa_mix_nid) return 0; - nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, + nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, ARRAY_SIZE(conn) - 1); for (i = 0; i < nums; i++) { if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) @@ -1098,7 +3296,7 @@ static int add_secret_dac_path(struct hda_codec *codec) !(caps & AC_WCAP_DIGITAL)) { conn[nums++] = nid; return snd_hda_override_conn_list(codec, - spec->gen.mixer_nid, + spec->aa_mix_nid, nums, conn); } } @@ -1116,7 +3314,7 @@ static int patch_vt1718S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x21; + spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); @@ -1247,7 +3445,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x19, &parm); /* Smart 5.1 PW2(1bh) */ - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1b, &parm); update_power_state(codec, 0x18, parm); update_power_state(codec, 0x11, parm); @@ -1256,12 +3454,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x23, &parm); /* Smart 5.1 PW1(1ah) */ - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1a, &parm); update_power_state(codec, 0x27, parm); /* Smart 5.1 PW5(1eh) */ - if (smart51_enabled(codec)) + if (spec->smart51_enabled) set_pin_power_state(codec, 0x1e, &parm); update_power_state(codec, 0x25, parm); @@ -1273,7 +3471,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) mono_out = 0; else { present = snd_hda_jack_detect(codec, 0x1d); - if (!spec->gen.indep_hp_enabled && present) + if (!spec->hp_independent_mode && present) mono_out = 0; else mono_out = 1; @@ -1288,7 +3486,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec) set_pin_power_state(codec, 0x1c, &parm); set_pin_power_state(codec, 0x1d, &parm); /* HP Independent Mode, power on AOW3 */ - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_power_state(codec, 0x25, parm); /* force to D0 for internal Speaker */ @@ -1307,7 +3505,7 @@ static int patch_vt1716S(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x16; + spec->aa_mix_nid = 0x16; override_mic_boost(codec, 0x1a, 0, 3, 40); override_mic_boost(codec, 0x1e, 0, 3, 40); @@ -1320,7 +3518,9 @@ static int patch_vt1716S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; - spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; + spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; + spec->num_mixers++; + spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; codec->patch_ops = via_patch_ops; @@ -1405,7 +3605,7 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec) update_power_state(codec, 0x35, parm); } - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_power_state(codec, 0x9, AC_PWRST_D0); /* Class-D */ @@ -1503,7 +3703,7 @@ static int patch_vt2002P(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x21; + spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); if (spec->codec_type == VT1802) @@ -1574,7 +3774,7 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec) set_pin_power_state(codec, 0x25, &parm); update_power_state(codec, 0x15, parm); update_power_state(codec, 0x35, parm); - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_power_state(codec, 0x9, AC_PWRST_D0); /* Internal Speaker */ @@ -1627,7 +3827,7 @@ static int patch_vt1812(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x21; + spec->aa_mix_nid = 0x21; override_mic_boost(codec, 0x2b, 0, 3, 40); override_mic_boost(codec, 0x29, 0, 3, 40); add_secret_dac_path(codec); @@ -1697,7 +3897,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x26, &parm); update_power_state(codec, 0x36, parm); - if (smart51_enabled(codec)) { + if (spec->smart51_enabled) { /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ set_pin_power_state(codec, 0x2b, &parm); update_power_state(codec, 0x3b, parm); @@ -1709,7 +3909,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x25, &parm); update_power_state(codec, 0x35, parm); - if (smart51_enabled(codec)) { + if (spec->smart51_enabled) { /* PW6(2ah), MW6(3ah), MUX6(1ah) */ set_pin_power_state(codec, 0x2a, &parm); update_power_state(codec, 0x3a, parm); @@ -1722,7 +3922,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) set_pin_power_state(codec, 0x28, &parm); update_power_state(codec, 0x38, parm); update_power_state(codec, 0x18, parm); - if (spec->gen.indep_hp_enabled) + if (spec->hp_independent_mode) update_conv_power_state(codec, 0xb, parm, 3); parm2 = parm; /* for pin 0x0b */ @@ -1730,7 +3930,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec) parm = AC_PWRST_D3; set_pin_power_state(codec, 0x24, &parm); update_power_state(codec, 0x34, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) + if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3) parm = parm2; update_conv_power_state(codec, 0x8, parm, 0); /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ @@ -1747,7 +3947,7 @@ static int patch_vt3476(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; - spec->gen.mixer_nid = 0x3f; + spec->aa_mix_nid = 0x3f; add_secret_dac_path(codec); /* automatic parse from the BIOS config */