thinkpad_helper.c 2.6 KB

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