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