thinkpad_helper.c 2.4 KB

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