.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * HD audio interface patch for Conexant HDA audio codec |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com> |
---|
5 | 6 | * Takashi Iwai <tiwai@suse.de> |
---|
6 | 7 | * Tobin Davis <tdavis@dsl-only.net> |
---|
7 | | - * |
---|
8 | | - * This driver is free software; you can redistribute it and/or modify |
---|
9 | | - * it under the terms of the GNU General Public License as published by |
---|
10 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
11 | | - * (at your option) any later version. |
---|
12 | | - * |
---|
13 | | - * This driver is distributed in the hope that it will be useful, |
---|
14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
16 | | - * GNU General Public License for more details. |
---|
17 | | - * |
---|
18 | | - * You should have received a copy of the GNU General Public License |
---|
19 | | - * along with this program; if not, write to the Free Software |
---|
20 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
21 | 8 | */ |
---|
22 | 9 | |
---|
23 | 10 | #include <linux/init.h> |
---|
.. | .. |
---|
27 | 14 | #include <sound/core.h> |
---|
28 | 15 | #include <sound/jack.h> |
---|
29 | 16 | |
---|
30 | | -#include "hda_codec.h" |
---|
| 17 | +#include <sound/hda_codec.h> |
---|
31 | 18 | #include "hda_local.h" |
---|
32 | 19 | #include "hda_auto_parser.h" |
---|
33 | 20 | #include "hda_beep.h" |
---|
.. | .. |
---|
129 | 116 | } |
---|
130 | 117 | |
---|
131 | 118 | static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, |
---|
132 | | - hda_nid_t *pins, bool on) |
---|
| 119 | + const hda_nid_t *pins, bool on) |
---|
133 | 120 | { |
---|
134 | 121 | int i; |
---|
135 | 122 | for (i = 0; i < num_pins; i++) { |
---|
.. | .. |
---|
150 | 137 | } |
---|
151 | 138 | |
---|
152 | 139 | /* turn on/off EAPD according to Master switch (inversely!) for mute LED */ |
---|
153 | | -static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled) |
---|
| 140 | +static int cx_auto_vmaster_mute_led(struct led_classdev *led_cdev, |
---|
| 141 | + enum led_brightness brightness) |
---|
154 | 142 | { |
---|
155 | | - struct hda_codec *codec = private_data; |
---|
| 143 | + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); |
---|
156 | 144 | struct conexant_spec *spec = codec->spec; |
---|
157 | 145 | |
---|
158 | 146 | snd_hda_codec_write(codec, spec->mute_led_eapd, 0, |
---|
159 | 147 | AC_VERB_SET_EAPD_BTLENABLE, |
---|
160 | | - enabled ? 0x00 : 0x02); |
---|
| 148 | + brightness ? 0x02 : 0x00); |
---|
| 149 | + return 0; |
---|
| 150 | +} |
---|
| 151 | + |
---|
| 152 | +static void cxt_init_gpio_led(struct hda_codec *codec) |
---|
| 153 | +{ |
---|
| 154 | + struct conexant_spec *spec = codec->spec; |
---|
| 155 | + unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; |
---|
| 156 | + |
---|
| 157 | + if (mask) { |
---|
| 158 | + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, |
---|
| 159 | + mask); |
---|
| 160 | + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, |
---|
| 161 | + mask); |
---|
| 162 | + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, |
---|
| 163 | + spec->gpio_led); |
---|
| 164 | + } |
---|
161 | 165 | } |
---|
162 | 166 | |
---|
163 | 167 | static int cx_auto_init(struct hda_codec *codec) |
---|
.. | .. |
---|
167 | 171 | if (!spec->dynamic_eapd) |
---|
168 | 172 | cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); |
---|
169 | 173 | |
---|
| 174 | + cxt_init_gpio_led(codec); |
---|
170 | 175 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); |
---|
171 | 176 | |
---|
172 | 177 | return 0; |
---|
.. | .. |
---|
210 | 215 | CXT_PINCFG_LEMOTE_A1205, |
---|
211 | 216 | CXT_PINCFG_COMPAQ_CQ60, |
---|
212 | 217 | CXT_FIXUP_STEREO_DMIC, |
---|
| 218 | + CXT_PINCFG_LENOVO_NOTEBOOK, |
---|
213 | 219 | CXT_FIXUP_INC_MIC_BOOST, |
---|
214 | 220 | CXT_FIXUP_HEADPHONE_MIC_PIN, |
---|
215 | 221 | CXT_FIXUP_HEADPHONE_MIC, |
---|
.. | .. |
---|
226 | 232 | CXT_FIXUP_HP_SPECTRE, |
---|
227 | 233 | CXT_FIXUP_HP_GATE_MIC, |
---|
228 | 234 | CXT_FIXUP_MUTE_LED_GPIO, |
---|
| 235 | + CXT_FIXUP_HP_ZBOOK_MUTE_LED, |
---|
229 | 236 | CXT_FIXUP_HEADSET_MIC, |
---|
230 | 237 | CXT_FIXUP_HP_MIC_NO_PRESENCE, |
---|
231 | 238 | }; |
---|
.. | .. |
---|
579 | 586 | if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
---|
580 | 587 | spec->mute_led_eapd = 0x1b; |
---|
581 | 588 | spec->dynamic_eapd = 1; |
---|
582 | | - spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led; |
---|
| 589 | + snd_hda_gen_add_mute_led_cdev(codec, cx_auto_vmaster_mute_led); |
---|
583 | 590 | } |
---|
584 | 591 | } |
---|
585 | 592 | |
---|
.. | .. |
---|
644 | 651 | } |
---|
645 | 652 | |
---|
646 | 653 | /* turn on/off mute LED via GPIO per vmaster hook */ |
---|
647 | | -static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled) |
---|
| 654 | +static int cxt_gpio_mute_update(struct led_classdev *led_cdev, |
---|
| 655 | + enum led_brightness brightness) |
---|
648 | 656 | { |
---|
649 | | - struct hda_codec *codec = private_data; |
---|
| 657 | + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); |
---|
650 | 658 | struct conexant_spec *spec = codec->spec; |
---|
651 | | - /* muted -> LED on */ |
---|
652 | | - cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, !enabled); |
---|
| 659 | + |
---|
| 660 | + cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, brightness); |
---|
| 661 | + return 0; |
---|
653 | 662 | } |
---|
654 | 663 | |
---|
655 | 664 | /* turn on/off mic-mute LED via GPIO per capture hook */ |
---|
656 | | -static void cxt_gpio_micmute_update(struct hda_codec *codec) |
---|
| 665 | +static int cxt_gpio_micmute_update(struct led_classdev *led_cdev, |
---|
| 666 | + enum led_brightness brightness) |
---|
| 667 | +{ |
---|
| 668 | + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); |
---|
| 669 | + struct conexant_spec *spec = codec->spec; |
---|
| 670 | + |
---|
| 671 | + cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, brightness); |
---|
| 672 | + return 0; |
---|
| 673 | +} |
---|
| 674 | + |
---|
| 675 | +static void cxt_setup_mute_led(struct hda_codec *codec, |
---|
| 676 | + unsigned int mute, unsigned int mic_mute) |
---|
657 | 677 | { |
---|
658 | 678 | struct conexant_spec *spec = codec->spec; |
---|
659 | 679 | |
---|
660 | | - cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, |
---|
661 | | - spec->gen.micmute_led.led_value); |
---|
| 680 | + spec->gpio_led = 0; |
---|
| 681 | + spec->mute_led_polarity = 0; |
---|
| 682 | + if (mute) { |
---|
| 683 | + snd_hda_gen_add_mute_led_cdev(codec, cxt_gpio_mute_update); |
---|
| 684 | + spec->gpio_mute_led_mask = mute; |
---|
| 685 | + } |
---|
| 686 | + if (mic_mute) { |
---|
| 687 | + snd_hda_gen_add_micmute_led_cdev(codec, cxt_gpio_micmute_update); |
---|
| 688 | + spec->gpio_mic_led_mask = mic_mute; |
---|
| 689 | + } |
---|
662 | 690 | } |
---|
663 | | - |
---|
664 | 691 | |
---|
665 | 692 | static void cxt_fixup_mute_led_gpio(struct hda_codec *codec, |
---|
666 | 693 | const struct hda_fixup *fix, int action) |
---|
667 | 694 | { |
---|
668 | | - struct conexant_spec *spec = codec->spec; |
---|
669 | | - static const struct hda_verb gpio_init[] = { |
---|
670 | | - { 0x01, AC_VERB_SET_GPIO_MASK, 0x03 }, |
---|
671 | | - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 }, |
---|
672 | | - {} |
---|
673 | | - }; |
---|
674 | | - |
---|
675 | | - if (action == HDA_FIXUP_ACT_PRE_PROBE) { |
---|
676 | | - spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook; |
---|
677 | | - spec->gpio_led = 0; |
---|
678 | | - spec->mute_led_polarity = 0; |
---|
679 | | - spec->gpio_mute_led_mask = 0x01; |
---|
680 | | - spec->gpio_mic_led_mask = 0x02; |
---|
681 | | - snd_hda_gen_add_micmute_led(codec, cxt_gpio_micmute_update); |
---|
682 | | - } |
---|
683 | | - snd_hda_add_verbs(codec, gpio_init); |
---|
684 | | - if (spec->gpio_led) |
---|
685 | | - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, |
---|
686 | | - spec->gpio_led); |
---|
| 695 | + if (action == HDA_FIXUP_ACT_PRE_PROBE) |
---|
| 696 | + cxt_setup_mute_led(codec, 0x01, 0x02); |
---|
687 | 697 | } |
---|
688 | 698 | |
---|
| 699 | +static void cxt_fixup_hp_zbook_mute_led(struct hda_codec *codec, |
---|
| 700 | + const struct hda_fixup *fix, int action) |
---|
| 701 | +{ |
---|
| 702 | + if (action == HDA_FIXUP_ACT_PRE_PROBE) |
---|
| 703 | + cxt_setup_mute_led(codec, 0x10, 0x20); |
---|
| 704 | +} |
---|
689 | 705 | |
---|
690 | 706 | /* ThinkPad X200 & co with cxt5051 */ |
---|
691 | 707 | static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { |
---|
.. | .. |
---|
749 | 765 | [CXT_FIXUP_STEREO_DMIC] = { |
---|
750 | 766 | .type = HDA_FIXUP_FUNC, |
---|
751 | 767 | .v.func = cxt_fixup_stereo_dmic, |
---|
| 768 | + }, |
---|
| 769 | + [CXT_PINCFG_LENOVO_NOTEBOOK] = { |
---|
| 770 | + .type = HDA_FIXUP_PINS, |
---|
| 771 | + .v.pins = (const struct hda_pintbl[]) { |
---|
| 772 | + { 0x1a, 0x05d71030 }, |
---|
| 773 | + { } |
---|
| 774 | + }, |
---|
| 775 | + .chain_id = CXT_FIXUP_STEREO_DMIC, |
---|
752 | 776 | }, |
---|
753 | 777 | [CXT_FIXUP_INC_MIC_BOOST] = { |
---|
754 | 778 | .type = HDA_FIXUP_FUNC, |
---|
.. | .. |
---|
846 | 870 | .type = HDA_FIXUP_FUNC, |
---|
847 | 871 | .v.func = cxt_fixup_mute_led_gpio, |
---|
848 | 872 | }, |
---|
| 873 | + [CXT_FIXUP_HP_ZBOOK_MUTE_LED] = { |
---|
| 874 | + .type = HDA_FIXUP_FUNC, |
---|
| 875 | + .v.func = cxt_fixup_hp_zbook_mute_led, |
---|
| 876 | + }, |
---|
849 | 877 | [CXT_FIXUP_HEADSET_MIC] = { |
---|
850 | 878 | .type = HDA_FIXUP_FUNC, |
---|
851 | 879 | .v.func = cxt_fixup_headset_mic, |
---|
.. | .. |
---|
918 | 946 | SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK), |
---|
919 | 947 | SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), |
---|
920 | 948 | SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), |
---|
| 949 | + SND_PCI_QUIRK(0x103c, 0x82b4, "HP ProDesk 600 G3", CXT_FIXUP_HP_MIC_NO_PRESENCE), |
---|
921 | 950 | SND_PCI_QUIRK(0x103c, 0x836e, "HP ProBook 455 G5", CXT_FIXUP_MUTE_LED_GPIO), |
---|
922 | 951 | SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO), |
---|
923 | 952 | SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK), |
---|
924 | 953 | SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), |
---|
925 | 954 | SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK), |
---|
926 | 955 | SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO), |
---|
| 956 | + SND_PCI_QUIRK(0x103c, 0x8427, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), |
---|
| 957 | + SND_PCI_QUIRK(0x103c, 0x844f, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED), |
---|
927 | 958 | SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE), |
---|
928 | 959 | SND_PCI_QUIRK(0x103c, 0x8456, "HP Z2 G4 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), |
---|
929 | 960 | SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE), |
---|
.. | .. |
---|
942 | 973 | SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC), |
---|
943 | 974 | SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), |
---|
944 | 975 | SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), |
---|
| 976 | + /* NOTE: we'd need to extend the quirk for 17aa:3977 as the same |
---|
| 977 | + * PCI SSID is used on multiple Lenovo models |
---|
| 978 | + */ |
---|
945 | 979 | SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), |
---|
946 | 980 | SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), |
---|
947 | 981 | SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), |
---|
.. | .. |
---|
963 | 997 | { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" }, |
---|
964 | 998 | { .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" }, |
---|
965 | 999 | { .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" }, |
---|
| 1000 | + { .id = CXT_FIXUP_HP_ZBOOK_MUTE_LED, .name = "hp-zbook-mute-led" }, |
---|
966 | 1001 | { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" }, |
---|
| 1002 | + { .id = CXT_PINCFG_LENOVO_NOTEBOOK, .name = "lenovo-20149" }, |
---|
967 | 1003 | {} |
---|
968 | 1004 | }; |
---|
969 | 1005 | |
---|
.. | .. |
---|
973 | 1009 | static void add_cx5051_fake_mutes(struct hda_codec *codec) |
---|
974 | 1010 | { |
---|
975 | 1011 | struct conexant_spec *spec = codec->spec; |
---|
976 | | - static hda_nid_t out_nids[] = { |
---|
| 1012 | + static const hda_nid_t out_nids[] = { |
---|
977 | 1013 | 0x10, 0x11, 0 |
---|
978 | 1014 | }; |
---|
979 | | - hda_nid_t *p; |
---|
| 1015 | + const hda_nid_t *p; |
---|
980 | 1016 | |
---|
981 | 1017 | for (p = out_nids; *p; p++) |
---|
982 | 1018 | snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT, |
---|
.. | .. |
---|
1001 | 1037 | |
---|
1002 | 1038 | cx_auto_parse_eapd(codec); |
---|
1003 | 1039 | spec->gen.own_eapd_ctl = 1; |
---|
1004 | | - if (spec->dynamic_eapd) |
---|
1005 | | - spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; |
---|
1006 | 1040 | |
---|
1007 | 1041 | switch (codec->core.vendor_id) { |
---|
1008 | 1042 | case 0x14f15045: |
---|
.. | .. |
---|
1025 | 1059 | snd_hda_pick_fixup(codec, cxt5051_fixup_models, |
---|
1026 | 1060 | cxt5051_fixups, cxt_fixups); |
---|
1027 | 1061 | break; |
---|
| 1062 | + case 0x14f15098: |
---|
| 1063 | + codec->pin_amp_workaround = 1; |
---|
| 1064 | + spec->gen.mixer_nid = 0x22; |
---|
| 1065 | + spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; |
---|
| 1066 | + snd_hda_pick_fixup(codec, cxt5066_fixup_models, |
---|
| 1067 | + cxt5066_fixups, cxt_fixups); |
---|
| 1068 | + break; |
---|
1028 | 1069 | case 0x14f150f2: |
---|
1029 | 1070 | codec->power_save_node = 1; |
---|
1030 | | - /* Fall through */ |
---|
| 1071 | + fallthrough; |
---|
1031 | 1072 | default: |
---|
1032 | 1073 | codec->pin_amp_workaround = 1; |
---|
1033 | 1074 | snd_hda_pick_fixup(codec, cxt5066_fixup_models, |
---|
.. | .. |
---|
1035 | 1076 | break; |
---|
1036 | 1077 | } |
---|
1037 | 1078 | |
---|
1038 | | - /* Show mute-led control only on HP laptops |
---|
1039 | | - * This is a sort of white-list: on HP laptops, EAPD corresponds |
---|
1040 | | - * only to the mute-LED without actualy amp function. Meanwhile, |
---|
1041 | | - * others may use EAPD really as an amp switch, so it might be |
---|
1042 | | - * not good to expose it blindly. |
---|
1043 | | - */ |
---|
1044 | | - switch (codec->core.subsystem_id >> 16) { |
---|
1045 | | - case 0x103c: |
---|
1046 | | - spec->gen.vmaster_mute_enum = 1; |
---|
1047 | | - break; |
---|
1048 | | - } |
---|
| 1079 | + if (!spec->gen.vmaster_mute.hook && spec->dynamic_eapd) |
---|
| 1080 | + spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; |
---|
1049 | 1081 | |
---|
1050 | 1082 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); |
---|
1051 | 1083 | |
---|
.. | .. |
---|
1054 | 1086 | if (err < 0) |
---|
1055 | 1087 | goto error; |
---|
1056 | 1088 | |
---|
1057 | | - err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); |
---|
| 1089 | + err = cx_auto_parse_beep(codec); |
---|
1058 | 1090 | if (err < 0) |
---|
1059 | 1091 | goto error; |
---|
1060 | 1092 | |
---|
1061 | | - err = cx_auto_parse_beep(codec); |
---|
| 1093 | + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); |
---|
1062 | 1094 | if (err < 0) |
---|
1063 | 1095 | goto error; |
---|
1064 | 1096 | |
---|
.. | .. |
---|
1089 | 1121 | HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto), |
---|
1090 | 1122 | HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto), |
---|
1091 | 1123 | HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto), |
---|
| 1124 | + HDA_CODEC_ENTRY(0x14f120d1, "SN6180", patch_conexant_auto), |
---|
1092 | 1125 | HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto), |
---|
1093 | 1126 | HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto), |
---|
1094 | 1127 | HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto), |
---|