[alsa-devel] [PATCH 00/38] Realtek HD-audio auto-parser
Hi,
for 3.4 kernel, finally I got rid of all static quirks from HD-audio Realtek codec parser. This means that the all necessary quirks have been converted to alc_fixup either the pin-config overrides, the additional verbs, or the extra init functions.
Since some machines have really weird pin mappings, I introduced the new mechanism in the parser to evaluate the best DAC->pin connections, which is based on the badness calculation. You won't notice on most of machines but some machines that have fewer DACs than necessary output pins will get different mappings by this change.
The whole changes can be seen in sound git tree topic/hda branch, as usual. git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
Now it's merged to main branch, so the snapshot tarball contains these changes, too.
The rest of the thread will contain the changes in patch_realtek.c. It's just for refernces, and I omitted the other files. For the complete changes, take a look at the git tree.
thanks,
Takashi
In most cases, the slave strings for vmaster are identical between volumes and switches except for "xxx Volume" and "xxx Switch" suffix. Now snd_hda_add_vmaster() takes the optional suffix argument so that each string can be composed with the given suffix, and we can share the slave name strings in both volume and switch calls nicely.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 40 ++++++++-------------------------------- 1 files changed, 8 insertions(+), 32 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 33b6077..42f1844 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1845,36 +1845,10 @@ DEFINE_CAPMIX_NOSRC(3); /* * slave controls for virtual master */ -static const char * const alc_slave_vols[] = { - "Front Playback Volume", - "Surround Playback Volume", - "Center Playback Volume", - "LFE Playback Volume", - "Side Playback Volume", - "Headphone Playback Volume", - "Speaker Playback Volume", - "Mono Playback Volume", - "Line-Out Playback Volume", - "CLFE Playback Volume", - "Bass Speaker Playback Volume", - "PCM Playback Volume", - NULL, -}; - -static const char * const alc_slave_sws[] = { - "Front Playback Switch", - "Surround Playback Switch", - "Center Playback Switch", - "LFE Playback Switch", - "Side Playback Switch", - "Headphone Playback Switch", - "Speaker Playback Switch", - "Mono Playback Switch", - "IEC958 Playback Switch", - "Line-Out Playback Switch", - "CLFE Playback Switch", - "Bass Speaker Playback Switch", - "PCM Playback Switch", +static const char * const alc_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Mono", "Line-Out", + "CLFE", "Bass Speaker", "PCM", NULL, };
@@ -1965,14 +1939,16 @@ static int __alc_build_controls(struct hda_codec *codec) snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, vmaster_tlv); err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, alc_slave_vols); + vmaster_tlv, alc_slave_pfxs, + "Playback Volume"); if (err < 0) return err; } if (!spec->no_analog && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, alc_slave_sws); + NULL, alc_slave_pfxs, + "Playback Switch"); if (err < 0) return err; }
A few machines with ALC861 & co are reported not to work properly with the auto-mute feature in software. The auto-mute feature is implemented in the hardware level, and the jack-detection never works with them.
Also, rename the fixup index as ALC861_FIXUP_* to follow the standard.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 38 ++++++++++++++++++++++++++++++-------- 1 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4a2a49f..c630598 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5563,8 +5563,10 @@ static const struct hda_amp_list alc861_loopbacks[] = {
/* Pin config fixes */ enum { - PINFIX_FSC_AMILO_PI1505, - PINFIX_ASUS_A6RP, + ALC861_FIXUP_FSC_AMILO_PI1505, + ALC861_FIXUP_AMP_VREF_0F, + ALC861_FIXUP_NO_JACK_DETECT, + ALC861_FIXUP_ASUS_A6RP, };
/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ @@ -5586,8 +5588,16 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, spec->keep_vref_in_automute = 1; }
+/* suppress the jack-detection */ +static void alc_fixup_no_jack_detect(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + if (action == ALC_FIXUP_ACT_PRE_PROBE) + codec->no_jack_detect = 1; +} + static const struct alc_fixup alc861_fixups[] = { - [PINFIX_FSC_AMILO_PI1505] = { + [ALC861_FIXUP_FSC_AMILO_PI1505] = { .type = ALC_FIXUP_PINS, .v.pins = (const struct alc_pincfg[]) { { 0x0b, 0x0221101f }, /* HP */ @@ -5595,17 +5605,29 @@ static const struct alc_fixup alc861_fixups[] = { { } } }, - [PINFIX_ASUS_A6RP] = { + [ALC861_FIXUP_AMP_VREF_0F] = { .type = ALC_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, }, + [ALC861_FIXUP_NO_JACK_DETECT] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, + [ALC861_FIXUP_ASUS_A6RP] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc861_fixup_asus_amp_vref_0f, + .chained = true, + .chain_id = ALC861_FIXUP_NO_JACK_DETECT, + } };
static const struct snd_pci_quirk alc861_fixup_tbl[] = { - SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP), - SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505), + SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT), + SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1584, 0x0000, "Uniwill ECS M31EI", ALC861_FIXUP_AMP_VREF_0F), + SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505), {} };
Add the jack-detect suppression for an ASUS machine with ALC892 codec.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=42655
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c630598..30ef877 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5875,6 +5875,7 @@ enum { ALC662_FIXUP_ASUS_MODE6, ALC662_FIXUP_ASUS_MODE7, ALC662_FIXUP_ASUS_MODE8, + ALC662_FIXUP_NO_JACK_DETECT, };
static const struct alc_fixup alc662_fixups[] = { @@ -6020,6 +6021,10 @@ static const struct alc_fixup alc662_fixups[] = { .chained = true, .chain_id = ALC662_FIXUP_SKU_IGNORE }, + [ALC662_FIXUP_NO_JACK_DETECT] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc_fixup_no_jack_detect, + }, };
static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -6028,6 +6033,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), + SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
The model=will for ALC260 requires the pin 0x0f to be a headphone and some special verbs for the COEF to turn on the amp. Now added these as fixup entries and removed the static model quirk.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 27 ++++++++++++++++++++++++--- 1 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 30ef877..f5f3710 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4207,21 +4207,42 @@ static const struct hda_amp_list alc260_loopbacks[] = { * Pin config fixes */ enum { - PINFIX_HP_DC5750, + ALC260_FIXUP_HP_DC5750, + ALC260_FIXUP_HP_PIN_0F, + ALC260_FIXUP_COEF, };
static const struct alc_fixup alc260_fixups[] = { - [PINFIX_HP_DC5750] = { + [ALC260_FIXUP_HP_DC5750] = { .type = ALC_FIXUP_PINS, .v.pins = (const struct alc_pincfg[]) { { 0x11, 0x90130110 }, /* speaker */ { } } }, + [ALC260_FIXUP_HP_PIN_0F] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x0f, 0x01214000 }, /* HP */ + { } + } + }, + [ALC260_FIXUP_COEF] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3040 }, + { } + }, + .chained = true, + .chain_id = ALC260_FIXUP_HP_PIN_0F, + }, };
static const struct snd_pci_quirk alc260_fixup_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750), + SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), + SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), + SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), {} };
The ALC260 model=acer needs GPIO1 setup. It could be selected well if the codec SSID is set properly by BIOS, but to make sure, enable it forcibly.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f5f3710..95ef722 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4210,6 +4210,7 @@ enum { ALC260_FIXUP_HP_DC5750, ALC260_FIXUP_HP_PIN_0F, ALC260_FIXUP_COEF, + ALC260_FIXUP_GPIO1, };
static const struct alc_fixup alc260_fixups[] = { @@ -4237,10 +4238,16 @@ static const struct alc_fixup alc260_fixups[] = { .chained = true, .chain_id = ALC260_FIXUP_HP_PIN_0F, }, + [ALC260_FIXUP_GPIO1] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio1_init_verbs, + }, };
static const struct snd_pci_quirk alc260_fixup_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), + SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), {}
The support for Replacer 627V in the auto-parser needs the unique unsol event handling: although the machine has a single output pin 0x0f, it's used for both the headphone and the speaker, and the driver needs to toggle the output route via GPIO 1.
In addition, it needs a special COEF setup with 0x3050.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 44 +++++++++++++++++++++++++++++++++++++++++ 1 files changed, 44 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 95ef722..cfa6ad7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4211,8 +4211,35 @@ enum { ALC260_FIXUP_HP_PIN_0F, ALC260_FIXUP_COEF, ALC260_FIXUP_GPIO1, + ALC260_FIXUP_GPIO1_TOGGLE, + ALC260_FIXUP_REPLACER, };
+static void alc260_gpio1_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + spec->hp_jack_present); +} + +static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action == ALC_FIXUP_ACT_PROBE) { + /* although the machine has only one output pin, we need to + * toggle GPIO1 according to the jack state + */ + spec->automute_hook = alc260_gpio1_automute; + spec->detect_hp = 1; + spec->automute_speaker = 1; + spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ + snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT); + spec->unsol_event = alc_sku_unsol_event; + add_verb(codec->spec, alc_gpio1_init_verbs); + } +} + static const struct alc_fixup alc260_fixups[] = { [ALC260_FIXUP_HP_DC5750] = { .type = ALC_FIXUP_PINS, @@ -4242,6 +4269,22 @@ static const struct alc_fixup alc260_fixups[] = { .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, + [ALC260_FIXUP_GPIO1_TOGGLE] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_HP_PIN_0F, + }, + [ALC260_FIXUP_REPLACER] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, + { } + }, + .chained = true, + .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, + }, };
static const struct snd_pci_quirk alc260_fixup_tbl[] = { @@ -4249,6 +4292,7 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), + SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), {} };
HP Presario B1900 needs a similar hack like Replacer, toggling GPIO1 per the jack state, in addition to the COEF setup used for other Acer laptops.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cfa6ad7..db1d8c8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4213,6 +4213,7 @@ enum { ALC260_FIXUP_GPIO1, ALC260_FIXUP_GPIO1_TOGGLE, ALC260_FIXUP_REPLACER, + ALC260_FIXUP_HP_B1900, };
static void alc260_gpio1_automute(struct hda_codec *codec) @@ -4285,6 +4286,12 @@ static const struct alc_fixup alc260_fixups[] = { .chained = true, .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, }, + [ALC260_FIXUP_HP_B1900] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc260_fixup_gpio1_toggle, + .chained = true, + .chain_id = ALC260_FIXUP_COEF, + } };
static const struct snd_pci_quirk alc260_fixup_tbl[] = { @@ -4292,6 +4299,7 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF), SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), + SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), {}
It's working with the auto-parser just with the standard GPIO 1 setup.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index db1d8c8..0d81eeb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4300,6 +4300,7 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), + SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), {}
Now we can clean up all static quirks for ALC260. Also many codes in alc_quirks.c can be ripped off since they have been used only by ALC260 static quirks.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 48 ++++++---------------------------------- 1 files changed, 8 insertions(+), 40 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0d81eeb..3ea4206 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4308,14 +4308,10 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = {
/* */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#include "alc260_quirks.c" -#endif - static int patch_alc260(struct hda_codec *codec) { struct alc_spec *spec; - int err, board_config; + int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -4325,38 +4321,13 @@ static int patch_alc260(struct hda_codec *codec)
spec->mixer_nid = 0x07;
- board_config = alc_board_config(codec, ALC260_MODEL_LAST, - alc260_models, alc260_cfg_tbl); - if (board_config < 0) { - snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC_MODEL_AUTO; - } - - if (board_config == ALC_MODEL_AUTO) { - alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } - - if (board_config == ALC_MODEL_AUTO) { - /* automatic parse from the BIOS config */ - err = alc260_parse_auto_config(codec); - if (err < 0) - goto error; -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS - else if (!err) { - printk(KERN_INFO - "hda_codec: Cannot set up configuration " - "from BIOS. Using base mode...\n"); - board_config = ALC260_BASIC; - } -#endif - } + alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- if (board_config != ALC_MODEL_AUTO) { - setup_preset(codec, &alc260_presets[board_config]); - spec->vmaster_nid = 0x08; - } + /* automatic parse from the BIOS config */ + err = alc260_parse_auto_config(codec); + if (err < 0) + goto error;
if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); @@ -4377,10 +4348,7 @@ static int patch_alc260(struct hda_codec *codec) alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
codec->patch_ops = alc_patch_ops; - if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc_auto_init_std; - else - codec->patch_ops.build_controls = __alc_build_controls; + spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist)
We've enabled the static fixups for ASUS machines with ALC269 codec, just for making things compatible during the transition to the auto- parser. However, it seems that the static configurations do more harmful than good, as some of entries don't match with the actual hardware setups.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3ea4206..b8e06eb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5396,7 +5396,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
-#if 1 +#if 0 /* Below is a quirk table taken from the old code. * Basically the device should work as is without the fixup table. * If BIOS doesn't give a proper info, enable the corresponding
When the connections from the pin selector contain only two widgets, a route to DAC and the aa-mixer, it's certainly a single connection. In such a case, get_dac_if_single() should return the connected DAC, too.
This will improve the detection of the individual DAC assignment for each pin.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0ffccc1..a5697c3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2972,8 +2972,12 @@ static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { + struct alc_spec *spec = codec->spec; hda_nid_t sel = alc_go_down_to_selector(codec, pin); - if (snd_hda_get_conn_list(codec, sel, NULL) == 1) + hda_nid_t srcs[5]; + int num = snd_hda_get_connections(codec, sel, srcs, + ARRAY_SIZE(srcs)); + if (num == 1 || (num == 2 && srcs[1] == spec->mixer_nid)) return alc_auto_look_for_dac(codec, pin); return 0; }
This patch improves the Realtek auto-parser for assigning the DACs and mixers in more suitable ways by evaluating the assignment with "badness" calculations.
When assigning a DAC hinders the assignment of individual DACs for other pins, some badness point is given. Similarly, when it blocks the assignment of unique mixer controls, another badness point is added. Also, if no DAC, even shared DAC, can be assigned, more badness is pointed. Finally, comparing the accumulated badness, the best route is chosen among several trials.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 377 ++++++++++++++++++++++++++++++++--------- 1 files changed, 293 insertions(+), 84 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a5697c3..4746afa 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2982,76 +2982,191 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) return 0; }
-/* return 0 if no possible DAC is found, 1 if one or more found */ +/* mark up volume and mute control NIDs: used during badness parsing and + * at creating actual controls + */ +static inline unsigned int get_ctl_pos(unsigned int data) +{ + hda_nid_t nid = get_amp_nid_(data); + unsigned int dir; + if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) + return 0; + dir = get_amp_direction_(data); + return (nid << 1) | dir; +} + +#define is_ctl_used(bits, data) \ + test_bit(get_ctl_pos(data), bits) +#define mark_ctl_usage(bits, data) \ + set_bit(get_ctl_pos(data), bits) + +static void clear_vol_marks(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls)); + memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls)); +} + +/* badness definition */ +enum { + /* No primary DAC is found for the main output */ + BAD_NO_PRIMARY_DAC = 0x10000, + /* No DAC is found for the extra output */ + BAD_NO_DAC = 0x4000, + /* No individual DAC for extra output */ + BAD_NO_EXTRA_DAC = 0x1000, + /* No individual DAC for extra surrounds */ + BAD_NO_EXTRA_SURR_DAC = 0x200, + /* Primary DAC shared with main surrounds */ + BAD_SHARED_SURROUND = 0x100, + /* Volume widget is shared */ + BAD_SHARED_VOL = 0x10, + /* Primary DAC shared with main CLFE */ + BAD_SHARED_CLFE = 0x10, + /* Primary DAC shared with extra surrounds */ + BAD_SHARED_EXTRA_SURROUND = 0x10, + /* No possible multi-ios */ + BAD_MULTI_IO = 0x1, +}; + +static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac); +static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac); + +static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t nid; + unsigned int val; + int badness = 0; + + nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (nid) { + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (is_ctl_used(spec->vol_ctls, nid)) + badness += BAD_SHARED_VOL; + else + mark_ctl_usage(spec->vol_ctls, val); + } else + badness += BAD_SHARED_VOL; + nid = alc_look_for_out_mute_nid(codec, pin, dac); + if (nid) { + unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); + if (is_ctl_used(spec->sw_ctls, val)) + badness += BAD_SHARED_VOL; + else + mark_ctl_usage(spec->sw_ctls, val); + } else + badness += BAD_SHARED_VOL; + return badness; +} + +/* try to assign DACs to extra pins and return the resultant badness */ static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs, const hda_nid_t *pins, hda_nid_t *dacs) { + struct alc_spec *spec = codec->spec; int i; + int badness = 0; + hda_nid_t dac;
if (num_outs && !dacs[0]) { - dacs[0] = alc_auto_look_for_dac(codec, pins[0]); - if (!dacs[0]) - return 0; + dac = dacs[0] = alc_auto_look_for_dac(codec, pins[0]); + if (!dacs[0]) { + dac = spec->private_dac_nids[0]; + if (!alc_auto_is_dac_reachable(codec, pins[0], dac)) + return BAD_NO_DAC; + badness += BAD_NO_EXTRA_DAC; + } + badness += eval_shared_vol_badness(codec, pins[0], dac); }
for (i = 1; i < num_outs; i++) dacs[i] = get_dac_if_single(codec, pins[i]); for (i = 1; i < num_outs; i++) { - if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pins[i]); + dac = dacs[i]; + if (!dac) + dac = dacs[i] = alc_auto_look_for_dac(codec, pins[i]); + if (!dac) { + if (alc_auto_is_dac_reachable(codec, pins[i], dacs[0])) { + dac = dacs[0]; + badness += BAD_SHARED_EXTRA_SURROUND; + } else if (alc_auto_is_dac_reachable(codec, pins[i], + spec->private_dac_nids[0])) { + dac = spec->private_dac_nids[0]; + badness += BAD_NO_EXTRA_SURR_DAC; + } else + badness += BAD_NO_DAC; + } + if (dac) + badness += eval_shared_vol_badness(codec, pins[i], dac); } - return 1; + return badness; }
static int alc_auto_fill_multi_ios(struct hda_codec *codec, unsigned int location, int offset); -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac);
/* fill in the dac_nids table from the parsed pin configuration */ -static int alc_auto_fill_dac_nids(struct hda_codec *codec) +static int fill_and_eval_dacs(struct hda_codec *codec, + bool fill_hardwired) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int location, defcfg; - int num_pins; - bool redone = false; - int i; + int i, err, badness;
- again: /* set num_dacs once to full for alc_auto_look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; - spec->multiout.hp_out_nid[0] = 0; - spec->multiout.extra_out_nid[0] = 0; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); spec->multiout.dac_nids = spec->private_dac_nids; + memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); + memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); + memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; + clear_vol_marks(codec); + badness = 0;
/* fill hard-wired DACs first */ - if (!redone) { + if (fill_hardwired) { for (i = 0; i < cfg->line_outs; i++) spec->private_dac_nids[i] = get_dac_if_single(codec, cfg->line_out_pins[i]); - if (cfg->hp_outs) - spec->multiout.hp_out_nid[0] = - get_dac_if_single(codec, cfg->hp_pins[0]); - if (cfg->speaker_outs) - spec->multiout.extra_out_nid[0] = - get_dac_if_single(codec, cfg->speaker_pins[0]); + for (i = 0; i < cfg->hp_outs; i++) + spec->multiout.hp_out_nid[i] = + get_dac_if_single(codec, cfg->hp_pins[i]); + for (i = 0; i < cfg->speaker_outs; i++) + spec->multiout.extra_out_nid[i] = + get_dac_if_single(codec, cfg->speaker_pins[i]); }
for (i = 0; i < cfg->line_outs; i++) { hda_nid_t pin = cfg->line_out_pins[i]; - if (spec->private_dac_nids[i]) - continue; - spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin); - if (!spec->private_dac_nids[i] && !redone) { - /* if we can't find primary DACs, re-probe without - * checking the hard-wired DACs - */ - redone = true; - goto again; + hda_nid_t dac; + if (!spec->private_dac_nids[i]) + spec->private_dac_nids[i] = + alc_auto_look_for_dac(codec, pin); + dac = spec->private_dac_nids[i]; + if (!dac) { + if (!i) + badness += BAD_NO_PRIMARY_DAC; + else if (alc_auto_is_dac_reachable(codec, pin, + spec->private_dac_nids[0])) { + if (i == 1) + badness += BAD_SHARED_SURROUND; + else + badness += BAD_SHARED_CLFE; + dac = spec->private_dac_nids[0]; + } else + badness += BAD_NO_DAC; } + if (dac) + badness += eval_shared_vol_badness(codec, pin, dac); }
/* re-count num_dacs and squash invalid entries */ @@ -3071,26 +3186,114 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) /* try to fill multi-io first */ defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]); location = get_defcfg_location(defcfg); - - num_pins = alc_auto_fill_multi_ios(codec, location, 0); - if (num_pins > 0) { - spec->multi_ios = num_pins; - spec->ext_channel_count = 2; - spec->multiout.num_dacs = num_pins + 1; - } + err = alc_auto_fill_multi_ios(codec, location, 0); + if (err < 0) + return err; + badness += err; }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) - alc_auto_fill_extra_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = alc_auto_fill_extra_dacs(codec, cfg->hp_outs, + cfg->hp_pins, + spec->multiout.hp_out_nid); + if (err < 0) + return err; + badness += err; + } if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - int err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid); - /* if no speaker volume is assigned, try again as the primary - * output - */ - if (!err && cfg->speaker_outs > 0 && + err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid); + if (err < 0) + return err; + badness += err; + } + if (!spec->multi_ios && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->hp_outs) { + /* try multi-ios with HP + inputs */ + defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]); + location = get_defcfg_location(defcfg); + err = alc_auto_fill_multi_ios(codec, location, 1); + if (err < 0) + return err; + badness += err; + } + + return badness; +} + +#define DEBUG_BADNESS + +#ifdef DEBUG_BADNESS +#define debug_badness snd_printdd +#else +#define debug_badness(...) +#endif + +static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) +{ + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[2], + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3]); + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->hp_pins[0], cfg->hp_pins[1], + cfg->hp_pins[2], cfg->hp_pins[2], + spec->multiout.hp_out_nid[0], + spec->multiout.hp_out_nid[1], + spec->multiout.hp_out_nid[2], + spec->multiout.hp_out_nid[3]); + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->speaker_pins[0], cfg->speaker_pins[1], + cfg->speaker_pins[2], cfg->speaker_pins[3], + spec->multiout.extra_out_nid[0], + spec->multiout.extra_out_nid[1], + spec->multiout.extra_out_nid[2], + spec->multiout.extra_out_nid[3]); +} + +static int alc_auto_fill_dac_nids(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *best_cfg; + int best_badness = INT_MAX; + int badness; + bool fill_hardwired = true; + bool best_wired = true; + bool hp_spk_swapped = false; + + best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); + if (!best_cfg) + return -ENOMEM; + *best_cfg = *cfg; + + for (;;) { + badness = fill_and_eval_dacs(codec, fill_hardwired); + if (badness < 0) + return badness; + debug_badness("==> lo_type=%d, wired=%d, badness=0x%x\n", + cfg->line_out_type, fill_hardwired, badness); + debug_show_configs(spec, cfg); + if (badness < best_badness) { + best_badness = badness; + *best_cfg = *cfg; + best_wired = fill_hardwired; + } + if (!badness) + break; + if (fill_hardwired) { + fill_hardwired = false; + continue; + } + if (hp_spk_swapped) + break; + hp_spk_swapped = true; + if (cfg->speaker_outs > 0 && cfg->line_out_type == AUTO_PIN_HP_OUT) { cfg->hp_outs = cfg->line_outs; memcpy(cfg->hp_pins, cfg->line_out_pins, @@ -3101,48 +3304,45 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) cfg->speaker_outs = 0; memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - redone = false; - goto again; - } + fill_hardwired = true; + continue; + } + if (cfg->hp_outs > 0 && + 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 = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + fill_hardwired = true; + continue; + } + break; }
- if (!spec->multi_ios && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->hp_outs) { - /* try multi-ios with HP + inputs */ - defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]); - location = get_defcfg_location(defcfg); - - num_pins = alc_auto_fill_multi_ios(codec, location, 1); - if (num_pins > 0) { - spec->multi_ios = num_pins; - spec->ext_channel_count = 2; - spec->multiout.num_dacs = num_pins + 1; - } + if (badness) { + *cfg = *best_cfg; + fill_and_eval_dacs(codec, best_wired); } + debug_badness("==> Best config: lo_type=%d, wired=%d\n", + cfg->line_out_type, best_wired); + debug_show_configs(spec, cfg);
if (cfg->line_out_pins[0]) spec->vmaster_nid = alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0], spec->multiout.dac_nids[0]); - return 0; -}
-static inline unsigned int get_ctl_pos(unsigned int data) -{ - hda_nid_t nid = get_amp_nid_(data); - unsigned int dir; - if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) - return 0; - dir = get_amp_direction_(data); - return (nid << 1) | dir; + /* clear the bitmap flags for creating controls */ + clear_vol_marks(codec); + kfree(best_cfg); + return 0; }
-#define is_ctl_used(bits, data) \ - test_bit(get_ctl_pos(data), bits) -#define mark_ctl_usage(bits, data) \ - set_bit(get_ctl_pos(data), bits) - static int alc_auto_add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, hda_nid_t nid, unsigned int chs) @@ -3539,6 +3739,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t prime_dac = spec->private_dac_nids[0]; int type, i, dacs, num_pins = 0; + int badness = 0;
dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { @@ -3563,12 +3764,16 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, } if (!dac) dac = alc_auto_look_for_dac(codec, nid); - if (!dac) + if (!dac) { + badness += BAD_MULTI_IO; continue; + } spec->multi_io[num_pins].pin = nid; spec->multi_io[num_pins].dac = dac; num_pins++; spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + if (num_pins >= 2) + break; } } spec->multiout.num_dacs = dacs; @@ -3577,9 +3782,13 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, memset(spec->private_dac_nids + dacs, 0, sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - dacs)); spec->private_dac_nids[0] = prime_dac; - return 0; + return badness; } - return num_pins; + + spec->multi_ios = num_pins; + spec->ext_channel_count = 2; + spec->multiout.num_dacs = num_pins + 1; + return 0; }
static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
So far, the Realtek driver tires to assign the single-connected routes for all pins only once at the beginning. However, since some DACs have been already mapped, the rest pins might have also single conections.
In this patch, the driver does the single-connection assignment in a loop until all possbile single-connections are checked. This will improve the DAC assignment, e.g. for ASUS G72.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 124 ++++++++++++++++++++++++++++++----------- 1 files changed, 91 insertions(+), 33 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4746afa..29c1925 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2926,10 +2926,22 @@ static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, return 0; }
+static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) +{ + struct alc_spec *spec = codec->spec; + if (found_in_nid_list(nid, spec->multiout.dac_nids, + ARRAY_SIZE(spec->private_dac_nids)) || + found_in_nid_list(nid, spec->multiout.hp_out_nid, + ARRAY_SIZE(spec->multiout.hp_out_nid)) || + found_in_nid_list(nid, spec->multiout.extra_out_nid, + ARRAY_SIZE(spec->multiout.extra_out_nid))) + return true; + return false; +} + /* look for an empty DAC slot */ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { - struct alc_spec *spec = codec->spec; hda_nid_t srcs[5]; int i, num;
@@ -2939,16 +2951,8 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); if (!nid) continue; - if (found_in_nid_list(nid, spec->multiout.dac_nids, - ARRAY_SIZE(spec->private_dac_nids))) - continue; - if (found_in_nid_list(nid, spec->multiout.hp_out_nid, - ARRAY_SIZE(spec->multiout.hp_out_nid))) - continue; - if (found_in_nid_list(nid, spec->multiout.extra_out_nid, - ARRAY_SIZE(spec->multiout.extra_out_nid))) - continue; - return nid; + if (!alc_is_dac_already_used(codec, nid)) + return nid; } return 0; } @@ -2974,12 +2978,23 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; hda_nid_t sel = alc_go_down_to_selector(codec, pin); - hda_nid_t srcs[5]; - int num = snd_hda_get_connections(codec, sel, srcs, + hda_nid_t nid, nid_found, srcs[5]; + int i, num = snd_hda_get_connections(codec, sel, srcs, ARRAY_SIZE(srcs)); - if (num == 1 || (num == 2 && srcs[1] == spec->mixer_nid)) + if (num == 1) return alc_auto_look_for_dac(codec, pin); - return 0; + nid_found = 0; + for (i = 0; i < num; i++) { + if (srcs[i] == spec->mixer_nid) + continue; + nid = alc_auto_mix_to_dac(codec, srcs[i]); + if (nid && !alc_is_dac_already_used(codec, nid)) { + if (nid_found) + return 0; + nid_found = nid; + } + } + return nid_found; }
/* mark up volume and mute control NIDs: used during badness parsing and @@ -3076,16 +3091,30 @@ static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs, int badness = 0; hda_nid_t dac;
- if (num_outs && !dacs[0]) { - dac = dacs[0] = alc_auto_look_for_dac(codec, pins[0]); - if (!dacs[0]) { - dac = spec->private_dac_nids[0]; - if (!alc_auto_is_dac_reachable(codec, pins[0], dac)) - return BAD_NO_DAC; - badness += BAD_NO_EXTRA_DAC; + if (!num_outs) + return 0; + + if (!dacs[0]) + dacs[0] = alc_auto_look_for_dac(codec, pins[0]); + if (!dacs[0]) { + for (i = 1; i < num_outs; i++) { + dac = dacs[i]; + if (dac && alc_auto_is_dac_reachable(codec, pins[0], dac)) { + dacs[0] = dac; + dacs[i] = 0; + break; + } } - badness += eval_shared_vol_badness(codec, pins[0], dac); } + dac = dacs[0]; + if (!dac) { + dac = spec->private_dac_nids[0]; + if (!alc_auto_is_dac_reachable(codec, pins[0], dac)) + return BAD_NO_DAC; + badness += BAD_NO_EXTRA_DAC; + } + if (dac) + badness += eval_shared_vol_badness(codec, pins[0], dac);
for (i = 1; i < num_outs; i++) dacs[i] = get_dac_if_single(codec, pins[i]); @@ -3113,6 +3142,21 @@ static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs, static int alc_auto_fill_multi_ios(struct hda_codec *codec, unsigned int location, int offset);
+static bool alc_map_singles(struct hda_codec *codec, int outs, + const hda_nid_t *pins, hda_nid_t *dacs) +{ + int i; + bool found = false; + for (i = 0; i < outs; i++) { + if (dacs[i]) + continue; + dacs[i] = get_dac_if_single(codec, pins[i]); + if (dacs[i]) + found = true; + } + return found; +} + /* fill in the dac_nids table from the parsed pin configuration */ static int fill_and_eval_dacs(struct hda_codec *codec, bool fill_hardwired) @@ -3120,7 +3164,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; unsigned int location, defcfg; - int i, err, badness; + int i, j, err, badness;
/* set num_dacs once to full for alc_auto_look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; @@ -3134,15 +3178,18 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
/* fill hard-wired DACs first */ if (fill_hardwired) { - for (i = 0; i < cfg->line_outs; i++) - spec->private_dac_nids[i] = - get_dac_if_single(codec, cfg->line_out_pins[i]); - for (i = 0; i < cfg->hp_outs; i++) - spec->multiout.hp_out_nid[i] = - get_dac_if_single(codec, cfg->hp_pins[i]); - for (i = 0; i < cfg->speaker_outs; i++) - spec->multiout.extra_out_nid[i] = - get_dac_if_single(codec, cfg->speaker_pins[i]); + bool mapped; + do { + mapped = alc_map_singles(codec, cfg->line_outs, + cfg->line_out_pins, + spec->private_dac_nids); + mapped |= alc_map_singles(codec, cfg->hp_outs, + cfg->hp_pins, + spec->multiout.hp_out_nid); + mapped |= alc_map_singles(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid); + } while (mapped); }
for (i = 0; i < cfg->line_outs; i++) { @@ -3152,6 +3199,17 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin); dac = spec->private_dac_nids[i]; + if (!dac && !i) { + for (j = 1; j < cfg->line_outs; j++) { + hda_nid_t dac2 = spec->private_dac_nids[j]; + if (dac2 && + alc_auto_is_dac_reachable(codec, pin, dac2)) { + dac = spec->private_dac_nids[0] = dac2; + spec->private_dac_nids[j] = 0; + break; + } + } + } if (!dac) { if (!i) badness += BAD_NO_PRIMARY_DAC;
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 29c1925..4b2ecbc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3298,6 +3298,11 @@ static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) spec->multiout.dac_nids[1], spec->multiout.dac_nids[2], spec->multiout.dac_nids[3]); + if (spec->multi_ios > 0) + debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", + spec->multi_ios, + spec->multi_io[0].pin, spec->multi_io[1].pin, + spec->multi_io[0].dac, spec->multi_io[1].dac); debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->hp_pins[0], cfg->hp_pins[1], cfg->hp_pins[2], cfg->hp_pins[2],
Try harder to fit the multi-io pins also by checking the hard-wired connections for multi-ios. Also, the badness values are adjusted to prioritize the multi-ios as more valuable. These changes will enable the multi-io on some machines without losing the current capability.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 196 ++++++++++++++++++++++++++++------------- 1 files changed, 134 insertions(+), 62 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4b2ecbc..d0c71d5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2929,6 +2929,7 @@ static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; + int i; if (found_in_nid_list(nid, spec->multiout.dac_nids, ARRAY_SIZE(spec->private_dac_nids)) || found_in_nid_list(nid, spec->multiout.hp_out_nid, @@ -2936,6 +2937,10 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) found_in_nid_list(nid, spec->multiout.extra_out_nid, ARRAY_SIZE(spec->multiout.extra_out_nid))) return true; + for (i = 0; i < spec->multi_ios; i++) { + if (spec->multi_io[i].dac == nid) + return true; + } return false; }
@@ -3028,20 +3033,20 @@ enum { BAD_NO_PRIMARY_DAC = 0x10000, /* No DAC is found for the extra output */ BAD_NO_DAC = 0x4000, + /* No possible multi-ios */ + BAD_MULTI_IO = 0x103, /* No individual DAC for extra output */ - BAD_NO_EXTRA_DAC = 0x1000, + BAD_NO_EXTRA_DAC = 0x102, /* No individual DAC for extra surrounds */ - BAD_NO_EXTRA_SURR_DAC = 0x200, + BAD_NO_EXTRA_SURR_DAC = 0x101, /* Primary DAC shared with main surrounds */ BAD_SHARED_SURROUND = 0x100, - /* Volume widget is shared */ - BAD_SHARED_VOL = 0x10, /* Primary DAC shared with main CLFE */ BAD_SHARED_CLFE = 0x10, /* Primary DAC shared with extra surrounds */ BAD_SHARED_EXTRA_SURROUND = 0x10, - /* No possible multi-ios */ - BAD_MULTI_IO = 0x1, + /* Volume widget is shared */ + BAD_SHARED_VOL = 0x10, };
static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, @@ -3140,7 +3145,8 @@ static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs, }
static int alc_auto_fill_multi_ios(struct hda_codec *codec, - unsigned int location, int offset); + hda_nid_t reference_pin, + bool hardwired, int offset);
static bool alc_map_singles(struct hda_codec *codec, int outs, const hda_nid_t *pins, hda_nid_t *dacs) @@ -3159,11 +3165,11 @@ static bool alc_map_singles(struct hda_codec *codec, int outs,
/* fill in the dac_nids table from the parsed pin configuration */ static int fill_and_eval_dacs(struct hda_codec *codec, - bool fill_hardwired) + bool fill_hardwired, + bool fill_mio_first) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int location, defcfg; int i, j, err, badness;
/* set num_dacs once to full for alc_auto_look_for_dac() */ @@ -3181,14 +3187,20 @@ static int fill_and_eval_dacs(struct hda_codec *codec, bool mapped; do { mapped = alc_map_singles(codec, cfg->line_outs, - cfg->line_out_pins, - spec->private_dac_nids); + cfg->line_out_pins, + spec->private_dac_nids); mapped |= alc_map_singles(codec, cfg->hp_outs, cfg->hp_pins, spec->multiout.hp_out_nid); mapped |= alc_map_singles(codec, cfg->speaker_outs, cfg->speaker_pins, spec->multiout.extra_out_nid); + if (fill_mio_first && cfg->line_outs == 1 && + cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); + if (!err) + mapped = true; + } } while (mapped); }
@@ -3240,14 +3252,13 @@ static int fill_and_eval_dacs(struct hda_codec *codec, } }
- if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + if (fill_mio_first && + cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ - defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]); - location = get_defcfg_location(defcfg); - err = alc_auto_fill_multi_ios(codec, location, 0); + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); if (err < 0) return err; - badness += err; + /* we don't count badness at this stage yet */ }
if (cfg->line_out_type != AUTO_PIN_HP_OUT) { @@ -3266,18 +3277,30 @@ static int fill_and_eval_dacs(struct hda_codec *codec, return err; badness += err; } - if (!spec->multi_ios && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->hp_outs) { + if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + badness += err; + } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { /* try multi-ios with HP + inputs */ - defcfg = snd_hda_codec_get_pincfg(codec, cfg->hp_pins[0]); - location = get_defcfg_location(defcfg); - err = alc_auto_fill_multi_ios(codec, location, 1); + err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, 1); if (err < 0) return err; badness += err; }
+ if (spec->multi_ios == 2) { + for (i = 0; i < 2; i++) + spec->private_dac_nids[spec->multiout.num_dacs++] = + spec->multi_io[i].dac; + spec->ext_channel_count = 2; + } else if (spec->multi_ios) { + spec->multi_ios = 0; + badness += BAD_MULTI_IO; + } + return badness; }
@@ -3326,8 +3349,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) struct auto_pin_cfg *best_cfg; int best_badness = INT_MAX; int badness; - bool fill_hardwired = true; - bool best_wired = true; + bool fill_hardwired = true, fill_mio_first = true; + bool best_wired = true, best_mio = true; bool hp_spk_swapped = false;
best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); @@ -3336,23 +3359,28 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) *best_cfg = *cfg;
for (;;) { - badness = fill_and_eval_dacs(codec, fill_hardwired); + badness = fill_and_eval_dacs(codec, fill_hardwired, + fill_mio_first); if (badness < 0) return badness; - debug_badness("==> lo_type=%d, wired=%d, badness=0x%x\n", - cfg->line_out_type, fill_hardwired, badness); + debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", + cfg->line_out_type, fill_hardwired, fill_mio_first, + badness); debug_show_configs(spec, cfg); if (badness < best_badness) { best_badness = badness; *best_cfg = *cfg; best_wired = fill_hardwired; + best_mio = fill_mio_first; } if (!badness) break; - if (fill_hardwired) { - fill_hardwired = false; + fill_mio_first = !fill_mio_first; + if (!fill_mio_first) + continue; + fill_hardwired = !fill_hardwired; + if (!fill_hardwired) continue; - } if (hp_spk_swapped) break; hp_spk_swapped = true; @@ -3389,10 +3417,10 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
if (badness) { *cfg = *best_cfg; - fill_and_eval_dacs(codec, best_wired); + fill_and_eval_dacs(codec, best_wired, best_mio); } - debug_badness("==> Best config: lo_type=%d, wired=%d\n", - cfg->line_out_type, best_wired); + debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", + cfg->line_out_type, best_wired, best_mio); debug_show_configs(spec, cfg);
if (cfg->line_out_pins[0]) @@ -3791,66 +3819,110 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) } }
+/* check whether the given pin can be a multi-io pin */ +static bool can_be_multiio_pin(struct hda_codec *codec, + unsigned int location, hda_nid_t nid) +{ + unsigned int defcfg, caps; + + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) + return false; + if (location && get_defcfg_location(defcfg) != location) + return false; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_OUT)) + return false; + return true; +} + /* * multi-io helper + * + * When hardwired is set, try to fill ony hardwired pins, and returns + * zero if any pins are filled, non-zero if nothing found. + * When hardwired is off, try to fill possible input pins, and returns + * the badness value. */ static int alc_auto_fill_multi_ios(struct hda_codec *codec, - unsigned int location, - int offset) + hda_nid_t reference_pin, + bool hardwired, int offset) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t prime_dac = spec->private_dac_nids[0]; - int type, i, dacs, num_pins = 0; + int type, i, j, dacs, num_pins, old_pins; + unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); + unsigned int location = get_defcfg_location(defcfg); int badness = 0;
+ old_pins = spec->multi_ios; + if (old_pins >= 2) + goto end_fill; + + num_pins = 0; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != type) + continue; + if (can_be_multiio_pin(codec, location, + cfg->inputs[i].pin)) + num_pins++; + } + } + if (num_pins < 2) + goto end_fill; + dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; hda_nid_t dac = 0; - unsigned int defcfg, caps; + if (cfg->inputs[i].type != type) continue; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - continue; - if (location && get_defcfg_location(defcfg) != location) + if (!can_be_multiio_pin(codec, location, nid)) continue; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) + for (j = 0; j < spec->multi_ios; j++) { + if (nid == spec->multi_io[j].pin) + break; + } + if (j < spec->multi_ios) continue; - if (offset && offset + num_pins < dacs) { - dac = spec->private_dac_nids[offset + num_pins]; + + if (offset && offset + spec->multi_ios < dacs) { + dac = spec->private_dac_nids[offset + spec->multi_ios]; if (!alc_auto_is_dac_reachable(codec, nid, dac)) dac = 0; } - if (!dac) + if (hardwired) + dac = get_dac_if_single(codec, nid); + else if (!dac) dac = alc_auto_look_for_dac(codec, nid); if (!dac) { - badness += BAD_MULTI_IO; + badness++; continue; } - spec->multi_io[num_pins].pin = nid; - spec->multi_io[num_pins].dac = dac; - num_pins++; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; - if (num_pins >= 2) + spec->multi_io[spec->multi_ios].pin = nid; + spec->multi_io[spec->multi_ios].dac = dac; + spec->multi_ios++; + if (spec->multi_ios >= 2) break; } } - spec->multiout.num_dacs = dacs; - if (num_pins < 2) { - /* clear up again */ - memset(spec->private_dac_nids + dacs, 0, - sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - dacs)); - spec->private_dac_nids[0] = prime_dac; + end_fill: + if (badness) + badness = BAD_MULTI_IO; + if (old_pins == spec->multi_ios) { + if (hardwired) + return 1; /* nothing found */ + else + return badness; /* no badness if nothing found */ + } + if (!hardwired && spec->multi_ios < 2) { + spec->multi_ios = old_pins; return badness; }
- spec->multi_ios = num_pins; - spec->ext_channel_count = 2; - spec->multiout.num_dacs = num_pins + 1; return 0; }
ALC880 model=lg could work fine with the auto-parser due to the recent rewrite, but it still needs the manual adjustment; namely, the BIOS leaves unused pins as some real active jacks. This confuses the parser. Thus we just cover these pins and override the pin-configs as a fix-up.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d0c71d5..a391465 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4404,6 +4404,7 @@ static const struct hda_amp_list alc880_loopbacks[] = { enum { ALC880_FIXUP_GPIO2, ALC880_FIXUP_MEDION_RIM, + ALC880_FIXUP_LG, };
static const struct alc_fixup alc880_fixups[] = { @@ -4421,10 +4422,23 @@ static const struct alc_fixup alc880_fixups[] = { .chained = true, .chain_id = ALC880_FIXUP_GPIO2, }, + [ALC880_FIXUP_LG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* disable bogus unused pins */ + { 0x16, 0x411111f0 }, + { 0x18, 0x411111f0 }, + { 0x1a, 0x411111f0 }, + { } + } + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), {} };
The Medion W810 with ALC880 has a typical BIOS bug, copying the pin-defaults without disabling the unused pins. At least, the pin 0x17 must be disabled. Also, it requires GPIO-2 setup.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a391465..1cad674 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4405,6 +4405,7 @@ enum { ALC880_FIXUP_GPIO2, ALC880_FIXUP_MEDION_RIM, ALC880_FIXUP_LG, + ALC880_FIXUP_W810, };
static const struct alc_fixup alc880_fixups[] = { @@ -4432,9 +4433,21 @@ static const struct alc_fixup alc880_fixups[] = { { } } }, + [ALC880_FIXUP_W810] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG),
Refactor the DAC filling function to be used for both the primary line outputs and extra outputs using the individual badness tables.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 154 ++++++++++++++++++++-------------------- 1 files changed, 77 insertions(+), 77 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1cad674..a0df05d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2969,6 +2969,8 @@ static bool alc_auto_is_dac_reachable(struct hda_codec *codec, hda_nid_t srcs[5]; int i, num;
+ if (!pin || !dac) + return false; pin = alc_go_down_to_selector(codec, pin); num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); for (i = 0; i < num; i++) { @@ -3087,60 +3089,88 @@ static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, return badness; }
-/* try to assign DACs to extra pins and return the resultant badness */ -static int alc_auto_fill_extra_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs) +struct badness_table { + int no_primary_dac; /* no primary DAC */ + int no_dac; /* no secondary DACs */ + int shared_primary; /* primary DAC is shared with main output */ + int shared_surr; /* secondary DAC shared with main or primary */ + int shared_clfe; /* third DAC shared with main or primary */ + int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ +}; + +static struct badness_table main_out_badness = { + .no_primary_dac = BAD_NO_PRIMARY_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_PRIMARY_DAC, + .shared_surr = BAD_SHARED_SURROUND, + .shared_clfe = BAD_SHARED_CLFE, + .shared_surr_main = BAD_SHARED_SURROUND, +}; + +static struct badness_table extra_out_badness = { + .no_primary_dac = BAD_NO_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_EXTRA_DAC, + .shared_surr = BAD_SHARED_EXTRA_SURROUND, + .shared_clfe = BAD_SHARED_EXTRA_SURROUND, + .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, +}; + +/* try to assign DACs to pins and return the resultant badness */ +static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, + const hda_nid_t *pins, hda_nid_t *dacs, + const struct badness_table *bad) { struct alc_spec *spec = codec->spec; - int i; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, j; int badness = 0; hda_nid_t dac;
if (!num_outs) return 0;
- if (!dacs[0]) - dacs[0] = alc_auto_look_for_dac(codec, pins[0]); - if (!dacs[0]) { - for (i = 1; i < num_outs; i++) { - dac = dacs[i]; - if (dac && alc_auto_is_dac_reachable(codec, pins[0], dac)) { - dacs[0] = dac; - dacs[i] = 0; - break; + for (i = 0; i < num_outs; i++) { + hda_nid_t pin = pins[i]; + if (!dacs[i]) + dacs[i] = alc_auto_look_for_dac(codec, pin); + if (!dacs[i] && !i) { + for (j = 1; j < num_outs; j++) { + if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) { + dacs[0] = dacs[j]; + dacs[j] = 0; + break; + } } } - } - dac = dacs[0]; - if (!dac) { - dac = spec->private_dac_nids[0]; - if (!alc_auto_is_dac_reachable(codec, pins[0], dac)) - return BAD_NO_DAC; - badness += BAD_NO_EXTRA_DAC; - } - if (dac) - badness += eval_shared_vol_badness(codec, pins[0], dac); - - for (i = 1; i < num_outs; i++) - dacs[i] = get_dac_if_single(codec, pins[i]); - for (i = 1; i < num_outs; i++) { dac = dacs[i]; - if (!dac) - dac = dacs[i] = alc_auto_look_for_dac(codec, pins[i]); if (!dac) { - if (alc_auto_is_dac_reachable(codec, pins[i], dacs[0])) { + if (alc_auto_is_dac_reachable(codec, pin, dacs[0])) dac = dacs[0]; - badness += BAD_SHARED_EXTRA_SURROUND; - } else if (alc_auto_is_dac_reachable(codec, pins[i], + else if (cfg->line_outs > i && + alc_auto_is_dac_reachable(codec, pin, + spec->private_dac_nids[i])) + dac = spec->private_dac_nids[i]; + if (dac) { + if (!i) + badness += bad->shared_primary; + else if (i == 1) + badness += bad->shared_surr; + else + badness += bad->shared_clfe; + } else if (alc_auto_is_dac_reachable(codec, pin, spec->private_dac_nids[0])) { dac = spec->private_dac_nids[0]; - badness += BAD_NO_EXTRA_SURR_DAC; - } else - badness += BAD_NO_DAC; + badness += bad->shared_surr_main; + } else if (!i) + badness += bad->no_primary_dac; + else + badness += bad->no_dac; } if (dac) - badness += eval_shared_vol_badness(codec, pins[i], dac); + badness += eval_shared_vol_badness(codec, pin, dac); } + return badness; }
@@ -3170,7 +3200,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int i, j, err, badness; + int i, err, badness;
/* set num_dacs once to full for alc_auto_look_for_dac() */ spec->multiout.num_dacs = cfg->line_outs; @@ -3204,40 +3234,9 @@ static int fill_and_eval_dacs(struct hda_codec *codec, } while (mapped); }
- for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t pin = cfg->line_out_pins[i]; - hda_nid_t dac; - if (!spec->private_dac_nids[i]) - spec->private_dac_nids[i] = - alc_auto_look_for_dac(codec, pin); - dac = spec->private_dac_nids[i]; - if (!dac && !i) { - for (j = 1; j < cfg->line_outs; j++) { - hda_nid_t dac2 = spec->private_dac_nids[j]; - if (dac2 && - alc_auto_is_dac_reachable(codec, pin, dac2)) { - dac = spec->private_dac_nids[0] = dac2; - spec->private_dac_nids[j] = 0; - break; - } - } - } - if (!dac) { - if (!i) - badness += BAD_NO_PRIMARY_DAC; - else if (alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[0])) { - if (i == 1) - badness += BAD_SHARED_SURROUND; - else - badness += BAD_SHARED_CLFE; - dac = spec->private_dac_nids[0]; - } else - badness += BAD_NO_DAC; - } - if (dac) - badness += eval_shared_vol_badness(codec, pin, dac); - } + badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins, + spec->private_dac_nids, + &main_out_badness);
/* re-count num_dacs and squash invalid entries */ spec->multiout.num_dacs = 0; @@ -3262,17 +3261,18 @@ static int fill_and_eval_dacs(struct hda_codec *codec, }
if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc_auto_fill_extra_dacs(codec, cfg->hp_outs, - cfg->hp_pins, - spec->multiout.hp_out_nid); + err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins, + spec->multiout.hp_out_nid, + &extra_out_badness); if (err < 0) return err; badness += err; } if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_extra_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid); + err = alc_auto_fill_dacs(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid, + &extra_out_badness); if (err < 0) return err; badness += err;
Clevo machines with ALC880 are all well with proper BIOS setup. It seems still requiring the additional COEF setup for the EAPD.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a0df05d..4f8c362 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4406,6 +4406,7 @@ enum { ALC880_FIXUP_MEDION_RIM, ALC880_FIXUP_LG, ALC880_FIXUP_W810, + ALC880_FIXUP_EAPD_COEF, };
static const struct alc_fixup alc880_fixups[] = { @@ -4443,10 +4444,20 @@ static const struct alc_fixup alc880_fixups[] = { .chained = true, .chain_id = ALC880_FIXUP_GPIO2, }, + [ALC880_FIXUP_EAPD_COEF] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, + {} + }, + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG),
It needs a few extra setups for EAPD, but others look fairly straightforward.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4f8c362..e6eec9a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4407,6 +4407,7 @@ enum { ALC880_FIXUP_LG, ALC880_FIXUP_W810, ALC880_FIXUP_EAPD_COEF, + ALC880_FIXUP_TCL_S700, };
static const struct alc_fixup alc880_fixups[] = { @@ -4453,6 +4454,17 @@ static const struct alc_fixup alc880_fixups[] = { {} }, }, + [ALC880_FIXUP_TCL_S700] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* change to EAPD mode */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, + {} + }, + .chained = true, + .chain_id = ALC880_FIXUP_GPIO2, + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { @@ -4463,6 +4475,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), {} };
Move the call of alc_apply_fixup() with ALC_FIXUP_ACT_PROBE after the whole setups of patch_ops & co, so that the fix-up function may override the default setup. This will be needed for installing the own unsol event handler (e.g. for volume-knob controls).
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 32 ++++++++++++++++---------------- 1 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e6eec9a..895113e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4566,8 +4566,6 @@ static int patch_alc880(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc_auto_init_std; @@ -4578,6 +4576,8 @@ static int patch_alc880(struct hda_codec *codec) spec->loopback.amplist = alc880_loopbacks; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error: @@ -4749,8 +4749,6 @@ static int patch_alc260(struct hda_codec *codec) set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; @@ -4759,6 +4757,8 @@ static int patch_alc260(struct hda_codec *codec) spec->loopback.amplist = alc260_loopbacks; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error: @@ -5165,8 +5165,6 @@ static int patch_alc882(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; if (board_config == ALC_MODEL_AUTO) spec->init_hook = alc_auto_init_std; @@ -5178,6 +5176,8 @@ static int patch_alc882(struct hda_codec *codec) spec->loopback.amplist = alc882_loopbacks; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error: @@ -5331,8 +5331,6 @@ static int patch_alc262(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; @@ -5342,6 +5340,8 @@ static int patch_alc262(struct hda_codec *codec) spec->loopback.amplist = alc262_loopbacks; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error: @@ -5992,8 +5992,6 @@ static int patch_alc269(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; #ifdef CONFIG_PM codec->patch_ops.resume = alc269_resume; @@ -6008,6 +6006,8 @@ static int patch_alc269(struct hda_codec *codec) codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error: @@ -6146,8 +6146,6 @@ static int patch_alc861(struct hda_codec *codec) set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -6156,6 +6154,8 @@ static int patch_alc861(struct hda_codec *codec) spec->loopback.amplist = alc861_loopbacks; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error: @@ -6270,8 +6270,6 @@ static int patch_alc861vd(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops;
spec->init_hook = alc_auto_init_std; @@ -6281,6 +6279,8 @@ static int patch_alc861vd(struct hda_codec *codec) spec->loopback.amplist = alc861vd_loopbacks; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error: @@ -6659,8 +6659,6 @@ static int patch_alc662(struct hda_codec *codec) } }
- alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); - codec->patch_ops = alc_patch_ops; spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; @@ -6670,6 +6668,8 @@ static int patch_alc662(struct hda_codec *codec) spec->loopback.amplist = alc662_loopbacks; #endif
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + return 0;
error:
Now adding the support for the volume-knob widget, we can move the static quirk for ALC880 model=fujitsu to the auto-parser completely.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 76 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 895113e..6a6436a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -651,15 +651,51 @@ static void alc_exec_unsol_event(struct hda_codec *codec, int action) snd_hda_jack_report_sync(codec); }
+/* update the master volume per volume-knob's unsol event */ +static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int val; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value *uctl; + + kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume"); + if (!kctl) + return; + uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return; + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + val &= HDA_AMP_VOLMASK; + uctl->value.integer.value[0] = val; + uctl->value.integer.value[1] = val; + kctl->put(kctl, uctl); + kfree(uctl); +} + /* unsolicited event for HP jack sensing */ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) { + int action; + if (codec->vendor_id == 0x10ec0880) res >>= 28; else res >>= 26; - res = snd_hda_jack_get_action(codec, res); - alc_exec_unsol_event(codec, res); + action = snd_hda_jack_get_action(codec, res); + if (res == ALC_DCVOL_EVENT) { + /* Execute the dc-vol event here as it requires the NID + * but we don't pass NID to alc_exec_unsol_event(). + * Once when we convert all static quirks to the auto-parser, + * this can be integerated into there. + */ + struct hda_jack_tbl *jack; + jack = snd_hda_jack_tbl_get_from_tag(codec, res); + if (jack) + alc_update_knob_master(codec, jack->nid); + return; + } + alc_exec_unsol_event(codec, action); }
/* call init functions of standard auto-mute helpers */ @@ -4408,8 +4444,18 @@ enum { ALC880_FIXUP_W810, ALC880_FIXUP_EAPD_COEF, ALC880_FIXUP_TCL_S700, + ALC880_FIXUP_VOL_KNOB, + ALC880_FIXUP_FUJITSU, };
+/* enable the volume-knob widget support on NID 0x21 */ +static void alc880_fixup_vol_knob(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + if (action == ALC_FIXUP_ACT_PROBE) + snd_hda_jack_detect_enable(codec, 0x21, ALC_DCVOL_EVENT); +} + static const struct alc_fixup alc880_fixups[] = { [ALC880_FIXUP_GPIO2] = { .type = ALC_FIXUP_VERBS, @@ -4465,6 +4511,30 @@ static const struct alc_fixup alc880_fixups[] = { .chained = true, .chain_id = ALC880_FIXUP_GPIO2, }, + [ALC880_FIXUP_VOL_KNOB] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc880_fixup_vol_knob, + }, + [ALC880_FIXUP_FUJITSU] = { + /* override all pins as BIOS on old Amilo is broken */ + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x01454140 }, /* SPDIF out */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { @@ -4472,6 +4542,8 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG),
Similar as the previous patch for model=fujitsu, we can now move the static quirk for F1734 to the auto-parser. The only difference is the default pin configurations: F1734 has less pins than Amilo's.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 24 ++++++++++++++++++++++++ 1 files changed, 24 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6a6436a..2d102f7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4446,6 +4446,7 @@ enum { ALC880_FIXUP_TCL_S700, ALC880_FIXUP_VOL_KNOB, ALC880_FIXUP_FUJITSU, + ALC880_FIXUP_F1734, };
/* enable the volume-knob widget support on NID 0x21 */ @@ -4535,14 +4536,37 @@ static const struct alc_fixup alc880_fixups[] = { .chained = true, .chain_id = ALC880_FIXUP_VOL_KNOB, }, + [ALC880_FIXUP_F1734] = { + /* almost compatible with FUJITSU, but no bass and SPDIF */ + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x411111f0 }, /* N/A */ + { 0x19, 0x01a19950 }, /* mic-in */ + { 0x1a, 0x411111f0 }, /* N/A */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_VOL_KNOB, + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG),
Uniwill p53 has a sane BIOS setup but just needs the volume-knob handling like Fujitsu laptops with ALC880.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2d102f7..3c0a46e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4562,6 +4562,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734),
The model=uniwill would work almost as is, but a couple of adjustments are needed to make the mutli-io working correctly. The headphone and speaker pins have to be marked properly in pin configs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3c0a46e..ff4410c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4447,6 +4447,7 @@ enum { ALC880_FIXUP_VOL_KNOB, ALC880_FIXUP_FUJITSU, ALC880_FIXUP_F1734, + ALC880_FIXUP_UNIWILL, };
/* enable the volume-knob widget support on NID 0x21 */ @@ -4556,12 +4557,23 @@ static const struct alc_fixup alc880_fixups[] = { .chained = true, .chain_id = ALC880_FIXUP_VOL_KNOB, }, + [ALC880_FIXUP_UNIWILL] = { + /* need to fix HP and speaker pins to be parsed correctly */ + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x0121411f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { } + }, + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM),
ALC880 model=uniwill-dig requires the fix-up of bogus BIOS pin default configurations. Other than that, it's pretty normal.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ff4410c..e88c753 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4448,6 +4448,7 @@ enum { ALC880_FIXUP_FUJITSU, ALC880_FIXUP_F1734, ALC880_FIXUP_UNIWILL, + ALC880_FIXUP_UNIWILL_DIG, };
/* enable the volume-knob widget support on NID 0x21 */ @@ -4567,11 +4568,23 @@ static const struct alc_fixup alc880_fixups[] = { { } }, }, + [ALC880_FIXUP_UNIWILL_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* disable bogus unused pins */ + { 0x17, 0x411111f0 }, + { 0x19, 0x411111f0 }, + { 0x1b, 0x411111f0 }, + { 0x1f, 0x411111f0 }, + { } + } + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), + SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734), SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL), SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB),
ASUS Z71V has a totally broken BIOS setup (at least the info I got), thus we need to override the whole pin-config table to make the auto-parser working correctly.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 20 ++++++++++++++++++++ 1 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e88c753..71acd9b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4449,6 +4449,7 @@ enum { ALC880_FIXUP_F1734, ALC880_FIXUP_UNIWILL, ALC880_FIXUP_UNIWILL_DIG, + ALC880_FIXUP_Z71V, };
/* enable the volume-knob widget support on NID 0x21 */ @@ -4579,10 +4580,29 @@ static const struct alc_fixup alc880_fixups[] = { { } } }, + [ALC880_FIXUP_Z71V] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + /* set up the whole pins as BIOS is utterly broken */ + { 0x14, 0x99030120 }, /* speaker */ + { 0x15, 0x0121411f }, /* HP */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19950 }, /* mic-in */ + { 0x19, 0x411111f0 }, /* N/A */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x411111f0 }, /* N/A */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + } + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734),
ASUS W1V has a sane pin-config table set by BIOS. The only missing piece is the setup of GPIO1.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 71acd9b..510ca92 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4438,6 +4438,7 @@ static const struct hda_amp_list alc880_loopbacks[] = { * ALC880 fix-ups */ enum { + ALC880_FIXUP_GPIO1, ALC880_FIXUP_GPIO2, ALC880_FIXUP_MEDION_RIM, ALC880_FIXUP_LG, @@ -4461,6 +4462,10 @@ static void alc880_fixup_vol_knob(struct hda_codec *codec, }
static const struct alc_fixup alc880_fixups[] = { + [ALC880_FIXUP_GPIO1] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio1_init_verbs, + }, [ALC880_FIXUP_GPIO2] = { .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, @@ -4602,6 +4607,7 @@ static const struct alc_fixup alc880_fixups[] = {
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_FIXUP_GPIO1), SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG),
It turned out that BIOS on most of ASUS mobo's set the pin-config tables reasonably well for the auto-parser. We'd need GPIO setups, but should work as is other than that.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 510ca92..fce31b0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4607,8 +4607,9 @@ static const struct alc_fixup alc880_fixups[] = {
static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810), - SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_FIXUP_GPIO1), SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V), + SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1), + SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF), SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG), SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734),
Finally the all static quirks for ALC880 are converted to the auto-parser. Since we are never sure whether the BIOS on so many old machines are really correct, the quirk table entries are copied as they are, but just providing the proper pin-config values accordingly.
Since alc880_quirks.c is removed, alc882_quirks.c has to be adjusted slightly to be built again. There might be some compile warnings due to the remaining alc882 quirks, but these shall be killed sooner or later, I don't care it much at this point.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 219 +++++++++++++++++++++++++++++++++-------- 1 files changed, 179 insertions(+), 40 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fce31b0..4ac1e38 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4451,6 +4451,15 @@ enum { ALC880_FIXUP_UNIWILL, ALC880_FIXUP_UNIWILL_DIG, ALC880_FIXUP_Z71V, + ALC880_FIXUP_3ST_BASE, + ALC880_FIXUP_3ST, + ALC880_FIXUP_3ST_DIG, + ALC880_FIXUP_5ST_BASE, + ALC880_FIXUP_5ST, + ALC880_FIXUP_5ST_DIG, + ALC880_FIXUP_6ST_BASE, + ALC880_FIXUP_6ST, + ALC880_FIXUP_6ST_DIG, };
/* enable the volume-knob widget support on NID 0x21 */ @@ -4603,6 +4612,114 @@ static const struct alc_fixup alc880_fixups[] = { { } } }, + [ALC880_FIXUP_3ST_BASE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x01014010 }, /* line-out */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_3ST] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_3ST_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_3ST_BASE, + }, + [ALC880_FIXUP_5ST_BASE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x411111f0 }, /* N/A */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01016412 }, /* surr */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x0121411f }, /* HP */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x02a19c40 }, /* front-mic */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_5ST] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_5ST_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_5ST_BASE, + }, + [ALC880_FIXUP_6ST_BASE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x01014010 }, /* front */ + { 0x15, 0x01016412 }, /* surr */ + { 0x16, 0x01011411 }, /* CLFE */ + { 0x17, 0x01012414 }, /* side */ + { 0x18, 0x01a19c30 }, /* mic-in */ + { 0x19, 0x02a19c40 }, /* front-mic */ + { 0x1a, 0x01813031 }, /* line-in */ + { 0x1b, 0x0121411f }, /* HP */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + /* 0x1e is filled in below */ + { 0x1f, 0x411111f0 }, /* N/A */ + { } + } + }, + [ALC880_FIXUP_6ST] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x411111f0 }, /* N/A */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, + [ALC880_FIXUP_6ST_DIG] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x1e, 0x0144111e }, /* SPDIF */ + { } + }, + .chained = true, + .chain_id = ALC880_FIXUP_6ST_BASE, + }, };
static const struct snd_pci_quirk alc880_fixup_tbl[] = { @@ -4625,6 +4742,60 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG), SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700), + + /* Below is the copied entries from alc880_quirks.c. + * It's not quite sure whether BIOS sets the correct pin-config table + * on these machines, thus they are kept to be compatible with + * the old static quirks. Once when it's confirmed to work without + * these overrides, it'd be better to remove. + */ + SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST), + SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST), + SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */ + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG), + /* default Intel */ + SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST), + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG), + {} +}; + +static const struct alc_model_fixup alc880_fixup_models[] = { + {.id = ALC880_FIXUP_3ST, .name = "3stack"}, + {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, + {.id = ALC880_FIXUP_5ST, .name = "5stack"}, + {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"}, + {.id = ALC880_FIXUP_6ST, .name = "6stack"}, + {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"}, {} };
@@ -4647,14 +4818,9 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { /* * OK, here we have finally the patch for ALC880 */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#include "alc880_quirks.c" -#endif - static int patch_alc880(struct hda_codec *codec) { struct alc_spec *spec; - int board_config; int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); @@ -4666,38 +4832,14 @@ static int patch_alc880(struct hda_codec *codec) spec->mixer_nid = 0x0b; spec->need_dac_fix = 1;
- board_config = alc_board_config(codec, ALC880_MODEL_LAST, - alc880_models, alc880_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC_MODEL_AUTO; - } - - if (board_config == ALC_MODEL_AUTO) { - alc_pick_fixup(codec, NULL, alc880_fixup_tbl, alc880_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } - - if (board_config == ALC_MODEL_AUTO) { - /* automatic parse from the BIOS config */ - err = alc880_parse_auto_config(codec); - if (err < 0) - goto error; -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS - else if (!err) { - printk(KERN_INFO - "hda_codec: Cannot set up configuration " - "from BIOS. Using 3-stack mode...\n"); - board_config = ALC880_3ST; - } -#endif - } + alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + alc880_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
- if (board_config != ALC_MODEL_AUTO) { - spec->vmaster_nid = 0x0c; - setup_preset(codec, &alc880_presets[board_config]); - } + /* automatic parse from the BIOS config */ + err = alc880_parse_auto_config(codec); + if (err < 0) + goto error;
if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); @@ -4716,10 +4858,7 @@ static int patch_alc880(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc_auto_init_std; - else - codec->patch_ops.build_controls = __alc_build_controls; + spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc880_loopbacks;
If anyone wants to debug the driver and avoid the existing fix-ups, pass model=nofixup option. Then the driver will skip to pick up the fixup list.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4ac1e38..3c6f5b5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1556,6 +1556,13 @@ static void alc_pick_fixup(struct hda_codec *codec, int id = -1; const char *name = NULL;
+ /* when model=nofixup is given, don't pick up any fixups */ + if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { + spec->fixup_list = NULL; + spec->fixup_id = -1; + return; + } + if (codec->modelname && models) { while (models->name) { if (!strcmp(codec->modelname, models->name)) {
Resitance is futile. The remaining static model quirks for Apple machines with ALC882-compatible codecs are converted to the auto-parser now. We can remove all alc*_quirks.c finally.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 143 ++++++++++++++++++++++++++--------------- 1 files changed, 92 insertions(+), 51 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3c6f5b5..c5216b5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4808,21 +4808,6 @@ static const struct alc_model_fixup alc880_fixup_models[] = {
/* - * board setups - */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#define alc_board_config \ - snd_hda_check_board_config -#define alc_board_codec_sid_config \ - snd_hda_check_board_codec_sid_config -#include "alc_quirks.c" -#else -#define alc_board_config(codec, nums, models, tbl) -1 -#define alc_board_codec_sid_config(codec, nums, models, tbl) -1 -#define setup_preset(codec, x) /* NOP */ -#endif - -/* * OK, here we have finally the patch for ALC880 */ static int patch_alc880(struct hda_codec *codec) @@ -5091,6 +5076,8 @@ enum { ALC882_FIXUP_EAPD, ALC883_FIXUP_EAPD, ALC883_FIXUP_ACER_EAPD, + ALC882_FIXUP_GPIO1, + ALC882_FIXUP_GPIO2, ALC882_FIXUP_GPIO3, ALC889_FIXUP_COEF, ALC882_FIXUP_ASUS_W2JC, @@ -5099,6 +5086,8 @@ enum { ALC882_FIXUP_ASPIRE_8930G_VERBS, ALC885_FIXUP_MACPRO_GPIO, ALC889_FIXUP_DAC_ROUTE, + ALC889_FIXUP_MBP_VREF, + ALC889_FIXUP_IMAC91_VREF, };
static void alc889_fixup_coef(struct hda_codec *codec, @@ -5169,6 +5158,51 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, } }
+/* Set VREF on HP pin */ +static void alc889_fixup_mbp_vref(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static hda_nid_t nids[2] = { 0x14, 0x15 }; + int i; + + if (action != ALC_FIXUP_ACT_INIT) + return; + for (i = 0; i < ARRAY_SIZE(nids); i++) { + unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); + if (get_defcfg_device(val) != AC_JACK_HP_OUT) + continue; + val = snd_hda_codec_read(codec, nids[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val |= AC_PINCTL_VREF_80; + snd_hda_codec_write(codec, nids[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + spec->keep_vref_in_automute = 1; + break; + } +} + +/* Set VREF on speaker pins on imac91 */ +static void alc889_fixup_imac91_vref(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static hda_nid_t nids[2] = { 0x18, 0x1a }; + int i; + + if (action != ALC_FIXUP_ACT_INIT) + return; + for (i = 0; i < ARRAY_SIZE(nids); i++) { + unsigned int val; + val = snd_hda_codec_read(codec, nids[i], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val |= AC_PINCTL_VREF_50; + snd_hda_codec_write(codec, nids[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); + } + spec->keep_vref_in_automute = 1; +} + static const struct alc_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { .type = ALC_FIXUP_PINS, @@ -5247,6 +5281,14 @@ static const struct alc_fixup alc882_fixups[] = { { } } }, + [ALC882_FIXUP_GPIO1] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio1_init_verbs, + }, + [ALC882_FIXUP_GPIO2] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = alc_gpio2_init_verbs, + }, [ALC882_FIXUP_GPIO3] = { .type = ALC_FIXUP_VERBS, .v.verbs = alc_gpio3_init_verbs, @@ -5320,6 +5362,18 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_FUNC, .v.func = alc889_fixup_dac_route, }, + [ALC889_FIXUP_MBP_VREF] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc889_fixup_mbp_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, + [ALC889_FIXUP_IMAC91_VREF] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc889_fixup_imac91_vref, + .chained = true, + .chain_id = ALC882_FIXUP_GPIO1, + }, };
static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -5353,11 +5407,26 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
/* All Apple entries are in codec SSIDs */ + SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_FIXUP_MACPRO_GPIO), SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO), SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD), + SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF), + SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF), SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO), + SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF), + SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), @@ -5382,14 +5451,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec)
/* */ -#ifdef CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS -#include "alc882_quirks.c" -#endif - static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; - int err, board_config; + int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -5413,36 +5478,15 @@ static int patch_alc882(struct hda_codec *codec) if (err < 0) goto error;
- board_config = alc_board_config(codec, ALC882_MODEL_LAST, - alc882_models, NULL); - if (board_config < 0) - board_config = alc_board_codec_sid_config(codec, - ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl); - - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = ALC_MODEL_AUTO; - } - - if (board_config == ALC_MODEL_AUTO) { - alc_pick_fixup(codec, NULL, alc882_fixup_tbl, alc882_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); - } + alc_pick_fixup(codec, NULL, alc882_fixup_tbl, alc882_fixups); + alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
- if (board_config == ALC_MODEL_AUTO) { - /* automatic parse from the BIOS config */ - err = alc882_parse_auto_config(codec); - if (err < 0) - goto error; - } - - if (board_config != ALC_MODEL_AUTO) { - setup_preset(codec, &alc882_presets[board_config]); - spec->vmaster_nid = 0x0c; - } + /* automatic parse from the BIOS config */ + err = alc882_parse_auto_config(codec); + if (err < 0) + goto error;
if (!spec->no_analog && !spec->adc_nids) { alc_auto_fill_adc_caps(codec); @@ -5461,10 +5505,7 @@ static int patch_alc882(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - if (board_config == ALC_MODEL_AUTO) - spec->init_hook = alc_auto_init_std; - else - codec->patch_ops.build_controls = __alc_build_controls; + spec->init_hook = alc_auto_init_std;
#ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist)
Similarly in patch_via.c, parse the active analog-loopback connections and create a list dynamically rather than static arrays.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 104 +++++++++-------------------------------- 1 files changed, 22 insertions(+), 82 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c5216b5..eba50df 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -200,6 +200,8 @@ struct alc_spec { hda_nid_t vmaster_nid; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; #endif
/* for PLL fix */ @@ -2690,6 +2692,25 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return channel_name[ch]; }
+#ifdef CONFIG_SND_HDA_POWER_SAVE +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct alc_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; +} +#else +#define add_loopback_list(spec, mix, idx) /* NOP */ +#endif + /* create input playback/capture controls for the given pin */ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname, int ctlidx, @@ -2705,6 +2726,7 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) return err; + add_loopback_list(spec, mix_nid, idx); return 0; }
@@ -4430,17 +4452,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec) return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids); }
-#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc880_loopbacks[] = { - { 0x0b, HDA_INPUT, 0 }, - { 0x0b, HDA_INPUT, 1 }, - { 0x0b, HDA_INPUT, 2 }, - { 0x0b, HDA_INPUT, 3 }, - { 0x0b, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - /* * ALC880 fix-ups */ @@ -4851,10 +4862,6 @@ static int patch_alc880(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops; spec->init_hook = alc_auto_init_std; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc880_loopbacks; -#endif
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
@@ -4876,17 +4883,6 @@ static int alc260_parse_auto_config(struct hda_codec *codec) return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); }
-#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc260_loopbacks[] = { - { 0x07, HDA_INPUT, 0 }, - { 0x07, HDA_INPUT, 1 }, - { 0x07, HDA_INPUT, 2 }, - { 0x07, HDA_INPUT, 3 }, - { 0x07, HDA_INPUT, 4 }, - { } /* end */ -}; -#endif - /* * Pin config fixes */ @@ -5032,10 +5028,6 @@ static int patch_alc260(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc260_loopbacks; -#endif
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
@@ -5058,9 +5050,6 @@ static int patch_alc260(struct hda_codec *codec) * In addition, an independent DAC for the multi-playback (not used in this * driver yet). */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc882_loopbacks alc880_loopbacks -#endif
/* * Pin config fixes @@ -5507,11 +5496,6 @@ static int patch_alc882(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->init_hook = alc_auto_init_std;
-#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc882_loopbacks; -#endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0; @@ -5608,10 +5592,6 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { };
-#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc262_loopbacks alc880_loopbacks -#endif - /* */ static int patch_alc262(struct hda_codec *codec) @@ -5671,11 +5651,6 @@ static int patch_alc262(struct hda_codec *codec) spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup;
-#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc262_loopbacks; -#endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0; @@ -5793,10 +5768,6 @@ static int patch_alc268(struct hda_codec *codec) /* * ALC269 */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc269_loopbacks alc880_loopbacks -#endif - static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -6336,8 +6307,6 @@ static int patch_alc269(struct hda_codec *codec) spec->shutup = alc269_shutup;
#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc269_loopbacks; if (alc269_mic2_for_mute_led(codec)) codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps; #endif @@ -6362,17 +6331,6 @@ static int alc861_parse_auto_config(struct hda_codec *codec) return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids); }
-#ifdef CONFIG_SND_HDA_POWER_SAVE -static const struct hda_amp_list alc861_loopbacks[] = { - { 0x15, HDA_INPUT, 0 }, - { 0x15, HDA_INPUT, 1 }, - { 0x15, HDA_INPUT, 2 }, - { 0x15, HDA_INPUT, 3 }, - { } /* end */ -}; -#endif - - /* Pin config fixes */ enum { ALC861_FIXUP_FSC_AMILO_PI1505, @@ -6486,8 +6444,6 @@ static int patch_alc861(struct hda_codec *codec) spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->power_hook = alc_power_eapd; - if (!spec->loopback.amplist) - spec->loopback.amplist = alc861_loopbacks; #endif
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -6506,10 +6462,6 @@ static int patch_alc861(struct hda_codec *codec) * * In addition, an independent DAC */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc861vd_loopbacks alc880_loopbacks -#endif - static int alc861vd_parse_auto_config(struct hda_codec *codec) { static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 }; @@ -6610,10 +6562,6 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup; -#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc861vd_loopbacks; -#endif
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
@@ -6635,9 +6583,6 @@ static int patch_alc861vd(struct hda_codec *codec) * In addition, an independent DAC for the multi-playback (not used in this * driver yet). */ -#ifdef CONFIG_SND_HDA_POWER_SAVE -#define alc662_loopbacks alc880_loopbacks -#endif
/* * BIOS auto configuration @@ -6999,11 +6944,6 @@ static int patch_alc662(struct hda_codec *codec) spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup;
-#ifdef CONFIG_SND_HDA_POWER_SAVE - if (!spec->loopback.amplist) - spec->loopback.amplist = alc662_loopbacks; -#endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
When BIOS is damn crazy and gives no pin-config at all, the driver might lead to a NULL dereference. Let's add a NULL check for such a case.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eba50df..997cc81 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -302,6 +302,9 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, int i, type, num_conns; hda_nid_t nid;
+ if (!spec->input_mux) + return 0; + mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; imux = &spec->input_mux[mux_idx]; if (!imux->num_items && mux_idx > 0)
Even if the outputs are using shared DACs, we can still create individual mute siwtches since they are assigned per pin. This allows to create, e.g. Speaker and Bass Speaker mute switches while the single volume is used for these outputs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 50 ++++++++++++++++------------------------ 1 files changed, 20 insertions(+), 30 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 997cc81..3cedb26 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3717,41 +3717,31 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0); }
- if (dacs[num_pins - 1]) { - /* OK, we have a multi-output system with individual volumes */ - for (i = 0; i < num_pins; i++) { - if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", - pfx, channel_name[i]); - err = alc_auto_create_extra_out(codec, pins[i], dacs[i], - name, 0); - } else { - err = alc_auto_create_extra_out(codec, pins[i], dacs[i], - pfx, i); - } - if (err < 0) - return err; - } - return 0; - } - - /* Let's create a bind-controls */ - ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_sw); - if (!ctl) - return -ENOMEM; - n = 0; for (i = 0; i < num_pins; i++) { - if (get_wcaps(codec, pins[i]) & AC_WCAP_OUT_AMP) - ctl->values[n++] = - HDA_COMPOSE_AMP_VAL(pins[i], 3, 0, HDA_OUTPUT); - } - if (n) { - snprintf(name, sizeof(name), "%s Playback Switch", pfx); - err = add_control(spec, ALC_CTL_BIND_SW, name, 0, (long)ctl); + hda_nid_t dac; + if (dacs[num_pins - 1]) + dac = dacs[i]; /* with individual volumes */ + else + dac = 0; + if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { + err = alc_auto_create_extra_out(codec, pins[i], dac, + "Bass Speaker", 0); + } else if (num_pins >= 3) { + snprintf(name, sizeof(name), "%s %s", + pfx, channel_name[i]); + err = alc_auto_create_extra_out(codec, pins[i], dac, + name, 0); + } else { + err = alc_auto_create_extra_out(codec, pins[i], dac, + pfx, i); + } if (err < 0) return err; } + if (dacs[num_pins - 1]) + return 0;
+ /* Let's create a bind-controls for volumes */ ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); if (!ctl) return -ENOMEM;
When the multi-io is added to the two speaker output configuration, the parser would try to add yet another "Bass Speaker" control since it checks only cfg->line_outs. Add a workaround for it by simply passing the channel name in the case of multi-io outputs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3cedb26..e5c0459 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3613,14 +3613,17 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, dac = spec->multiout.dac_nids[i]; if (!dac) continue; - if (i >= cfg->line_outs) + if (i >= cfg->line_outs) { pin = spec->multi_io[i - 1].pin; - else + index = 0; + name = channel_name[i]; + } else { pin = cfg->line_out_pins[i]; + name = alc_get_line_out_pfx(spec, i, true, &index); + }
sw = alc_look_for_out_mute_nid(codec, pin, dac); vol = alc_look_for_out_vol_nid(codec, pin, dac); - name = alc_get_line_out_pfx(spec, i, true, &index); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1);
When the machine has two speakers but wants to put more multi-io jacks, the parser shouldn't consider about the shared DAC but try to assign the individual DACs. Otherwise the channel mapping would be fairly confused and lead to the wrong DACs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e5c0459..e82911a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3353,7 +3353,11 @@ static int fill_and_eval_dacs(struct hda_codec *codec, } if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { /* try multi-ios with HP + inputs */ - err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, 1); + int offset = 0; + if (cfg->line_outs >= 3) + offset = 1; + err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, + offset); if (err < 0) return err; badness += err;
A few clean-ups for post-static-quirk time: - Call alc_auto_init_std() statically in alc_init() instead of setting spec->init_hook in each caller. spec->init_hook field is left unused for any future use.
- Move the call of set_capture_mixer() to to alc_parse_auto_config() instead of each caller.
- Get rid of the ADC-filling and imux check in each parser function. This is no longer needed since the auto-parser always check ADCs and imux. It was only for the static quirks.
- Kill unused defines
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 140 ++--------------------------------------- 1 files changed, 5 insertions(+), 135 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e82911a..e142f6f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -222,8 +222,6 @@ struct alc_spec { struct snd_array bind_ctls; };
-#define ALC_MODEL_AUTO 0 /* common for all chips */ - static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int bits) { @@ -1074,45 +1072,6 @@ static bool alc_check_dyn_adc_switch(struct hda_codec *codec) return true; }
-/* rebuild imux for matching with the given auto-mic pins (if not yet) */ -static bool alc_rebuild_imux_for_auto_mic(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux; - static char * const texts[3] = { - "Mic", "Internal Mic", "Dock Mic" - }; - int i; - - if (!spec->auto_mic) - return false; - imux = &spec->private_imux[0]; - if (spec->input_mux == imux) - return true; - spec->imux_pins[0] = spec->ext_mic_pin; - spec->imux_pins[1] = spec->int_mic_pin; - spec->imux_pins[2] = spec->dock_mic_pin; - for (i = 0; i < 3; i++) { - strcpy(imux->items[i].label, texts[i]); - if (spec->imux_pins[i]) { - hda_nid_t pin = spec->imux_pins[i]; - int c; - for (c = 0; c < spec->num_adc_nids; c++) { - hda_nid_t cap = get_capsrc(spec, c); - int idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - imux->items[i].index = idx; - break; - } - } - imux->num_items = i + 1; - } - } - spec->num_mux_defs = 1; - spec->input_mux = imux; - return true; -} - /* check whether all auto-mic pins are valid; setup indices if OK */ static bool alc_auto_mic_check_imux(struct hda_codec *codec) { @@ -2092,6 +2051,7 @@ static int alc_build_controls(struct hda_codec *codec) */
static void alc_init_special_input_src(struct hda_codec *codec); +static void alc_auto_init_std(struct hda_codec *codec);
static int alc_init(struct hda_codec *codec) { @@ -2104,6 +2064,7 @@ static int alc_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); alc_init_special_input_src(codec); + alc_auto_init_std(codec);
if (spec->init_hook) spec->init_hook(codec); @@ -4442,6 +4403,9 @@ static int alc_parse_auto_config(struct hda_codec *codec, if (spec->kctls.list) add_mixer(spec, spec->kctls.list);
+ if (!spec->no_analog && !spec->cap_mixer) + set_capture_mixer(codec); + return 1; }
@@ -4844,15 +4808,6 @@ static int patch_alc880(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -4861,7 +4816,6 @@ static int patch_alc880(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std;
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
@@ -5009,15 +4963,6 @@ static int patch_alc260(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -5026,7 +4971,6 @@ static int patch_alc260(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup;
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -5477,15 +5421,6 @@ static int patch_alc882(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -5494,7 +5429,6 @@ static int patch_alc882(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std;
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
@@ -5631,15 +5565,6 @@ static int patch_alc262(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -5648,7 +5573,6 @@ static int patch_alc262(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup;
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -5745,17 +5669,7 @@ static int patch_alc268(struct hda_codec *codec) (0 << AC_AMPCAP_MUTE_SHIFT)); }
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup;
return 0; @@ -6283,15 +6197,6 @@ static int patch_alc269(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -6303,7 +6208,6 @@ static int patch_alc269(struct hda_codec *codec) #ifdef CONFIG_PM codec->patch_ops.resume = alc269_resume; #endif - spec->init_hook = alc_auto_init_std; spec->shutup = alc269_shutup;
#ifdef CONFIG_SND_HDA_POWER_SAVE @@ -6424,15 +6328,6 @@ static int patch_alc861(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) @@ -6441,7 +6336,6 @@ static int patch_alc861(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; #ifdef CONFIG_SND_HDA_POWER_SAVE spec->power_hook = alc_power_eapd; #endif @@ -6542,15 +6436,6 @@ static int patch_alc861vd(struct hda_codec *codec) add_verb(spec, alc660vd_eapd_verbs); }
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) @@ -6560,7 +6445,6 @@ static int patch_alc861vd(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
- spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup;
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -6912,15 +6796,6 @@ static int patch_alc662(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && !spec->adc_nids) { - alc_auto_fill_adc_caps(codec); - alc_rebuild_imux_for_auto_mic(codec); - alc_remove_invalid_adc_nids(codec); - } - - if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - if (!spec->no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) @@ -6941,7 +6816,6 @@ static int patch_alc662(struct hda_codec *codec) }
codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std; spec->shutup = alc_eapd_shutup;
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); @@ -6984,11 +6858,7 @@ static int patch_alc680(struct hda_codec *codec) return err; }
- if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - codec->patch_ops = alc_patch_ops; - spec->init_hook = alc_auto_init_std;
return 0; }
On 02/24/2012 05:35 PM, Takashi Iwai wrote:
Hi,
for 3.4 kernel, finally I got rid of all static quirks from HD-audio Realtek codec parser. This means that the all necessary quirks have been converted to alc_fixup either the pin-config overrides, the additional verbs, or the extra init functions.
Since some machines have really weird pin mappings, I introduced the new mechanism in the parser to evaluate the best DAC->pin connections, which is based on the badness calculation. You won't notice on most of machines but some machines that have fewer DACs than necessary output pins will get different mappings by this change.
The whole changes can be seen in sound git tree topic/hda branch, as usual. git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
Now it's merged to main branch, so the snapshot tarball contains these changes, too.
The rest of the thread will contain the changes in patch_realtek.c. It's just for refernces, and I omitted the other files. For the complete changes, take a look at the git tree.
Hi Takashi,
I think this is a very good move. Unfortunately I don't have time to review all 38 patches thoroughly, but if there is anything in particular you would like a second pair of eyes on, let me know.
participants (2)
-
David Henningsson
-
Takashi Iwai