]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - sound/pci/hda/dell_wmi_helper.c
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
[mirror_ubuntu-hirsute-kernel.git] / sound / pci / hda / dell_wmi_helper.c
1 /* Helper functions for Dell Mic Mute LED control;
2 * to be included from codec driver
3 */
4
5 #if IS_ENABLED(CONFIG_DELL_LAPTOP)
6 #include <linux/dell-led.h>
7
8 enum {
9 MICMUTE_LED_ON,
10 MICMUTE_LED_OFF,
11 MICMUTE_LED_FOLLOW_CAPTURE,
12 MICMUTE_LED_FOLLOW_MUTE,
13 };
14
15 static int dell_led_mode = MICMUTE_LED_FOLLOW_MUTE;
16 static int dell_capture;
17 static int dell_led_value;
18 static int (*dell_micmute_led_set_func)(int);
19 static void (*dell_old_cap_hook)(struct hda_codec *,
20 struct snd_kcontrol *,
21 struct snd_ctl_elem_value *);
22
23 static void call_micmute_led_update(void)
24 {
25 int val;
26
27 switch (dell_led_mode) {
28 case MICMUTE_LED_ON:
29 val = 1;
30 break;
31 case MICMUTE_LED_OFF:
32 val = 0;
33 break;
34 case MICMUTE_LED_FOLLOW_CAPTURE:
35 val = dell_capture;
36 break;
37 case MICMUTE_LED_FOLLOW_MUTE:
38 default:
39 val = !dell_capture;
40 break;
41 }
42
43 if (val == dell_led_value)
44 return;
45 dell_led_value = val;
46 dell_micmute_led_set_func(dell_led_value);
47 }
48
49 static void update_dell_wmi_micmute_led(struct hda_codec *codec,
50 struct snd_kcontrol *kcontrol,
51 struct snd_ctl_elem_value *ucontrol)
52 {
53 if (dell_old_cap_hook)
54 dell_old_cap_hook(codec, kcontrol, ucontrol);
55
56 if (!ucontrol || !dell_micmute_led_set_func)
57 return;
58 if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
59 /* TODO: How do I verify if it's a mono or stereo here? */
60 dell_capture = (ucontrol->value.integer.value[0] ||
61 ucontrol->value.integer.value[1]);
62 call_micmute_led_update();
63 }
64 }
65
66 static int dell_mic_mute_led_mode_info(struct snd_kcontrol *kcontrol,
67 struct snd_ctl_elem_info *uinfo)
68 {
69 static const char * const texts[] = {
70 "On", "Off", "Follow Capture", "Follow Mute",
71 };
72
73 return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
74 }
75
76 static int dell_mic_mute_led_mode_get(struct snd_kcontrol *kcontrol,
77 struct snd_ctl_elem_value *ucontrol)
78 {
79 ucontrol->value.enumerated.item[0] = dell_led_mode;
80 return 0;
81 }
82
83 static int dell_mic_mute_led_mode_put(struct snd_kcontrol *kcontrol,
84 struct snd_ctl_elem_value *ucontrol)
85 {
86 unsigned int mode;
87
88 mode = ucontrol->value.enumerated.item[0];
89 if (mode > MICMUTE_LED_FOLLOW_MUTE)
90 mode = MICMUTE_LED_FOLLOW_MUTE;
91 if (mode == dell_led_mode)
92 return 0;
93 dell_led_mode = mode;
94 call_micmute_led_update();
95 return 1;
96 }
97
98 static const struct snd_kcontrol_new dell_mic_mute_mode_ctls[] = {
99 {
100 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
101 .name = "Mic Mute-LED Mode",
102 .info = dell_mic_mute_led_mode_info,
103 .get = dell_mic_mute_led_mode_get,
104 .put = dell_mic_mute_led_mode_put,
105 },
106 {}
107 };
108
109 static void alc_fixup_dell_wmi(struct hda_codec *codec,
110 const struct hda_fixup *fix, int action)
111 {
112 struct alc_spec *spec = codec->spec;
113 bool removefunc = false;
114
115 if (action == HDA_FIXUP_ACT_PROBE) {
116 if (!dell_micmute_led_set_func)
117 dell_micmute_led_set_func = symbol_request(dell_micmute_led_set);
118 if (!dell_micmute_led_set_func) {
119 codec_warn(codec, "Failed to find dell wmi symbol dell_micmute_led_set\n");
120 return;
121 }
122
123 removefunc = true;
124 if (dell_micmute_led_set_func(false) >= 0) {
125 dell_led_value = 0;
126 if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch)
127 codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
128 else {
129 dell_old_cap_hook = spec->gen.cap_sync_hook;
130 spec->gen.cap_sync_hook = update_dell_wmi_micmute_led;
131 removefunc = false;
132 add_mixer(spec, dell_mic_mute_mode_ctls);
133 }
134 }
135
136 }
137
138 if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
139 symbol_put(dell_micmute_led_set);
140 dell_micmute_led_set_func = NULL;
141 dell_old_cap_hook = NULL;
142 }
143 }
144
145 #else /* CONFIG_DELL_LAPTOP */
146 static void alc_fixup_dell_wmi(struct hda_codec *codec,
147 const struct hda_fixup *fix, int action)
148 {
149 }
150
151 #endif /* CONFIG_DELL_LAPTOP */