1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * HD audio codec driver for Senary HDA audio codec 4 * 5 * Initially based on conexant.c 6 */ 7 8 #include <linux/init.h> 9 #include <linux/delay.h> 10 #include <linux/slab.h> 11 #include <linux/module.h> 12 #include <sound/core.h> 13 #include <sound/jack.h> 14 15 #include <sound/hda_codec.h> 16 #include "hda_local.h" 17 #include "hda_auto_parser.h" 18 #include "hda_beep.h" 19 #include "hda_jack.h" 20 #include "generic.h" 21 22 struct senary_spec { 23 struct hda_gen_spec gen; 24 25 /* extra EAPD pins */ 26 unsigned int num_eapds; 27 hda_nid_t eapds[4]; 28 bool dynamic_eapd; 29 hda_nid_t mute_led_eapd; 30 31 unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ 32 33 int mute_led_polarity; 34 unsigned int gpio_led; 35 unsigned int gpio_mute_led_mask; 36 unsigned int gpio_mic_led_mask; 37 }; 38 39 #ifdef CONFIG_SND_HDA_INPUT_BEEP 40 /* additional beep mixers; private_value will be overwritten */ 41 static const struct snd_kcontrol_new senary_beep_mixer[] = { 42 HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), 43 HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), 44 }; 45 46 static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid, 47 int idx, int dir) 48 { 49 struct snd_kcontrol_new *knew; 50 unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); 51 int i; 52 53 spec->gen.beep_nid = nid; 54 for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) { 55 knew = snd_hda_gen_add_kctl(&spec->gen, NULL, 56 &senary_beep_mixer[i]); 57 if (!knew) 58 return -ENOMEM; 59 knew->private_value = beep_amp; 60 } 61 return 0; 62 } 63 64 static int senary_auto_parse_beep(struct hda_codec *codec) 65 { 66 struct senary_spec *spec = codec->spec; 67 hda_nid_t nid; 68 69 for_each_hda_codec_node(nid, codec) 70 if ((get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) && 71 (get_wcaps(codec, nid) & (AC_WCAP_OUT_AMP | AC_WCAP_AMP_OVRD))) 72 return set_beep_amp(spec, nid, 0, HDA_OUTPUT); 73 return 0; 74 } 75 #else 76 #define senary_auto_parse_beep(codec) 0 77 #endif 78 79 /* parse EAPDs */ 80 static void senary_auto_parse_eapd(struct hda_codec *codec) 81 { 82 struct senary_spec *spec = codec->spec; 83 hda_nid_t nid; 84 85 for_each_hda_codec_node(nid, codec) { 86 if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) 87 continue; 88 if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) 89 continue; 90 spec->eapds[spec->num_eapds++] = nid; 91 if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) 92 break; 93 } 94 } 95 96 static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins, 97 const hda_nid_t *pins, bool on) 98 { 99 int i; 100 101 for (i = 0; i < num_pins; i++) { 102 if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD) 103 snd_hda_codec_write(codec, pins[i], 0, 104 AC_VERB_SET_EAPD_BTLENABLE, 105 on ? 0x02 : 0); 106 } 107 } 108 109 /* turn on/off EAPD according to Master switch */ 110 static void senary_auto_vmaster_hook(void *private_data, int enabled) 111 { 112 struct hda_codec *codec = private_data; 113 struct senary_spec *spec = codec->spec; 114 115 senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled); 116 } 117 118 static void senary_init_gpio_led(struct hda_codec *codec) 119 { 120 struct senary_spec *spec = codec->spec; 121 unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask; 122 123 if (mask) { 124 snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, 125 mask); 126 snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, 127 mask); 128 snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 129 spec->gpio_led); 130 } 131 } 132 133 static int senary_init(struct hda_codec *codec) 134 { 135 struct senary_spec *spec = codec->spec; 136 137 snd_hda_gen_init(codec); 138 senary_init_gpio_led(codec); 139 if (!spec->dynamic_eapd) 140 senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); 141 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); 142 143 return 0; 144 } 145 146 static void senary_shutdown(struct hda_codec *codec) 147 { 148 struct senary_spec *spec = codec->spec; 149 150 /* Turn the problematic codec into D3 to avoid spurious noises 151 * from the internal speaker during (and after) reboot 152 */ 153 senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); 154 } 155 156 static void senary_remove(struct hda_codec *codec) 157 { 158 senary_shutdown(codec); 159 snd_hda_gen_remove(codec); 160 } 161 162 static int senary_suspend(struct hda_codec *codec) 163 { 164 senary_shutdown(codec); 165 return 0; 166 } 167 168 static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id) 169 { 170 struct senary_spec *spec; 171 int err; 172 173 codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); 174 175 spec = kzalloc_obj(*spec); 176 if (!spec) 177 return -ENOMEM; 178 snd_hda_gen_spec_init(&spec->gen); 179 codec->spec = spec; 180 181 senary_auto_parse_eapd(codec); 182 spec->gen.own_eapd_ctl = 1; 183 184 if (!spec->gen.vmaster_mute.hook) 185 spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook; 186 187 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 188 189 err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 190 spec->parse_flags); 191 if (err < 0) 192 goto error; 193 194 err = senary_auto_parse_beep(codec); 195 if (err < 0) 196 goto error; 197 198 err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 199 if (err < 0) 200 goto error; 201 202 /* Some laptops with Senary chips show stalls in S3 resume, 203 * which falls into the single-cmd mode. 204 * Better to make reset, then. 205 */ 206 if (!codec->bus->core.sync_write) { 207 codec_info(codec, 208 "Enable sync_write for stable communication\n"); 209 codec->bus->core.sync_write = 1; 210 codec->bus->allow_bus_reset = 1; 211 } 212 213 snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 214 215 return 0; 216 217 error: 218 senary_remove(codec); 219 return err; 220 } 221 222 static const struct hda_codec_ops senary_codec_ops = { 223 .probe = senary_probe, 224 .remove = senary_remove, 225 .build_controls = snd_hda_gen_build_controls, 226 .build_pcms = snd_hda_gen_build_pcms, 227 .init = senary_init, 228 .unsol_event = snd_hda_jack_unsol_event, 229 .suspend = senary_suspend, 230 .check_power_status = snd_hda_gen_check_power_status, 231 .stream_pm = snd_hda_gen_stream_pm, 232 }; 233 234 /* 235 */ 236 237 static const struct hda_device_id snd_hda_id_senary[] = { 238 HDA_CODEC_ID(0x1fa86186, "SN6186"), 239 {} /* terminator */ 240 }; 241 MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary); 242 243 MODULE_LICENSE("GPL"); 244 MODULE_DESCRIPTION("Senarytech HD-audio codec"); 245 246 static struct hda_codec_driver senary_driver = { 247 .id = snd_hda_id_senary, 248 .ops = &senary_codec_ops, 249 }; 250 251 module_hda_codec_driver(senary_driver); 252