thinkpad_helper.c 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Helper functions for Thinkpad LED control;
  3. * to be included from codec driver
  4. */
  5. #if IS_ENABLED(CONFIG_THINKPAD_ACPI)
  6. #include <linux/acpi.h>
  7. #include <linux/thinkpad_acpi.h>
  8. static int (*led_set_func)(int, bool);
  9. static void (*old_vmaster_hook)(void *, int);
  10. static bool is_thinkpad(struct hda_codec *codec)
  11. {
  12. return (codec->core.subsystem_id >> 16 == 0x17aa) &&
  13. (acpi_dev_found("LEN0068") || acpi_dev_found("LEN0268") ||
  14. acpi_dev_found("IBM0068"));
  15. }
  16. static void update_tpacpi_mute_led(void *private_data, int enabled)
  17. {
  18. if (old_vmaster_hook)
  19. old_vmaster_hook(private_data, enabled);
  20. if (led_set_func)
  21. led_set_func(TPACPI_LED_MUTE, !enabled);
  22. }
  23. static void update_tpacpi_micmute_led(struct hda_codec *codec,
  24. struct snd_kcontrol *kcontrol,
  25. struct snd_ctl_elem_value *ucontrol)
  26. {
  27. if (!ucontrol || !led_set_func)
  28. return;
  29. if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
  30. /* TODO: How do I verify if it's a mono or stereo here? */
  31. bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
  32. led_set_func(TPACPI_LED_MICMUTE, !val);
  33. }
  34. }
  35. static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
  36. const struct hda_fixup *fix, int action)
  37. {
  38. struct hda_gen_spec *spec = codec->spec;
  39. bool removefunc = false;
  40. if (action == HDA_FIXUP_ACT_PROBE) {
  41. if (!is_thinkpad(codec))
  42. return;
  43. if (!led_set_func)
  44. led_set_func = symbol_request(tpacpi_led_set);
  45. if (!led_set_func) {
  46. codec_warn(codec,
  47. "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
  48. return;
  49. }
  50. removefunc = true;
  51. if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
  52. old_vmaster_hook = spec->vmaster_mute.hook;
  53. spec->vmaster_mute.hook = update_tpacpi_mute_led;
  54. removefunc = false;
  55. }
  56. if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
  57. if (spec->num_adc_nids > 1 && !spec->dyn_adc_switch)
  58. codec_dbg(codec,
  59. "Skipping micmute LED control due to several ADCs");
  60. else {
  61. spec->cap_sync_hook = update_tpacpi_micmute_led;
  62. removefunc = false;
  63. }
  64. }
  65. }
  66. if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
  67. symbol_put(tpacpi_led_set);
  68. led_set_func = NULL;
  69. old_vmaster_hook = NULL;
  70. }
  71. }
  72. #else /* CONFIG_THINKPAD_ACPI */
  73. static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
  74. const struct hda_fixup *fix, int action)
  75. {
  76. }
  77. #endif /* CONFIG_THINKPAD_ACPI */