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