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 acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
  14                                 void **rv)
  15{
  16        bool *found = context;
  17        *found = true;
  18        return AE_OK;
  19}
  20
  21static bool is_thinkpad(struct hda_codec *codec)
  22{
  23        bool found = false;
  24        if (codec->subsystem_id >> 16 != 0x17aa)
  25                return false;
  26        if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
  27                return true;
  28        found = false;
  29        return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
  30}
  31
  32static void update_tpacpi_mute_led(void *private_data, int enabled)
  33{
  34        if (old_vmaster_hook)
  35                old_vmaster_hook(private_data, enabled);
  36
  37        if (led_set_func)
  38                led_set_func(TPACPI_LED_MUTE, !enabled);
  39}
  40
  41static void update_tpacpi_micmute_led(struct hda_codec *codec,
  42                                      struct snd_kcontrol *kcontrol,
  43                                      struct snd_ctl_elem_value *ucontrol)
  44{
  45        if (!ucontrol || !led_set_func)
  46                return;
  47        if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
  48                /* TODO: How do I verify if it's a mono or stereo here? */
  49                bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1];
  50                led_set_func(TPACPI_LED_MICMUTE, !val);
  51        }
  52}
  53
  54static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
  55                                    const struct hda_fixup *fix, int action)
  56{
  57        struct hda_gen_spec *spec = codec->spec;
  58        bool removefunc = false;
  59
  60        if (action == HDA_FIXUP_ACT_PROBE) {
  61                if (!is_thinkpad(codec))
  62                        return;
  63                if (!led_set_func)
  64                        led_set_func = symbol_request(tpacpi_led_set);
  65                if (!led_set_func) {
  66                        codec_warn(codec,
  67                                   "Failed to find thinkpad-acpi symbol tpacpi_led_set\n");
  68                        return;
  69                }
  70
  71                removefunc = true;
  72                if (led_set_func(TPACPI_LED_MUTE, false) >= 0) {
  73                        old_vmaster_hook = spec->vmaster_mute.hook;
  74                        spec->vmaster_mute.hook = update_tpacpi_mute_led;
  75                        removefunc = false;
  76                }
  77                if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
  78                        if (spec->num_adc_nids > 1)
  79                                codec_dbg(codec,
  80                                          "Skipping micmute LED control due to several ADCs");
  81                        else {
  82                                spec->cap_sync_hook = update_tpacpi_micmute_led;
  83                                removefunc = false;
  84                        }
  85                }
  86        }
  87
  88        if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
  89                symbol_put(tpacpi_led_set);
  90                led_set_func = NULL;
  91                old_vmaster_hook = NULL;
  92        }
  93}
  94
  95#else /* CONFIG_THINKPAD_ACPI */
  96
  97static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
  98                                    const struct hda_fixup *fix, int action)
  99{
 100}
 101
 102#endif /* CONFIG_THINKPAD_ACPI */
 103