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