1*aeeb85f2STakashi Iwai // SPDX-License-Identifier: GPL-2.0-or-later 2*aeeb85f2STakashi Iwai 3*aeeb85f2STakashi Iwai #include <linux/init.h> 4*aeeb85f2STakashi Iwai #include <linux/module.h> 5*aeeb85f2STakashi Iwai #include "realtek.h" 6*aeeb85f2STakashi Iwai 7*aeeb85f2STakashi Iwai /* bind Beep switches of both NID 0x0f and 0x10 */ 8*aeeb85f2STakashi Iwai static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol, 9*aeeb85f2STakashi Iwai struct snd_ctl_elem_value *ucontrol) 10*aeeb85f2STakashi Iwai { 11*aeeb85f2STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 12*aeeb85f2STakashi Iwai unsigned long pval; 13*aeeb85f2STakashi Iwai int err; 14*aeeb85f2STakashi Iwai 15*aeeb85f2STakashi Iwai mutex_lock(&codec->control_mutex); 16*aeeb85f2STakashi Iwai pval = kcontrol->private_value; 17*aeeb85f2STakashi Iwai kcontrol->private_value = (pval & ~0xff) | 0x0f; 18*aeeb85f2STakashi Iwai err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 19*aeeb85f2STakashi Iwai if (err >= 0) { 20*aeeb85f2STakashi Iwai kcontrol->private_value = (pval & ~0xff) | 0x10; 21*aeeb85f2STakashi Iwai err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 22*aeeb85f2STakashi Iwai } 23*aeeb85f2STakashi Iwai kcontrol->private_value = pval; 24*aeeb85f2STakashi Iwai mutex_unlock(&codec->control_mutex); 25*aeeb85f2STakashi Iwai return err; 26*aeeb85f2STakashi Iwai } 27*aeeb85f2STakashi Iwai 28*aeeb85f2STakashi Iwai static const struct snd_kcontrol_new alc268_beep_mixer[] = { 29*aeeb85f2STakashi Iwai HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT), 30*aeeb85f2STakashi Iwai { 31*aeeb85f2STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 32*aeeb85f2STakashi Iwai .name = "Beep Playback Switch", 33*aeeb85f2STakashi Iwai .subdevice = HDA_SUBDEV_AMP_FLAG, 34*aeeb85f2STakashi Iwai .info = snd_hda_mixer_amp_switch_info, 35*aeeb85f2STakashi Iwai .get = snd_hda_mixer_amp_switch_get, 36*aeeb85f2STakashi Iwai .put = alc268_beep_switch_put, 37*aeeb85f2STakashi Iwai .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT) 38*aeeb85f2STakashi Iwai }, 39*aeeb85f2STakashi Iwai }; 40*aeeb85f2STakashi Iwai 41*aeeb85f2STakashi Iwai /* set PCBEEP vol = 0, mute connections */ 42*aeeb85f2STakashi Iwai static const struct hda_verb alc268_beep_init_verbs[] = { 43*aeeb85f2STakashi Iwai {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 44*aeeb85f2STakashi Iwai {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 45*aeeb85f2STakashi Iwai {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 46*aeeb85f2STakashi Iwai { } 47*aeeb85f2STakashi Iwai }; 48*aeeb85f2STakashi Iwai 49*aeeb85f2STakashi Iwai enum { 50*aeeb85f2STakashi Iwai ALC268_FIXUP_INV_DMIC, 51*aeeb85f2STakashi Iwai ALC268_FIXUP_HP_EAPD, 52*aeeb85f2STakashi Iwai ALC268_FIXUP_SPDIF, 53*aeeb85f2STakashi Iwai }; 54*aeeb85f2STakashi Iwai 55*aeeb85f2STakashi Iwai static const struct hda_fixup alc268_fixups[] = { 56*aeeb85f2STakashi Iwai [ALC268_FIXUP_INV_DMIC] = { 57*aeeb85f2STakashi Iwai .type = HDA_FIXUP_FUNC, 58*aeeb85f2STakashi Iwai .v.func = alc_fixup_inv_dmic, 59*aeeb85f2STakashi Iwai }, 60*aeeb85f2STakashi Iwai [ALC268_FIXUP_HP_EAPD] = { 61*aeeb85f2STakashi Iwai .type = HDA_FIXUP_VERBS, 62*aeeb85f2STakashi Iwai .v.verbs = (const struct hda_verb[]) { 63*aeeb85f2STakashi Iwai {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, 64*aeeb85f2STakashi Iwai {} 65*aeeb85f2STakashi Iwai } 66*aeeb85f2STakashi Iwai }, 67*aeeb85f2STakashi Iwai [ALC268_FIXUP_SPDIF] = { 68*aeeb85f2STakashi Iwai .type = HDA_FIXUP_PINS, 69*aeeb85f2STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 70*aeeb85f2STakashi Iwai { 0x1e, 0x014b1180 }, /* enable SPDIF out */ 71*aeeb85f2STakashi Iwai {} 72*aeeb85f2STakashi Iwai } 73*aeeb85f2STakashi Iwai }, 74*aeeb85f2STakashi Iwai }; 75*aeeb85f2STakashi Iwai 76*aeeb85f2STakashi Iwai static const struct hda_model_fixup alc268_fixup_models[] = { 77*aeeb85f2STakashi Iwai {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, 78*aeeb85f2STakashi Iwai {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, 79*aeeb85f2STakashi Iwai {.id = ALC268_FIXUP_SPDIF, .name = "spdif"}, 80*aeeb85f2STakashi Iwai {} 81*aeeb85f2STakashi Iwai }; 82*aeeb85f2STakashi Iwai 83*aeeb85f2STakashi Iwai static const struct hda_quirk alc268_fixup_tbl[] = { 84*aeeb85f2STakashi Iwai SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF), 85*aeeb85f2STakashi Iwai SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC), 86*aeeb85f2STakashi Iwai /* below is codec SSID since multiple Toshiba laptops have the 87*aeeb85f2STakashi Iwai * same PCI SSID 1179:ff00 88*aeeb85f2STakashi Iwai */ 89*aeeb85f2STakashi Iwai SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD), 90*aeeb85f2STakashi Iwai {} 91*aeeb85f2STakashi Iwai }; 92*aeeb85f2STakashi Iwai 93*aeeb85f2STakashi Iwai /* 94*aeeb85f2STakashi Iwai * BIOS auto configuration 95*aeeb85f2STakashi Iwai */ 96*aeeb85f2STakashi Iwai static int alc268_parse_auto_config(struct hda_codec *codec) 97*aeeb85f2STakashi Iwai { 98*aeeb85f2STakashi Iwai static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; 99*aeeb85f2STakashi Iwai return alc_parse_auto_config(codec, NULL, alc268_ssids); 100*aeeb85f2STakashi Iwai } 101*aeeb85f2STakashi Iwai 102*aeeb85f2STakashi Iwai /* 103*aeeb85f2STakashi Iwai */ 104*aeeb85f2STakashi Iwai static int patch_alc268(struct hda_codec *codec) 105*aeeb85f2STakashi Iwai { 106*aeeb85f2STakashi Iwai struct alc_spec *spec; 107*aeeb85f2STakashi Iwai int i, err; 108*aeeb85f2STakashi Iwai 109*aeeb85f2STakashi Iwai /* ALC268 has no aa-loopback mixer */ 110*aeeb85f2STakashi Iwai err = alc_alloc_spec(codec, 0); 111*aeeb85f2STakashi Iwai if (err < 0) 112*aeeb85f2STakashi Iwai return err; 113*aeeb85f2STakashi Iwai 114*aeeb85f2STakashi Iwai spec = codec->spec; 115*aeeb85f2STakashi Iwai if (has_cdefine_beep(codec)) 116*aeeb85f2STakashi Iwai spec->gen.beep_nid = 0x01; 117*aeeb85f2STakashi Iwai 118*aeeb85f2STakashi Iwai spec->shutup = alc_eapd_shutup; 119*aeeb85f2STakashi Iwai 120*aeeb85f2STakashi Iwai alc_pre_init(codec); 121*aeeb85f2STakashi Iwai 122*aeeb85f2STakashi Iwai snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); 123*aeeb85f2STakashi Iwai snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 124*aeeb85f2STakashi Iwai 125*aeeb85f2STakashi Iwai /* automatic parse from the BIOS config */ 126*aeeb85f2STakashi Iwai err = alc268_parse_auto_config(codec); 127*aeeb85f2STakashi Iwai if (err < 0) 128*aeeb85f2STakashi Iwai goto error; 129*aeeb85f2STakashi Iwai 130*aeeb85f2STakashi Iwai if (err > 0 && !spec->gen.no_analog && 131*aeeb85f2STakashi Iwai spec->gen.autocfg.speaker_pins[0] != 0x1d) { 132*aeeb85f2STakashi Iwai for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) { 133*aeeb85f2STakashi Iwai if (!snd_hda_gen_add_kctl(&spec->gen, NULL, 134*aeeb85f2STakashi Iwai &alc268_beep_mixer[i])) { 135*aeeb85f2STakashi Iwai err = -ENOMEM; 136*aeeb85f2STakashi Iwai goto error; 137*aeeb85f2STakashi Iwai } 138*aeeb85f2STakashi Iwai } 139*aeeb85f2STakashi Iwai snd_hda_add_verbs(codec, alc268_beep_init_verbs); 140*aeeb85f2STakashi Iwai if (!query_amp_caps(codec, 0x1d, HDA_INPUT)) 141*aeeb85f2STakashi Iwai /* override the amp caps for beep generator */ 142*aeeb85f2STakashi Iwai snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, 143*aeeb85f2STakashi Iwai (0x0c << AC_AMPCAP_OFFSET_SHIFT) | 144*aeeb85f2STakashi Iwai (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) | 145*aeeb85f2STakashi Iwai (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) | 146*aeeb85f2STakashi Iwai (0 << AC_AMPCAP_MUTE_SHIFT)); 147*aeeb85f2STakashi Iwai } 148*aeeb85f2STakashi Iwai 149*aeeb85f2STakashi Iwai snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 150*aeeb85f2STakashi Iwai 151*aeeb85f2STakashi Iwai return 0; 152*aeeb85f2STakashi Iwai 153*aeeb85f2STakashi Iwai error: 154*aeeb85f2STakashi Iwai alc_free(codec); 155*aeeb85f2STakashi Iwai return err; 156*aeeb85f2STakashi Iwai } 157*aeeb85f2STakashi Iwai 158*aeeb85f2STakashi Iwai /* 159*aeeb85f2STakashi Iwai * driver entries 160*aeeb85f2STakashi Iwai */ 161*aeeb85f2STakashi Iwai static const struct hda_device_id snd_hda_id_alc268[] = { 162*aeeb85f2STakashi Iwai HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268), 163*aeeb85f2STakashi Iwai HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268), 164*aeeb85f2STakashi Iwai {} /* terminator */ 165*aeeb85f2STakashi Iwai }; 166*aeeb85f2STakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc268); 167*aeeb85f2STakashi Iwai 168*aeeb85f2STakashi Iwai MODULE_LICENSE("GPL"); 169*aeeb85f2STakashi Iwai MODULE_DESCRIPTION("Realtek ALC267/268 HD-audio codec"); 170*aeeb85f2STakashi Iwai MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK"); 171*aeeb85f2STakashi Iwai 172*aeeb85f2STakashi Iwai static struct hda_codec_driver alc268_driver = { 173*aeeb85f2STakashi Iwai .id = snd_hda_id_alc268, 174*aeeb85f2STakashi Iwai }; 175*aeeb85f2STakashi Iwai 176*aeeb85f2STakashi Iwai module_hda_codec_driver(alc268_driver); 177