patch_senarytech.c 6.0 KB

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