| // SPDX-License-Identifier: GPL-2.0-or-later |
| // |
| // Realtek ALC260 codec |
| // |
| |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include "realtek.h" |
| |
| static int alc260_parse_auto_config(struct hda_codec *codec) |
| { |
| static const hda_nid_t alc260_ignore[] = { 0x17, 0 }; |
| static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 }; |
| return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids); |
| } |
| |
| /* |
| * Pin config fixes |
| */ |
| enum { |
| ALC260_FIXUP_HP_DC5750, |
| ALC260_FIXUP_HP_PIN_0F, |
| ALC260_FIXUP_COEF, |
| ALC260_FIXUP_GPIO1, |
| ALC260_FIXUP_GPIO1_TOGGLE, |
| ALC260_FIXUP_REPLACER, |
| ALC260_FIXUP_HP_B1900, |
| ALC260_FIXUP_KN1, |
| ALC260_FIXUP_FSC_S7020, |
| ALC260_FIXUP_FSC_S7020_JWSE, |
| ALC260_FIXUP_VAIO_PINS, |
| }; |
| |
| static void alc260_gpio1_automute(struct hda_codec *codec) |
| { |
| struct alc_spec *spec = codec->spec; |
| |
| alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present); |
| } |
| |
| static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, |
| const struct hda_fixup *fix, int action) |
| { |
| struct alc_spec *spec = codec->spec; |
| if (action == HDA_FIXUP_ACT_PROBE) { |
| /* although the machine has only one output pin, we need to |
| * toggle GPIO1 according to the jack state |
| */ |
| spec->gen.automute_hook = alc260_gpio1_automute; |
| spec->gen.detect_hp = 1; |
| spec->gen.automute_speaker = 1; |
| spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ |
| snd_hda_jack_detect_enable_callback(codec, 0x0f, |
| snd_hda_gen_hp_automute); |
| alc_setup_gpio(codec, 0x01); |
| } |
| } |
| |
| static void alc260_fixup_kn1(struct hda_codec *codec, |
| const struct hda_fixup *fix, int action) |
| { |
| struct alc_spec *spec = codec->spec; |
| static const struct hda_pintbl pincfgs[] = { |
| { 0x0f, 0x02214000 }, /* HP/speaker */ |
| { 0x12, 0x90a60160 }, /* int mic */ |
| { 0x13, 0x02a19000 }, /* ext mic */ |
| { 0x18, 0x01446000 }, /* SPDIF out */ |
| /* disable bogus I/O pins */ |
| { 0x10, 0x411111f0 }, |
| { 0x11, 0x411111f0 }, |
| { 0x14, 0x411111f0 }, |
| { 0x15, 0x411111f0 }, |
| { 0x16, 0x411111f0 }, |
| { 0x17, 0x411111f0 }, |
| { 0x19, 0x411111f0 }, |
| { } |
| }; |
| |
| switch (action) { |
| case HDA_FIXUP_ACT_PRE_PROBE: |
| snd_hda_apply_pincfgs(codec, pincfgs); |
| spec->init_amp = ALC_INIT_NONE; |
| break; |
| } |
| } |
| |
| static void alc260_fixup_fsc_s7020(struct hda_codec *codec, |
| const struct hda_fixup *fix, int action) |
| { |
| struct alc_spec *spec = codec->spec; |
| if (action == HDA_FIXUP_ACT_PRE_PROBE) |
| spec->init_amp = ALC_INIT_NONE; |
| } |
| |
| static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec, |
| const struct hda_fixup *fix, int action) |
| { |
| struct alc_spec *spec = codec->spec; |
| if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
| spec->gen.add_jack_modes = 1; |
| spec->gen.hp_mic = 1; |
| } |
| } |
| |
| static const struct hda_fixup alc260_fixups[] = { |
| [ALC260_FIXUP_HP_DC5750] = { |
| .type = HDA_FIXUP_PINS, |
| .v.pins = (const struct hda_pintbl[]) { |
| { 0x11, 0x90130110 }, /* speaker */ |
| { } |
| } |
| }, |
| [ALC260_FIXUP_HP_PIN_0F] = { |
| .type = HDA_FIXUP_PINS, |
| .v.pins = (const struct hda_pintbl[]) { |
| { 0x0f, 0x01214000 }, /* HP */ |
| { } |
| } |
| }, |
| [ALC260_FIXUP_COEF] = { |
| .type = HDA_FIXUP_VERBS, |
| .v.verbs = (const struct hda_verb[]) { |
| { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, |
| { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 }, |
| { } |
| }, |
| }, |
| [ALC260_FIXUP_GPIO1] = { |
| .type = HDA_FIXUP_FUNC, |
| .v.func = alc_fixup_gpio1, |
| }, |
| [ALC260_FIXUP_GPIO1_TOGGLE] = { |
| .type = HDA_FIXUP_FUNC, |
| .v.func = alc260_fixup_gpio1_toggle, |
| .chained = true, |
| .chain_id = ALC260_FIXUP_HP_PIN_0F, |
| }, |
| [ALC260_FIXUP_REPLACER] = { |
| .type = HDA_FIXUP_VERBS, |
| .v.verbs = (const struct hda_verb[]) { |
| { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 }, |
| { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 }, |
| { } |
| }, |
| .chained = true, |
| .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, |
| }, |
| [ALC260_FIXUP_HP_B1900] = { |
| .type = HDA_FIXUP_FUNC, |
| .v.func = alc260_fixup_gpio1_toggle, |
| .chained = true, |
| .chain_id = ALC260_FIXUP_COEF, |
| }, |
| [ALC260_FIXUP_KN1] = { |
| .type = HDA_FIXUP_FUNC, |
| .v.func = alc260_fixup_kn1, |
| }, |
| [ALC260_FIXUP_FSC_S7020] = { |
| .type = HDA_FIXUP_FUNC, |
| .v.func = alc260_fixup_fsc_s7020, |
| }, |
| [ALC260_FIXUP_FSC_S7020_JWSE] = { |
| .type = HDA_FIXUP_FUNC, |
| .v.func = alc260_fixup_fsc_s7020_jwse, |
| .chained = true, |
| .chain_id = ALC260_FIXUP_FSC_S7020, |
| }, |
| [ALC260_FIXUP_VAIO_PINS] = { |
| .type = HDA_FIXUP_PINS, |
| .v.pins = (const struct hda_pintbl[]) { |
| /* Pin configs are missing completely on some VAIOs */ |
| { 0x0f, 0x01211020 }, |
| { 0x10, 0x0001003f }, |
| { 0x11, 0x411111f0 }, |
| { 0x12, 0x01a15930 }, |
| { 0x13, 0x411111f0 }, |
| { 0x14, 0x411111f0 }, |
| { 0x15, 0x411111f0 }, |
| { 0x16, 0x411111f0 }, |
| { 0x17, 0x411111f0 }, |
| { 0x18, 0x411111f0 }, |
| { 0x19, 0x411111f0 }, |
| { } |
| } |
| }, |
| }; |
| |
| static const struct hda_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(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), |
| SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS), |
| SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F), |
| SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), |
| SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), |
| SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), |
| SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), |
| SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF), |
| {} |
| }; |
| |
| static const struct hda_model_fixup alc260_fixup_models[] = { |
| {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, |
| {.id = ALC260_FIXUP_COEF, .name = "coef"}, |
| {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, |
| {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, |
| {} |
| }; |
| |
| /* |
| */ |
| static int alc260_probe(struct hda_codec *codec, const struct hda_device_id *id) |
| { |
| struct alc_spec *spec; |
| int err; |
| |
| err = alc_alloc_spec(codec, 0x07); |
| if (err < 0) |
| return err; |
| |
| spec = codec->spec; |
| /* as quite a few machines require HP amp for speaker outputs, |
| * it's easier to enable it unconditionally; even if it's unneeded, |
| * it's almost harmless. |
| */ |
| spec->gen.prefer_hp_amp = 1; |
| spec->gen.beep_nid = 0x01; |
| |
| spec->shutup = alc_eapd_shutup; |
| |
| alc_pre_init(codec); |
| |
| snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, |
| alc260_fixups); |
| snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); |
| |
| /* automatic parse from the BIOS config */ |
| err = alc260_parse_auto_config(codec); |
| if (err < 0) |
| goto error; |
| |
| if (!spec->gen.no_analog) { |
| err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); |
| if (err < 0) |
| goto error; |
| } |
| |
| snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); |
| |
| return 0; |
| |
| error: |
| snd_hda_gen_remove(codec); |
| return err; |
| } |
| |
| static const struct hda_codec_ops alc260_codec_ops = { |
| .probe = alc260_probe, |
| .remove = snd_hda_gen_remove, |
| .build_controls = alc_build_controls, |
| .build_pcms = snd_hda_gen_build_pcms, |
| .init = alc_init, |
| .unsol_event = snd_hda_jack_unsol_event, |
| .resume = alc_resume, |
| .suspend = alc_suspend, |
| .check_power_status = snd_hda_gen_check_power_status, |
| .stream_pm = snd_hda_gen_stream_pm, |
| }; |
| |
| /* |
| * driver entries |
| */ |
| static const struct hda_device_id snd_hda_id_alc260[] = { |
| HDA_CODEC_ID(0x10ec0260, "ALC260"), |
| {} /* terminator */ |
| }; |
| MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc260); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("Realtek ALC260 HD-audio codec"); |
| MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); |
| |
| static struct hda_codec_driver alc260_driver = { |
| .id = snd_hda_id_alc260, |
| .ops = &alc260_codec_ops, |
| }; |
| |
| module_hda_codec_driver(alc260_driver); |