1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver for Dell laptop extras
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) Red Hat <mjg@redhat.com>
6*4882a593Smuzhiyun * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
7*4882a593Smuzhiyun * Copyright (c) 2014 Pali Rohár <pali@kernel.org>
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Based on documentation in the libsmbios package:
10*4882a593Smuzhiyun * Copyright (C) 2005-2014 Dell Inc.
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/init.h>
18*4882a593Smuzhiyun #include <linux/platform_device.h>
19*4882a593Smuzhiyun #include <linux/backlight.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun #include <linux/dmi.h>
22*4882a593Smuzhiyun #include <linux/io.h>
23*4882a593Smuzhiyun #include <linux/rfkill.h>
24*4882a593Smuzhiyun #include <linux/power_supply.h>
25*4882a593Smuzhiyun #include <linux/acpi.h>
26*4882a593Smuzhiyun #include <linux/mm.h>
27*4882a593Smuzhiyun #include <linux/i8042.h>
28*4882a593Smuzhiyun #include <linux/debugfs.h>
29*4882a593Smuzhiyun #include <linux/seq_file.h>
30*4882a593Smuzhiyun #include <acpi/video.h>
31*4882a593Smuzhiyun #include "dell-rbtn.h"
32*4882a593Smuzhiyun #include "dell-smbios.h"
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun struct quirk_entry {
35*4882a593Smuzhiyun bool touchpad_led;
36*4882a593Smuzhiyun bool kbd_led_not_present;
37*4882a593Smuzhiyun bool kbd_led_levels_off_1;
38*4882a593Smuzhiyun bool kbd_missing_ac_tag;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun bool needs_kbd_timeouts;
41*4882a593Smuzhiyun /*
42*4882a593Smuzhiyun * Ordered list of timeouts expressed in seconds.
43*4882a593Smuzhiyun * The list must end with -1
44*4882a593Smuzhiyun */
45*4882a593Smuzhiyun int kbd_timeouts[];
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun static struct quirk_entry *quirks;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun static struct quirk_entry quirk_dell_vostro_v130 = {
51*4882a593Smuzhiyun .touchpad_led = true,
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
dmi_matched(const struct dmi_system_id * dmi)54*4882a593Smuzhiyun static int __init dmi_matched(const struct dmi_system_id *dmi)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun quirks = dmi->driver_data;
57*4882a593Smuzhiyun return 1;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun /*
61*4882a593Smuzhiyun * These values come from Windows utility provided by Dell. If any other value
62*4882a593Smuzhiyun * is used then BIOS silently set timeout to 0 without any error message.
63*4882a593Smuzhiyun */
64*4882a593Smuzhiyun static struct quirk_entry quirk_dell_xps13_9333 = {
65*4882a593Smuzhiyun .needs_kbd_timeouts = true,
66*4882a593Smuzhiyun .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun static struct quirk_entry quirk_dell_xps13_9370 = {
70*4882a593Smuzhiyun .kbd_missing_ac_tag = true,
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun static struct quirk_entry quirk_dell_latitude_e6410 = {
74*4882a593Smuzhiyun .kbd_led_levels_off_1 = true,
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun static struct quirk_entry quirk_dell_inspiron_1012 = {
78*4882a593Smuzhiyun .kbd_led_not_present = true,
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static struct platform_driver platform_driver = {
82*4882a593Smuzhiyun .driver = {
83*4882a593Smuzhiyun .name = "dell-laptop",
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun };
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun static struct platform_device *platform_device;
88*4882a593Smuzhiyun static struct backlight_device *dell_backlight_device;
89*4882a593Smuzhiyun static struct rfkill *wifi_rfkill;
90*4882a593Smuzhiyun static struct rfkill *bluetooth_rfkill;
91*4882a593Smuzhiyun static struct rfkill *wwan_rfkill;
92*4882a593Smuzhiyun static bool force_rfkill;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun module_param(force_rfkill, bool, 0444);
95*4882a593Smuzhiyun MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun static const struct dmi_system_id dell_device_table[] __initconst = {
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun .ident = "Dell laptop",
100*4882a593Smuzhiyun .matches = {
101*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
102*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
103*4882a593Smuzhiyun },
104*4882a593Smuzhiyun },
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun .matches = {
107*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
108*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
109*4882a593Smuzhiyun },
110*4882a593Smuzhiyun },
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun .matches = {
113*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
114*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
115*4882a593Smuzhiyun },
116*4882a593Smuzhiyun },
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun .matches = {
119*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
120*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_TYPE, "30"), /*Tablet*/
121*4882a593Smuzhiyun },
122*4882a593Smuzhiyun },
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun .matches = {
125*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
126*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /*Convertible*/
127*4882a593Smuzhiyun },
128*4882a593Smuzhiyun },
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun .matches = {
131*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
132*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_TYPE, "32"), /*Detachable*/
133*4882a593Smuzhiyun },
134*4882a593Smuzhiyun },
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun .ident = "Dell Computer Corporation",
137*4882a593Smuzhiyun .matches = {
138*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
139*4882a593Smuzhiyun DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
140*4882a593Smuzhiyun },
141*4882a593Smuzhiyun },
142*4882a593Smuzhiyun { }
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun MODULE_DEVICE_TABLE(dmi, dell_device_table);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun static const struct dmi_system_id dell_quirks[] __initconst = {
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun .callback = dmi_matched,
149*4882a593Smuzhiyun .ident = "Dell Vostro V130",
150*4882a593Smuzhiyun .matches = {
151*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
152*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"),
153*4882a593Smuzhiyun },
154*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
155*4882a593Smuzhiyun },
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun .callback = dmi_matched,
158*4882a593Smuzhiyun .ident = "Dell Vostro V131",
159*4882a593Smuzhiyun .matches = {
160*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
161*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
162*4882a593Smuzhiyun },
163*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
164*4882a593Smuzhiyun },
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun .callback = dmi_matched,
167*4882a593Smuzhiyun .ident = "Dell Vostro 3350",
168*4882a593Smuzhiyun .matches = {
169*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
170*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"),
171*4882a593Smuzhiyun },
172*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
173*4882a593Smuzhiyun },
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun .callback = dmi_matched,
176*4882a593Smuzhiyun .ident = "Dell Vostro 3555",
177*4882a593Smuzhiyun .matches = {
178*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
179*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"),
180*4882a593Smuzhiyun },
181*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
182*4882a593Smuzhiyun },
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun .callback = dmi_matched,
185*4882a593Smuzhiyun .ident = "Dell Inspiron N311z",
186*4882a593Smuzhiyun .matches = {
187*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
188*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"),
189*4882a593Smuzhiyun },
190*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
191*4882a593Smuzhiyun },
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun .callback = dmi_matched,
194*4882a593Smuzhiyun .ident = "Dell Inspiron M5110",
195*4882a593Smuzhiyun .matches = {
196*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
197*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
198*4882a593Smuzhiyun },
199*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
200*4882a593Smuzhiyun },
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun .callback = dmi_matched,
203*4882a593Smuzhiyun .ident = "Dell Vostro 3360",
204*4882a593Smuzhiyun .matches = {
205*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
206*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"),
207*4882a593Smuzhiyun },
208*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
209*4882a593Smuzhiyun },
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun .callback = dmi_matched,
212*4882a593Smuzhiyun .ident = "Dell Vostro 3460",
213*4882a593Smuzhiyun .matches = {
214*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
215*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3460"),
216*4882a593Smuzhiyun },
217*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
218*4882a593Smuzhiyun },
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun .callback = dmi_matched,
221*4882a593Smuzhiyun .ident = "Dell Vostro 3560",
222*4882a593Smuzhiyun .matches = {
223*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
224*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3560"),
225*4882a593Smuzhiyun },
226*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
227*4882a593Smuzhiyun },
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun .callback = dmi_matched,
230*4882a593Smuzhiyun .ident = "Dell Vostro 3450",
231*4882a593Smuzhiyun .matches = {
232*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
233*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Dell System Vostro 3450"),
234*4882a593Smuzhiyun },
235*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
236*4882a593Smuzhiyun },
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun .callback = dmi_matched,
239*4882a593Smuzhiyun .ident = "Dell Inspiron 5420",
240*4882a593Smuzhiyun .matches = {
241*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
242*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5420"),
243*4882a593Smuzhiyun },
244*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
245*4882a593Smuzhiyun },
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun .callback = dmi_matched,
248*4882a593Smuzhiyun .ident = "Dell Inspiron 5520",
249*4882a593Smuzhiyun .matches = {
250*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
251*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5520"),
252*4882a593Smuzhiyun },
253*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
254*4882a593Smuzhiyun },
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun .callback = dmi_matched,
257*4882a593Smuzhiyun .ident = "Dell Inspiron 5720",
258*4882a593Smuzhiyun .matches = {
259*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
260*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5720"),
261*4882a593Smuzhiyun },
262*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
263*4882a593Smuzhiyun },
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun .callback = dmi_matched,
266*4882a593Smuzhiyun .ident = "Dell Inspiron 7420",
267*4882a593Smuzhiyun .matches = {
268*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
269*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7420"),
270*4882a593Smuzhiyun },
271*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
272*4882a593Smuzhiyun },
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun .callback = dmi_matched,
275*4882a593Smuzhiyun .ident = "Dell Inspiron 7520",
276*4882a593Smuzhiyun .matches = {
277*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
278*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
279*4882a593Smuzhiyun },
280*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
281*4882a593Smuzhiyun },
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun .callback = dmi_matched,
284*4882a593Smuzhiyun .ident = "Dell Inspiron 7720",
285*4882a593Smuzhiyun .matches = {
286*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
287*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
288*4882a593Smuzhiyun },
289*4882a593Smuzhiyun .driver_data = &quirk_dell_vostro_v130,
290*4882a593Smuzhiyun },
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun .callback = dmi_matched,
293*4882a593Smuzhiyun .ident = "Dell XPS13 9333",
294*4882a593Smuzhiyun .matches = {
295*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
296*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
297*4882a593Smuzhiyun },
298*4882a593Smuzhiyun .driver_data = &quirk_dell_xps13_9333,
299*4882a593Smuzhiyun },
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun .callback = dmi_matched,
302*4882a593Smuzhiyun .ident = "Dell XPS 13 9370",
303*4882a593Smuzhiyun .matches = {
304*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
305*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9370"),
306*4882a593Smuzhiyun },
307*4882a593Smuzhiyun .driver_data = &quirk_dell_xps13_9370,
308*4882a593Smuzhiyun },
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun .callback = dmi_matched,
311*4882a593Smuzhiyun .ident = "Dell Latitude E6410",
312*4882a593Smuzhiyun .matches = {
313*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
314*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6410"),
315*4882a593Smuzhiyun },
316*4882a593Smuzhiyun .driver_data = &quirk_dell_latitude_e6410,
317*4882a593Smuzhiyun },
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun .callback = dmi_matched,
320*4882a593Smuzhiyun .ident = "Dell Inspiron 1012",
321*4882a593Smuzhiyun .matches = {
322*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
323*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
324*4882a593Smuzhiyun },
325*4882a593Smuzhiyun .driver_data = &quirk_dell_inspiron_1012,
326*4882a593Smuzhiyun },
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun .callback = dmi_matched,
329*4882a593Smuzhiyun .ident = "Dell Inspiron 1018",
330*4882a593Smuzhiyun .matches = {
331*4882a593Smuzhiyun DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
332*4882a593Smuzhiyun DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1018"),
333*4882a593Smuzhiyun },
334*4882a593Smuzhiyun .driver_data = &quirk_dell_inspiron_1012,
335*4882a593Smuzhiyun },
336*4882a593Smuzhiyun { }
337*4882a593Smuzhiyun };
338*4882a593Smuzhiyun
dell_fill_request(struct calling_interface_buffer * buffer,u32 arg0,u32 arg1,u32 arg2,u32 arg3)339*4882a593Smuzhiyun static void dell_fill_request(struct calling_interface_buffer *buffer,
340*4882a593Smuzhiyun u32 arg0, u32 arg1, u32 arg2, u32 arg3)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun memset(buffer, 0, sizeof(struct calling_interface_buffer));
343*4882a593Smuzhiyun buffer->input[0] = arg0;
344*4882a593Smuzhiyun buffer->input[1] = arg1;
345*4882a593Smuzhiyun buffer->input[2] = arg2;
346*4882a593Smuzhiyun buffer->input[3] = arg3;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun
dell_send_request(struct calling_interface_buffer * buffer,u16 class,u16 select)349*4882a593Smuzhiyun static int dell_send_request(struct calling_interface_buffer *buffer,
350*4882a593Smuzhiyun u16 class, u16 select)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun int ret;
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun buffer->cmd_class = class;
355*4882a593Smuzhiyun buffer->cmd_select = select;
356*4882a593Smuzhiyun ret = dell_smbios_call(buffer);
357*4882a593Smuzhiyun if (ret != 0)
358*4882a593Smuzhiyun return ret;
359*4882a593Smuzhiyun return dell_smbios_error(buffer->output[0]);
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun /*
363*4882a593Smuzhiyun * Derived from information in smbios-wireless-ctl:
364*4882a593Smuzhiyun *
365*4882a593Smuzhiyun * cbSelect 17, Value 11
366*4882a593Smuzhiyun *
367*4882a593Smuzhiyun * Return Wireless Info
368*4882a593Smuzhiyun * cbArg1, byte0 = 0x00
369*4882a593Smuzhiyun *
370*4882a593Smuzhiyun * cbRes1 Standard return codes (0, -1, -2)
371*4882a593Smuzhiyun * cbRes2 Info bit flags:
372*4882a593Smuzhiyun *
373*4882a593Smuzhiyun * 0 Hardware switch supported (1)
374*4882a593Smuzhiyun * 1 WiFi locator supported (1)
375*4882a593Smuzhiyun * 2 WLAN supported (1)
376*4882a593Smuzhiyun * 3 Bluetooth (BT) supported (1)
377*4882a593Smuzhiyun * 4 WWAN supported (1)
378*4882a593Smuzhiyun * 5 Wireless KBD supported (1)
379*4882a593Smuzhiyun * 6 Uw b supported (1)
380*4882a593Smuzhiyun * 7 WiGig supported (1)
381*4882a593Smuzhiyun * 8 WLAN installed (1)
382*4882a593Smuzhiyun * 9 BT installed (1)
383*4882a593Smuzhiyun * 10 WWAN installed (1)
384*4882a593Smuzhiyun * 11 Uw b installed (1)
385*4882a593Smuzhiyun * 12 WiGig installed (1)
386*4882a593Smuzhiyun * 13-15 Reserved (0)
387*4882a593Smuzhiyun * 16 Hardware (HW) switch is On (1)
388*4882a593Smuzhiyun * 17 WLAN disabled (1)
389*4882a593Smuzhiyun * 18 BT disabled (1)
390*4882a593Smuzhiyun * 19 WWAN disabled (1)
391*4882a593Smuzhiyun * 20 Uw b disabled (1)
392*4882a593Smuzhiyun * 21 WiGig disabled (1)
393*4882a593Smuzhiyun * 20-31 Reserved (0)
394*4882a593Smuzhiyun *
395*4882a593Smuzhiyun * cbRes3 NVRAM size in bytes
396*4882a593Smuzhiyun * cbRes4, byte 0 NVRAM format version number
397*4882a593Smuzhiyun *
398*4882a593Smuzhiyun *
399*4882a593Smuzhiyun * Set QuickSet Radio Disable Flag
400*4882a593Smuzhiyun * cbArg1, byte0 = 0x01
401*4882a593Smuzhiyun * cbArg1, byte1
402*4882a593Smuzhiyun * Radio ID value:
403*4882a593Smuzhiyun * 0 Radio Status
404*4882a593Smuzhiyun * 1 WLAN ID
405*4882a593Smuzhiyun * 2 BT ID
406*4882a593Smuzhiyun * 3 WWAN ID
407*4882a593Smuzhiyun * 4 UWB ID
408*4882a593Smuzhiyun * 5 WIGIG ID
409*4882a593Smuzhiyun * cbArg1, byte2 Flag bits:
410*4882a593Smuzhiyun * 0 QuickSet disables radio (1)
411*4882a593Smuzhiyun * 1-7 Reserved (0)
412*4882a593Smuzhiyun *
413*4882a593Smuzhiyun * cbRes1 Standard return codes (0, -1, -2)
414*4882a593Smuzhiyun * cbRes2 QuickSet (QS) radio disable bit map:
415*4882a593Smuzhiyun * 0 QS disables WLAN
416*4882a593Smuzhiyun * 1 QS disables BT
417*4882a593Smuzhiyun * 2 QS disables WWAN
418*4882a593Smuzhiyun * 3 QS disables UWB
419*4882a593Smuzhiyun * 4 QS disables WIGIG
420*4882a593Smuzhiyun * 5-31 Reserved (0)
421*4882a593Smuzhiyun *
422*4882a593Smuzhiyun * Wireless Switch Configuration
423*4882a593Smuzhiyun * cbArg1, byte0 = 0x02
424*4882a593Smuzhiyun *
425*4882a593Smuzhiyun * cbArg1, byte1
426*4882a593Smuzhiyun * Subcommand:
427*4882a593Smuzhiyun * 0 Get config
428*4882a593Smuzhiyun * 1 Set config
429*4882a593Smuzhiyun * 2 Set WiFi locator enable/disable
430*4882a593Smuzhiyun * cbArg1,byte2
431*4882a593Smuzhiyun * Switch settings (if byte 1==1):
432*4882a593Smuzhiyun * 0 WLAN sw itch control (1)
433*4882a593Smuzhiyun * 1 BT sw itch control (1)
434*4882a593Smuzhiyun * 2 WWAN sw itch control (1)
435*4882a593Smuzhiyun * 3 UWB sw itch control (1)
436*4882a593Smuzhiyun * 4 WiGig sw itch control (1)
437*4882a593Smuzhiyun * 5-7 Reserved (0)
438*4882a593Smuzhiyun * cbArg1, byte2 Enable bits (if byte 1==2):
439*4882a593Smuzhiyun * 0 Enable WiFi locator (1)
440*4882a593Smuzhiyun *
441*4882a593Smuzhiyun * cbRes1 Standard return codes (0, -1, -2)
442*4882a593Smuzhiyun * cbRes2 QuickSet radio disable bit map:
443*4882a593Smuzhiyun * 0 WLAN controlled by sw itch (1)
444*4882a593Smuzhiyun * 1 BT controlled by sw itch (1)
445*4882a593Smuzhiyun * 2 WWAN controlled by sw itch (1)
446*4882a593Smuzhiyun * 3 UWB controlled by sw itch (1)
447*4882a593Smuzhiyun * 4 WiGig controlled by sw itch (1)
448*4882a593Smuzhiyun * 5-6 Reserved (0)
449*4882a593Smuzhiyun * 7 Wireless sw itch config locked (1)
450*4882a593Smuzhiyun * 8 WiFi locator enabled (1)
451*4882a593Smuzhiyun * 9-14 Reserved (0)
452*4882a593Smuzhiyun * 15 WiFi locator setting locked (1)
453*4882a593Smuzhiyun * 16-31 Reserved (0)
454*4882a593Smuzhiyun *
455*4882a593Smuzhiyun * Read Local Config Data (LCD)
456*4882a593Smuzhiyun * cbArg1, byte0 = 0x10
457*4882a593Smuzhiyun * cbArg1, byte1 NVRAM index low byte
458*4882a593Smuzhiyun * cbArg1, byte2 NVRAM index high byte
459*4882a593Smuzhiyun * cbRes1 Standard return codes (0, -1, -2)
460*4882a593Smuzhiyun * cbRes2 4 bytes read from LCD[index]
461*4882a593Smuzhiyun * cbRes3 4 bytes read from LCD[index+4]
462*4882a593Smuzhiyun * cbRes4 4 bytes read from LCD[index+8]
463*4882a593Smuzhiyun *
464*4882a593Smuzhiyun * Write Local Config Data (LCD)
465*4882a593Smuzhiyun * cbArg1, byte0 = 0x11
466*4882a593Smuzhiyun * cbArg1, byte1 NVRAM index low byte
467*4882a593Smuzhiyun * cbArg1, byte2 NVRAM index high byte
468*4882a593Smuzhiyun * cbArg2 4 bytes to w rite at LCD[index]
469*4882a593Smuzhiyun * cbArg3 4 bytes to w rite at LCD[index+4]
470*4882a593Smuzhiyun * cbArg4 4 bytes to w rite at LCD[index+8]
471*4882a593Smuzhiyun * cbRes1 Standard return codes (0, -1, -2)
472*4882a593Smuzhiyun *
473*4882a593Smuzhiyun * Populate Local Config Data from NVRAM
474*4882a593Smuzhiyun * cbArg1, byte0 = 0x12
475*4882a593Smuzhiyun * cbRes1 Standard return codes (0, -1, -2)
476*4882a593Smuzhiyun *
477*4882a593Smuzhiyun * Commit Local Config Data to NVRAM
478*4882a593Smuzhiyun * cbArg1, byte0 = 0x13
479*4882a593Smuzhiyun * cbRes1 Standard return codes (0, -1, -2)
480*4882a593Smuzhiyun */
481*4882a593Smuzhiyun
dell_rfkill_set(void * data,bool blocked)482*4882a593Smuzhiyun static int dell_rfkill_set(void *data, bool blocked)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun int disable = blocked ? 1 : 0;
485*4882a593Smuzhiyun unsigned long radio = (unsigned long)data;
486*4882a593Smuzhiyun int hwswitch_bit = (unsigned long)data - 1;
487*4882a593Smuzhiyun struct calling_interface_buffer buffer;
488*4882a593Smuzhiyun int hwswitch;
489*4882a593Smuzhiyun int status;
490*4882a593Smuzhiyun int ret;
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun dell_fill_request(&buffer, 0, 0, 0, 0);
493*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
494*4882a593Smuzhiyun if (ret)
495*4882a593Smuzhiyun return ret;
496*4882a593Smuzhiyun status = buffer.output[1];
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun dell_fill_request(&buffer, 0x2, 0, 0, 0);
499*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
500*4882a593Smuzhiyun if (ret)
501*4882a593Smuzhiyun return ret;
502*4882a593Smuzhiyun hwswitch = buffer.output[1];
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun /* If the hardware switch controls this radio, and the hardware
505*4882a593Smuzhiyun switch is disabled, always disable the radio */
506*4882a593Smuzhiyun if (ret == 0 && (hwswitch & BIT(hwswitch_bit)) &&
507*4882a593Smuzhiyun (status & BIT(0)) && !(status & BIT(16)))
508*4882a593Smuzhiyun disable = 1;
509*4882a593Smuzhiyun
510*4882a593Smuzhiyun dell_fill_request(&buffer, 1 | (radio<<8) | (disable << 16), 0, 0, 0);
511*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
512*4882a593Smuzhiyun return ret;
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun
dell_rfkill_update_sw_state(struct rfkill * rfkill,int radio,int status)515*4882a593Smuzhiyun static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
516*4882a593Smuzhiyun int status)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun if (status & BIT(0)) {
519*4882a593Smuzhiyun /* Has hw-switch, sync sw_state to BIOS */
520*4882a593Smuzhiyun struct calling_interface_buffer buffer;
521*4882a593Smuzhiyun int block = rfkill_blocked(rfkill);
522*4882a593Smuzhiyun dell_fill_request(&buffer,
523*4882a593Smuzhiyun 1 | (radio << 8) | (block << 16), 0, 0, 0);
524*4882a593Smuzhiyun dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
525*4882a593Smuzhiyun } else {
526*4882a593Smuzhiyun /* No hw-switch, sync BIOS state to sw_state */
527*4882a593Smuzhiyun rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun }
530*4882a593Smuzhiyun
dell_rfkill_update_hw_state(struct rfkill * rfkill,int radio,int status,int hwswitch)531*4882a593Smuzhiyun static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
532*4882a593Smuzhiyun int status, int hwswitch)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun if (hwswitch & (BIT(radio - 1)))
535*4882a593Smuzhiyun rfkill_set_hw_state(rfkill, !(status & BIT(16)));
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
dell_rfkill_query(struct rfkill * rfkill,void * data)538*4882a593Smuzhiyun static void dell_rfkill_query(struct rfkill *rfkill, void *data)
539*4882a593Smuzhiyun {
540*4882a593Smuzhiyun int radio = ((unsigned long)data & 0xF);
541*4882a593Smuzhiyun struct calling_interface_buffer buffer;
542*4882a593Smuzhiyun int hwswitch;
543*4882a593Smuzhiyun int status;
544*4882a593Smuzhiyun int ret;
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun dell_fill_request(&buffer, 0, 0, 0, 0);
547*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
548*4882a593Smuzhiyun status = buffer.output[1];
549*4882a593Smuzhiyun
550*4882a593Smuzhiyun if (ret != 0 || !(status & BIT(0))) {
551*4882a593Smuzhiyun return;
552*4882a593Smuzhiyun }
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun dell_fill_request(&buffer, 0x2, 0, 0, 0);
555*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
556*4882a593Smuzhiyun hwswitch = buffer.output[1];
557*4882a593Smuzhiyun
558*4882a593Smuzhiyun if (ret != 0)
559*4882a593Smuzhiyun return;
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun dell_rfkill_update_hw_state(rfkill, radio, status, hwswitch);
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun static const struct rfkill_ops dell_rfkill_ops = {
565*4882a593Smuzhiyun .set_block = dell_rfkill_set,
566*4882a593Smuzhiyun .query = dell_rfkill_query,
567*4882a593Smuzhiyun };
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun static struct dentry *dell_laptop_dir;
570*4882a593Smuzhiyun
dell_debugfs_show(struct seq_file * s,void * data)571*4882a593Smuzhiyun static int dell_debugfs_show(struct seq_file *s, void *data)
572*4882a593Smuzhiyun {
573*4882a593Smuzhiyun struct calling_interface_buffer buffer;
574*4882a593Smuzhiyun int hwswitch_state;
575*4882a593Smuzhiyun int hwswitch_ret;
576*4882a593Smuzhiyun int status;
577*4882a593Smuzhiyun int ret;
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun dell_fill_request(&buffer, 0, 0, 0, 0);
580*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
581*4882a593Smuzhiyun if (ret)
582*4882a593Smuzhiyun return ret;
583*4882a593Smuzhiyun status = buffer.output[1];
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun dell_fill_request(&buffer, 0x2, 0, 0, 0);
586*4882a593Smuzhiyun hwswitch_ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
587*4882a593Smuzhiyun if (hwswitch_ret)
588*4882a593Smuzhiyun return hwswitch_ret;
589*4882a593Smuzhiyun hwswitch_state = buffer.output[1];
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun seq_printf(s, "return:\t%d\n", ret);
592*4882a593Smuzhiyun seq_printf(s, "status:\t0x%X\n", status);
593*4882a593Smuzhiyun seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
594*4882a593Smuzhiyun status & BIT(0));
595*4882a593Smuzhiyun seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
596*4882a593Smuzhiyun (status & BIT(1)) >> 1);
597*4882a593Smuzhiyun seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
598*4882a593Smuzhiyun (status & BIT(2)) >> 2);
599*4882a593Smuzhiyun seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
600*4882a593Smuzhiyun (status & BIT(3)) >> 3);
601*4882a593Smuzhiyun seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
602*4882a593Smuzhiyun (status & BIT(4)) >> 4);
603*4882a593Smuzhiyun seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
604*4882a593Smuzhiyun (status & BIT(5)) >> 5);
605*4882a593Smuzhiyun seq_printf(s, "Bit 6 : UWB supported: %lu\n",
606*4882a593Smuzhiyun (status & BIT(6)) >> 6);
607*4882a593Smuzhiyun seq_printf(s, "Bit 7 : WiGig supported: %lu\n",
608*4882a593Smuzhiyun (status & BIT(7)) >> 7);
609*4882a593Smuzhiyun seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
610*4882a593Smuzhiyun (status & BIT(8)) >> 8);
611*4882a593Smuzhiyun seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
612*4882a593Smuzhiyun (status & BIT(9)) >> 9);
613*4882a593Smuzhiyun seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
614*4882a593Smuzhiyun (status & BIT(10)) >> 10);
615*4882a593Smuzhiyun seq_printf(s, "Bit 11: UWB installed: %lu\n",
616*4882a593Smuzhiyun (status & BIT(11)) >> 11);
617*4882a593Smuzhiyun seq_printf(s, "Bit 12: WiGig installed: %lu\n",
618*4882a593Smuzhiyun (status & BIT(12)) >> 12);
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
621*4882a593Smuzhiyun (status & BIT(16)) >> 16);
622*4882a593Smuzhiyun seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
623*4882a593Smuzhiyun (status & BIT(17)) >> 17);
624*4882a593Smuzhiyun seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
625*4882a593Smuzhiyun (status & BIT(18)) >> 18);
626*4882a593Smuzhiyun seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
627*4882a593Smuzhiyun (status & BIT(19)) >> 19);
628*4882a593Smuzhiyun seq_printf(s, "Bit 20: UWB is blocked: %lu\n",
629*4882a593Smuzhiyun (status & BIT(20)) >> 20);
630*4882a593Smuzhiyun seq_printf(s, "Bit 21: WiGig is blocked: %lu\n",
631*4882a593Smuzhiyun (status & BIT(21)) >> 21);
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun seq_printf(s, "\nhwswitch_return:\t%d\n", hwswitch_ret);
634*4882a593Smuzhiyun seq_printf(s, "hwswitch_state:\t0x%X\n", hwswitch_state);
635*4882a593Smuzhiyun seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
636*4882a593Smuzhiyun hwswitch_state & BIT(0));
637*4882a593Smuzhiyun seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
638*4882a593Smuzhiyun (hwswitch_state & BIT(1)) >> 1);
639*4882a593Smuzhiyun seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
640*4882a593Smuzhiyun (hwswitch_state & BIT(2)) >> 2);
641*4882a593Smuzhiyun seq_printf(s, "Bit 3 : UWB controlled by switch: %lu\n",
642*4882a593Smuzhiyun (hwswitch_state & BIT(3)) >> 3);
643*4882a593Smuzhiyun seq_printf(s, "Bit 4 : WiGig controlled by switch: %lu\n",
644*4882a593Smuzhiyun (hwswitch_state & BIT(4)) >> 4);
645*4882a593Smuzhiyun seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
646*4882a593Smuzhiyun (hwswitch_state & BIT(7)) >> 7);
647*4882a593Smuzhiyun seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
648*4882a593Smuzhiyun (hwswitch_state & BIT(8)) >> 8);
649*4882a593Smuzhiyun seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
650*4882a593Smuzhiyun (hwswitch_state & BIT(15)) >> 15);
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun return 0;
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun DEFINE_SHOW_ATTRIBUTE(dell_debugfs);
655*4882a593Smuzhiyun
dell_update_rfkill(struct work_struct * ignored)656*4882a593Smuzhiyun static void dell_update_rfkill(struct work_struct *ignored)
657*4882a593Smuzhiyun {
658*4882a593Smuzhiyun struct calling_interface_buffer buffer;
659*4882a593Smuzhiyun int hwswitch = 0;
660*4882a593Smuzhiyun int status;
661*4882a593Smuzhiyun int ret;
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun dell_fill_request(&buffer, 0, 0, 0, 0);
664*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
665*4882a593Smuzhiyun status = buffer.output[1];
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun if (ret != 0)
668*4882a593Smuzhiyun return;
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun dell_fill_request(&buffer, 0x2, 0, 0, 0);
671*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun if (ret == 0 && (status & BIT(0)))
674*4882a593Smuzhiyun hwswitch = buffer.output[1];
675*4882a593Smuzhiyun
676*4882a593Smuzhiyun if (wifi_rfkill) {
677*4882a593Smuzhiyun dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
678*4882a593Smuzhiyun dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
679*4882a593Smuzhiyun }
680*4882a593Smuzhiyun if (bluetooth_rfkill) {
681*4882a593Smuzhiyun dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
682*4882a593Smuzhiyun hwswitch);
683*4882a593Smuzhiyun dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun if (wwan_rfkill) {
686*4882a593Smuzhiyun dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
687*4882a593Smuzhiyun dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun }
690*4882a593Smuzhiyun static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
691*4882a593Smuzhiyun
dell_laptop_i8042_filter(unsigned char data,unsigned char str,struct serio * port)692*4882a593Smuzhiyun static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
693*4882a593Smuzhiyun struct serio *port)
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun static bool extended;
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun if (str & I8042_STR_AUXDATA)
698*4882a593Smuzhiyun return false;
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun if (unlikely(data == 0xe0)) {
701*4882a593Smuzhiyun extended = true;
702*4882a593Smuzhiyun return false;
703*4882a593Smuzhiyun } else if (unlikely(extended)) {
704*4882a593Smuzhiyun switch (data) {
705*4882a593Smuzhiyun case 0x8:
706*4882a593Smuzhiyun schedule_delayed_work(&dell_rfkill_work,
707*4882a593Smuzhiyun round_jiffies_relative(HZ / 4));
708*4882a593Smuzhiyun break;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun extended = false;
711*4882a593Smuzhiyun }
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun return false;
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun static int (*dell_rbtn_notifier_register_func)(struct notifier_block *);
717*4882a593Smuzhiyun static int (*dell_rbtn_notifier_unregister_func)(struct notifier_block *);
718*4882a593Smuzhiyun
dell_laptop_rbtn_notifier_call(struct notifier_block * nb,unsigned long action,void * data)719*4882a593Smuzhiyun static int dell_laptop_rbtn_notifier_call(struct notifier_block *nb,
720*4882a593Smuzhiyun unsigned long action, void *data)
721*4882a593Smuzhiyun {
722*4882a593Smuzhiyun schedule_delayed_work(&dell_rfkill_work, 0);
723*4882a593Smuzhiyun return NOTIFY_OK;
724*4882a593Smuzhiyun }
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun static struct notifier_block dell_laptop_rbtn_notifier = {
727*4882a593Smuzhiyun .notifier_call = dell_laptop_rbtn_notifier_call,
728*4882a593Smuzhiyun };
729*4882a593Smuzhiyun
dell_setup_rfkill(void)730*4882a593Smuzhiyun static int __init dell_setup_rfkill(void)
731*4882a593Smuzhiyun {
732*4882a593Smuzhiyun struct calling_interface_buffer buffer;
733*4882a593Smuzhiyun int status, ret, whitelisted;
734*4882a593Smuzhiyun const char *product;
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun /*
737*4882a593Smuzhiyun * rfkill support causes trouble on various models, mostly Inspirons.
738*4882a593Smuzhiyun * So we whitelist certain series, and don't support rfkill on others.
739*4882a593Smuzhiyun */
740*4882a593Smuzhiyun whitelisted = 0;
741*4882a593Smuzhiyun product = dmi_get_system_info(DMI_PRODUCT_NAME);
742*4882a593Smuzhiyun if (product && (strncmp(product, "Latitude", 8) == 0 ||
743*4882a593Smuzhiyun strncmp(product, "Precision", 9) == 0))
744*4882a593Smuzhiyun whitelisted = 1;
745*4882a593Smuzhiyun if (!force_rfkill && !whitelisted)
746*4882a593Smuzhiyun return 0;
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun dell_fill_request(&buffer, 0, 0, 0, 0);
749*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_INFO, SELECT_RFKILL);
750*4882a593Smuzhiyun status = buffer.output[1];
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun /* dell wireless info smbios call is not supported */
753*4882a593Smuzhiyun if (ret != 0)
754*4882a593Smuzhiyun return 0;
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun /* rfkill is only tested on laptops with a hwswitch */
757*4882a593Smuzhiyun if (!(status & BIT(0)) && !force_rfkill)
758*4882a593Smuzhiyun return 0;
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
761*4882a593Smuzhiyun wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
762*4882a593Smuzhiyun RFKILL_TYPE_WLAN,
763*4882a593Smuzhiyun &dell_rfkill_ops, (void *) 1);
764*4882a593Smuzhiyun if (!wifi_rfkill) {
765*4882a593Smuzhiyun ret = -ENOMEM;
766*4882a593Smuzhiyun goto err_wifi;
767*4882a593Smuzhiyun }
768*4882a593Smuzhiyun ret = rfkill_register(wifi_rfkill);
769*4882a593Smuzhiyun if (ret)
770*4882a593Smuzhiyun goto err_wifi;
771*4882a593Smuzhiyun }
772*4882a593Smuzhiyun
773*4882a593Smuzhiyun if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
774*4882a593Smuzhiyun bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
775*4882a593Smuzhiyun &platform_device->dev,
776*4882a593Smuzhiyun RFKILL_TYPE_BLUETOOTH,
777*4882a593Smuzhiyun &dell_rfkill_ops, (void *) 2);
778*4882a593Smuzhiyun if (!bluetooth_rfkill) {
779*4882a593Smuzhiyun ret = -ENOMEM;
780*4882a593Smuzhiyun goto err_bluetooth;
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun ret = rfkill_register(bluetooth_rfkill);
783*4882a593Smuzhiyun if (ret)
784*4882a593Smuzhiyun goto err_bluetooth;
785*4882a593Smuzhiyun }
786*4882a593Smuzhiyun
787*4882a593Smuzhiyun if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
788*4882a593Smuzhiyun wwan_rfkill = rfkill_alloc("dell-wwan",
789*4882a593Smuzhiyun &platform_device->dev,
790*4882a593Smuzhiyun RFKILL_TYPE_WWAN,
791*4882a593Smuzhiyun &dell_rfkill_ops, (void *) 3);
792*4882a593Smuzhiyun if (!wwan_rfkill) {
793*4882a593Smuzhiyun ret = -ENOMEM;
794*4882a593Smuzhiyun goto err_wwan;
795*4882a593Smuzhiyun }
796*4882a593Smuzhiyun ret = rfkill_register(wwan_rfkill);
797*4882a593Smuzhiyun if (ret)
798*4882a593Smuzhiyun goto err_wwan;
799*4882a593Smuzhiyun }
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun /*
802*4882a593Smuzhiyun * Dell Airplane Mode Switch driver (dell-rbtn) supports ACPI devices
803*4882a593Smuzhiyun * which can receive events from HW slider switch.
804*4882a593Smuzhiyun *
805*4882a593Smuzhiyun * Dell SMBIOS on whitelisted models supports controlling radio devices
806*4882a593Smuzhiyun * but does not support receiving HW button switch events. We can use
807*4882a593Smuzhiyun * i8042 filter hook function to receive keyboard data and handle
808*4882a593Smuzhiyun * keycode for HW button.
809*4882a593Smuzhiyun *
810*4882a593Smuzhiyun * So if it is possible we will use Dell Airplane Mode Switch ACPI
811*4882a593Smuzhiyun * driver for receiving HW events and Dell SMBIOS for setting rfkill
812*4882a593Smuzhiyun * states. If ACPI driver or device is not available we will fallback to
813*4882a593Smuzhiyun * i8042 filter hook function.
814*4882a593Smuzhiyun *
815*4882a593Smuzhiyun * To prevent duplicate rfkill devices which control and do same thing,
816*4882a593Smuzhiyun * dell-rbtn driver will automatically remove its own rfkill devices
817*4882a593Smuzhiyun * once function dell_rbtn_notifier_register() is called.
818*4882a593Smuzhiyun */
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun dell_rbtn_notifier_register_func =
821*4882a593Smuzhiyun symbol_request(dell_rbtn_notifier_register);
822*4882a593Smuzhiyun if (dell_rbtn_notifier_register_func) {
823*4882a593Smuzhiyun dell_rbtn_notifier_unregister_func =
824*4882a593Smuzhiyun symbol_request(dell_rbtn_notifier_unregister);
825*4882a593Smuzhiyun if (!dell_rbtn_notifier_unregister_func) {
826*4882a593Smuzhiyun symbol_put(dell_rbtn_notifier_register);
827*4882a593Smuzhiyun dell_rbtn_notifier_register_func = NULL;
828*4882a593Smuzhiyun }
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun
831*4882a593Smuzhiyun if (dell_rbtn_notifier_register_func) {
832*4882a593Smuzhiyun ret = dell_rbtn_notifier_register_func(
833*4882a593Smuzhiyun &dell_laptop_rbtn_notifier);
834*4882a593Smuzhiyun symbol_put(dell_rbtn_notifier_register);
835*4882a593Smuzhiyun dell_rbtn_notifier_register_func = NULL;
836*4882a593Smuzhiyun if (ret != 0) {
837*4882a593Smuzhiyun symbol_put(dell_rbtn_notifier_unregister);
838*4882a593Smuzhiyun dell_rbtn_notifier_unregister_func = NULL;
839*4882a593Smuzhiyun }
840*4882a593Smuzhiyun } else {
841*4882a593Smuzhiyun pr_info("Symbols from dell-rbtn acpi driver are not available\n");
842*4882a593Smuzhiyun ret = -ENODEV;
843*4882a593Smuzhiyun }
844*4882a593Smuzhiyun
845*4882a593Smuzhiyun if (ret == 0) {
846*4882a593Smuzhiyun pr_info("Using dell-rbtn acpi driver for receiving events\n");
847*4882a593Smuzhiyun } else if (ret != -ENODEV) {
848*4882a593Smuzhiyun pr_warn("Unable to register dell rbtn notifier\n");
849*4882a593Smuzhiyun goto err_filter;
850*4882a593Smuzhiyun } else {
851*4882a593Smuzhiyun ret = i8042_install_filter(dell_laptop_i8042_filter);
852*4882a593Smuzhiyun if (ret) {
853*4882a593Smuzhiyun pr_warn("Unable to install key filter\n");
854*4882a593Smuzhiyun goto err_filter;
855*4882a593Smuzhiyun }
856*4882a593Smuzhiyun pr_info("Using i8042 filter function for receiving events\n");
857*4882a593Smuzhiyun }
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun return 0;
860*4882a593Smuzhiyun err_filter:
861*4882a593Smuzhiyun if (wwan_rfkill)
862*4882a593Smuzhiyun rfkill_unregister(wwan_rfkill);
863*4882a593Smuzhiyun err_wwan:
864*4882a593Smuzhiyun rfkill_destroy(wwan_rfkill);
865*4882a593Smuzhiyun if (bluetooth_rfkill)
866*4882a593Smuzhiyun rfkill_unregister(bluetooth_rfkill);
867*4882a593Smuzhiyun err_bluetooth:
868*4882a593Smuzhiyun rfkill_destroy(bluetooth_rfkill);
869*4882a593Smuzhiyun if (wifi_rfkill)
870*4882a593Smuzhiyun rfkill_unregister(wifi_rfkill);
871*4882a593Smuzhiyun err_wifi:
872*4882a593Smuzhiyun rfkill_destroy(wifi_rfkill);
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun return ret;
875*4882a593Smuzhiyun }
876*4882a593Smuzhiyun
dell_cleanup_rfkill(void)877*4882a593Smuzhiyun static void dell_cleanup_rfkill(void)
878*4882a593Smuzhiyun {
879*4882a593Smuzhiyun if (dell_rbtn_notifier_unregister_func) {
880*4882a593Smuzhiyun dell_rbtn_notifier_unregister_func(&dell_laptop_rbtn_notifier);
881*4882a593Smuzhiyun symbol_put(dell_rbtn_notifier_unregister);
882*4882a593Smuzhiyun dell_rbtn_notifier_unregister_func = NULL;
883*4882a593Smuzhiyun } else {
884*4882a593Smuzhiyun i8042_remove_filter(dell_laptop_i8042_filter);
885*4882a593Smuzhiyun }
886*4882a593Smuzhiyun cancel_delayed_work_sync(&dell_rfkill_work);
887*4882a593Smuzhiyun if (wifi_rfkill) {
888*4882a593Smuzhiyun rfkill_unregister(wifi_rfkill);
889*4882a593Smuzhiyun rfkill_destroy(wifi_rfkill);
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun if (bluetooth_rfkill) {
892*4882a593Smuzhiyun rfkill_unregister(bluetooth_rfkill);
893*4882a593Smuzhiyun rfkill_destroy(bluetooth_rfkill);
894*4882a593Smuzhiyun }
895*4882a593Smuzhiyun if (wwan_rfkill) {
896*4882a593Smuzhiyun rfkill_unregister(wwan_rfkill);
897*4882a593Smuzhiyun rfkill_destroy(wwan_rfkill);
898*4882a593Smuzhiyun }
899*4882a593Smuzhiyun }
900*4882a593Smuzhiyun
dell_send_intensity(struct backlight_device * bd)901*4882a593Smuzhiyun static int dell_send_intensity(struct backlight_device *bd)
902*4882a593Smuzhiyun {
903*4882a593Smuzhiyun struct calling_interface_buffer buffer;
904*4882a593Smuzhiyun struct calling_interface_token *token;
905*4882a593Smuzhiyun int ret;
906*4882a593Smuzhiyun
907*4882a593Smuzhiyun token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
908*4882a593Smuzhiyun if (!token)
909*4882a593Smuzhiyun return -ENODEV;
910*4882a593Smuzhiyun
911*4882a593Smuzhiyun dell_fill_request(&buffer,
912*4882a593Smuzhiyun token->location, bd->props.brightness, 0, 0);
913*4882a593Smuzhiyun if (power_supply_is_system_supplied() > 0)
914*4882a593Smuzhiyun ret = dell_send_request(&buffer,
915*4882a593Smuzhiyun CLASS_TOKEN_WRITE, SELECT_TOKEN_AC);
916*4882a593Smuzhiyun else
917*4882a593Smuzhiyun ret = dell_send_request(&buffer,
918*4882a593Smuzhiyun CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT);
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun return ret;
921*4882a593Smuzhiyun }
922*4882a593Smuzhiyun
dell_get_intensity(struct backlight_device * bd)923*4882a593Smuzhiyun static int dell_get_intensity(struct backlight_device *bd)
924*4882a593Smuzhiyun {
925*4882a593Smuzhiyun struct calling_interface_buffer buffer;
926*4882a593Smuzhiyun struct calling_interface_token *token;
927*4882a593Smuzhiyun int ret;
928*4882a593Smuzhiyun
929*4882a593Smuzhiyun token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
930*4882a593Smuzhiyun if (!token)
931*4882a593Smuzhiyun return -ENODEV;
932*4882a593Smuzhiyun
933*4882a593Smuzhiyun dell_fill_request(&buffer, token->location, 0, 0, 0);
934*4882a593Smuzhiyun if (power_supply_is_system_supplied() > 0)
935*4882a593Smuzhiyun ret = dell_send_request(&buffer,
936*4882a593Smuzhiyun CLASS_TOKEN_READ, SELECT_TOKEN_AC);
937*4882a593Smuzhiyun else
938*4882a593Smuzhiyun ret = dell_send_request(&buffer,
939*4882a593Smuzhiyun CLASS_TOKEN_READ, SELECT_TOKEN_BAT);
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun if (ret == 0)
942*4882a593Smuzhiyun ret = buffer.output[1];
943*4882a593Smuzhiyun
944*4882a593Smuzhiyun return ret;
945*4882a593Smuzhiyun }
946*4882a593Smuzhiyun
947*4882a593Smuzhiyun static const struct backlight_ops dell_ops = {
948*4882a593Smuzhiyun .get_brightness = dell_get_intensity,
949*4882a593Smuzhiyun .update_status = dell_send_intensity,
950*4882a593Smuzhiyun };
951*4882a593Smuzhiyun
touchpad_led_on(void)952*4882a593Smuzhiyun static void touchpad_led_on(void)
953*4882a593Smuzhiyun {
954*4882a593Smuzhiyun int command = 0x97;
955*4882a593Smuzhiyun char data = 1;
956*4882a593Smuzhiyun i8042_command(&data, command | 1 << 12);
957*4882a593Smuzhiyun }
958*4882a593Smuzhiyun
touchpad_led_off(void)959*4882a593Smuzhiyun static void touchpad_led_off(void)
960*4882a593Smuzhiyun {
961*4882a593Smuzhiyun int command = 0x97;
962*4882a593Smuzhiyun char data = 2;
963*4882a593Smuzhiyun i8042_command(&data, command | 1 << 12);
964*4882a593Smuzhiyun }
965*4882a593Smuzhiyun
touchpad_led_set(struct led_classdev * led_cdev,enum led_brightness value)966*4882a593Smuzhiyun static void touchpad_led_set(struct led_classdev *led_cdev,
967*4882a593Smuzhiyun enum led_brightness value)
968*4882a593Smuzhiyun {
969*4882a593Smuzhiyun if (value > 0)
970*4882a593Smuzhiyun touchpad_led_on();
971*4882a593Smuzhiyun else
972*4882a593Smuzhiyun touchpad_led_off();
973*4882a593Smuzhiyun }
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun static struct led_classdev touchpad_led = {
976*4882a593Smuzhiyun .name = "dell-laptop::touchpad",
977*4882a593Smuzhiyun .brightness_set = touchpad_led_set,
978*4882a593Smuzhiyun .flags = LED_CORE_SUSPENDRESUME,
979*4882a593Smuzhiyun };
980*4882a593Smuzhiyun
touchpad_led_init(struct device * dev)981*4882a593Smuzhiyun static int __init touchpad_led_init(struct device *dev)
982*4882a593Smuzhiyun {
983*4882a593Smuzhiyun return led_classdev_register(dev, &touchpad_led);
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun
touchpad_led_exit(void)986*4882a593Smuzhiyun static void touchpad_led_exit(void)
987*4882a593Smuzhiyun {
988*4882a593Smuzhiyun led_classdev_unregister(&touchpad_led);
989*4882a593Smuzhiyun }
990*4882a593Smuzhiyun
991*4882a593Smuzhiyun /*
992*4882a593Smuzhiyun * Derived from information in smbios-keyboard-ctl:
993*4882a593Smuzhiyun *
994*4882a593Smuzhiyun * cbClass 4
995*4882a593Smuzhiyun * cbSelect 11
996*4882a593Smuzhiyun * Keyboard illumination
997*4882a593Smuzhiyun * cbArg1 determines the function to be performed
998*4882a593Smuzhiyun *
999*4882a593Smuzhiyun * cbArg1 0x0 = Get Feature Information
1000*4882a593Smuzhiyun * cbRES1 Standard return codes (0, -1, -2)
1001*4882a593Smuzhiyun * cbRES2, word0 Bitmap of user-selectable modes
1002*4882a593Smuzhiyun * bit 0 Always off (All systems)
1003*4882a593Smuzhiyun * bit 1 Always on (Travis ATG, Siberia)
1004*4882a593Smuzhiyun * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG)
1005*4882a593Smuzhiyun * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off
1006*4882a593Smuzhiyun * bit 4 Auto: Input-activity-based On; input-activity based Off
1007*4882a593Smuzhiyun * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off
1008*4882a593Smuzhiyun * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off
1009*4882a593Smuzhiyun * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off
1010*4882a593Smuzhiyun * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off
1011*4882a593Smuzhiyun * bits 9-15 Reserved for future use
1012*4882a593Smuzhiyun * cbRES2, byte2 Reserved for future use
1013*4882a593Smuzhiyun * cbRES2, byte3 Keyboard illumination type
1014*4882a593Smuzhiyun * 0 Reserved
1015*4882a593Smuzhiyun * 1 Tasklight
1016*4882a593Smuzhiyun * 2 Backlight
1017*4882a593Smuzhiyun * 3-255 Reserved for future use
1018*4882a593Smuzhiyun * cbRES3, byte0 Supported auto keyboard illumination trigger bitmap.
1019*4882a593Smuzhiyun * bit 0 Any keystroke
1020*4882a593Smuzhiyun * bit 1 Touchpad activity
1021*4882a593Smuzhiyun * bit 2 Pointing stick
1022*4882a593Smuzhiyun * bit 3 Any mouse
1023*4882a593Smuzhiyun * bits 4-7 Reserved for future use
1024*4882a593Smuzhiyun * cbRES3, byte1 Supported timeout unit bitmap
1025*4882a593Smuzhiyun * bit 0 Seconds
1026*4882a593Smuzhiyun * bit 1 Minutes
1027*4882a593Smuzhiyun * bit 2 Hours
1028*4882a593Smuzhiyun * bit 3 Days
1029*4882a593Smuzhiyun * bits 4-7 Reserved for future use
1030*4882a593Smuzhiyun * cbRES3, byte2 Number of keyboard light brightness levels
1031*4882a593Smuzhiyun * cbRES4, byte0 Maximum acceptable seconds value (0 if seconds not supported).
1032*4882a593Smuzhiyun * cbRES4, byte1 Maximum acceptable minutes value (0 if minutes not supported).
1033*4882a593Smuzhiyun * cbRES4, byte2 Maximum acceptable hours value (0 if hours not supported).
1034*4882a593Smuzhiyun * cbRES4, byte3 Maximum acceptable days value (0 if days not supported)
1035*4882a593Smuzhiyun *
1036*4882a593Smuzhiyun * cbArg1 0x1 = Get Current State
1037*4882a593Smuzhiyun * cbRES1 Standard return codes (0, -1, -2)
1038*4882a593Smuzhiyun * cbRES2, word0 Bitmap of current mode state
1039*4882a593Smuzhiyun * bit 0 Always off (All systems)
1040*4882a593Smuzhiyun * bit 1 Always on (Travis ATG, Siberia)
1041*4882a593Smuzhiyun * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG)
1042*4882a593Smuzhiyun * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off
1043*4882a593Smuzhiyun * bit 4 Auto: Input-activity-based On; input-activity based Off
1044*4882a593Smuzhiyun * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off
1045*4882a593Smuzhiyun * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off
1046*4882a593Smuzhiyun * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off
1047*4882a593Smuzhiyun * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off
1048*4882a593Smuzhiyun * bits 9-15 Reserved for future use
1049*4882a593Smuzhiyun * Note: Only One bit can be set
1050*4882a593Smuzhiyun * cbRES2, byte2 Currently active auto keyboard illumination triggers.
1051*4882a593Smuzhiyun * bit 0 Any keystroke
1052*4882a593Smuzhiyun * bit 1 Touchpad activity
1053*4882a593Smuzhiyun * bit 2 Pointing stick
1054*4882a593Smuzhiyun * bit 3 Any mouse
1055*4882a593Smuzhiyun * bits 4-7 Reserved for future use
1056*4882a593Smuzhiyun * cbRES2, byte3 Current Timeout on battery
1057*4882a593Smuzhiyun * bits 7:6 Timeout units indicator:
1058*4882a593Smuzhiyun * 00b Seconds
1059*4882a593Smuzhiyun * 01b Minutes
1060*4882a593Smuzhiyun * 10b Hours
1061*4882a593Smuzhiyun * 11b Days
1062*4882a593Smuzhiyun * bits 5:0 Timeout value (0-63) in sec/min/hr/day
1063*4882a593Smuzhiyun * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte
1064*4882a593Smuzhiyun * are set upon return from the [Get feature information] call.
1065*4882a593Smuzhiyun * cbRES3, byte0 Current setting of ALS value that turns the light on or off.
1066*4882a593Smuzhiyun * cbRES3, byte1 Current ALS reading
1067*4882a593Smuzhiyun * cbRES3, byte2 Current keyboard light level.
1068*4882a593Smuzhiyun * cbRES3, byte3 Current timeout on AC Power
1069*4882a593Smuzhiyun * bits 7:6 Timeout units indicator:
1070*4882a593Smuzhiyun * 00b Seconds
1071*4882a593Smuzhiyun * 01b Minutes
1072*4882a593Smuzhiyun * 10b Hours
1073*4882a593Smuzhiyun * 11b Days
1074*4882a593Smuzhiyun * Bits 5:0 Timeout value (0-63) in sec/min/hr/day
1075*4882a593Smuzhiyun * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte2
1076*4882a593Smuzhiyun * are set upon return from the upon return from the [Get Feature information] call.
1077*4882a593Smuzhiyun *
1078*4882a593Smuzhiyun * cbArg1 0x2 = Set New State
1079*4882a593Smuzhiyun * cbRES1 Standard return codes (0, -1, -2)
1080*4882a593Smuzhiyun * cbArg2, word0 Bitmap of current mode state
1081*4882a593Smuzhiyun * bit 0 Always off (All systems)
1082*4882a593Smuzhiyun * bit 1 Always on (Travis ATG, Siberia)
1083*4882a593Smuzhiyun * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG)
1084*4882a593Smuzhiyun * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off
1085*4882a593Smuzhiyun * bit 4 Auto: Input-activity-based On; input-activity based Off
1086*4882a593Smuzhiyun * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off
1087*4882a593Smuzhiyun * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off
1088*4882a593Smuzhiyun * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off
1089*4882a593Smuzhiyun * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off
1090*4882a593Smuzhiyun * bits 9-15 Reserved for future use
1091*4882a593Smuzhiyun * Note: Only One bit can be set
1092*4882a593Smuzhiyun * cbArg2, byte2 Desired auto keyboard illumination triggers. Must remain inactive to allow
1093*4882a593Smuzhiyun * keyboard to turn off automatically.
1094*4882a593Smuzhiyun * bit 0 Any keystroke
1095*4882a593Smuzhiyun * bit 1 Touchpad activity
1096*4882a593Smuzhiyun * bit 2 Pointing stick
1097*4882a593Smuzhiyun * bit 3 Any mouse
1098*4882a593Smuzhiyun * bits 4-7 Reserved for future use
1099*4882a593Smuzhiyun * cbArg2, byte3 Desired Timeout on battery
1100*4882a593Smuzhiyun * bits 7:6 Timeout units indicator:
1101*4882a593Smuzhiyun * 00b Seconds
1102*4882a593Smuzhiyun * 01b Minutes
1103*4882a593Smuzhiyun * 10b Hours
1104*4882a593Smuzhiyun * 11b Days
1105*4882a593Smuzhiyun * bits 5:0 Timeout value (0-63) in sec/min/hr/day
1106*4882a593Smuzhiyun * cbArg3, byte0 Desired setting of ALS value that turns the light on or off.
1107*4882a593Smuzhiyun * cbArg3, byte2 Desired keyboard light level.
1108*4882a593Smuzhiyun * cbArg3, byte3 Desired Timeout on AC power
1109*4882a593Smuzhiyun * bits 7:6 Timeout units indicator:
1110*4882a593Smuzhiyun * 00b Seconds
1111*4882a593Smuzhiyun * 01b Minutes
1112*4882a593Smuzhiyun * 10b Hours
1113*4882a593Smuzhiyun * 11b Days
1114*4882a593Smuzhiyun * bits 5:0 Timeout value (0-63) in sec/min/hr/day
1115*4882a593Smuzhiyun */
1116*4882a593Smuzhiyun
1117*4882a593Smuzhiyun
1118*4882a593Smuzhiyun enum kbd_timeout_unit {
1119*4882a593Smuzhiyun KBD_TIMEOUT_SECONDS = 0,
1120*4882a593Smuzhiyun KBD_TIMEOUT_MINUTES,
1121*4882a593Smuzhiyun KBD_TIMEOUT_HOURS,
1122*4882a593Smuzhiyun KBD_TIMEOUT_DAYS,
1123*4882a593Smuzhiyun };
1124*4882a593Smuzhiyun
1125*4882a593Smuzhiyun enum kbd_mode_bit {
1126*4882a593Smuzhiyun KBD_MODE_BIT_OFF = 0,
1127*4882a593Smuzhiyun KBD_MODE_BIT_ON,
1128*4882a593Smuzhiyun KBD_MODE_BIT_ALS,
1129*4882a593Smuzhiyun KBD_MODE_BIT_TRIGGER_ALS,
1130*4882a593Smuzhiyun KBD_MODE_BIT_TRIGGER,
1131*4882a593Smuzhiyun KBD_MODE_BIT_TRIGGER_25,
1132*4882a593Smuzhiyun KBD_MODE_BIT_TRIGGER_50,
1133*4882a593Smuzhiyun KBD_MODE_BIT_TRIGGER_75,
1134*4882a593Smuzhiyun KBD_MODE_BIT_TRIGGER_100,
1135*4882a593Smuzhiyun };
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun #define kbd_is_als_mode_bit(bit) \
1138*4882a593Smuzhiyun ((bit) == KBD_MODE_BIT_ALS || (bit) == KBD_MODE_BIT_TRIGGER_ALS)
1139*4882a593Smuzhiyun #define kbd_is_trigger_mode_bit(bit) \
1140*4882a593Smuzhiyun ((bit) >= KBD_MODE_BIT_TRIGGER_ALS && (bit) <= KBD_MODE_BIT_TRIGGER_100)
1141*4882a593Smuzhiyun #define kbd_is_level_mode_bit(bit) \
1142*4882a593Smuzhiyun ((bit) >= KBD_MODE_BIT_TRIGGER_25 && (bit) <= KBD_MODE_BIT_TRIGGER_100)
1143*4882a593Smuzhiyun
1144*4882a593Smuzhiyun struct kbd_info {
1145*4882a593Smuzhiyun u16 modes;
1146*4882a593Smuzhiyun u8 type;
1147*4882a593Smuzhiyun u8 triggers;
1148*4882a593Smuzhiyun u8 levels;
1149*4882a593Smuzhiyun u8 seconds;
1150*4882a593Smuzhiyun u8 minutes;
1151*4882a593Smuzhiyun u8 hours;
1152*4882a593Smuzhiyun u8 days;
1153*4882a593Smuzhiyun };
1154*4882a593Smuzhiyun
1155*4882a593Smuzhiyun struct kbd_state {
1156*4882a593Smuzhiyun u8 mode_bit;
1157*4882a593Smuzhiyun u8 triggers;
1158*4882a593Smuzhiyun u8 timeout_value;
1159*4882a593Smuzhiyun u8 timeout_unit;
1160*4882a593Smuzhiyun u8 timeout_value_ac;
1161*4882a593Smuzhiyun u8 timeout_unit_ac;
1162*4882a593Smuzhiyun u8 als_setting;
1163*4882a593Smuzhiyun u8 als_value;
1164*4882a593Smuzhiyun u8 level;
1165*4882a593Smuzhiyun };
1166*4882a593Smuzhiyun
1167*4882a593Smuzhiyun static const int kbd_tokens[] = {
1168*4882a593Smuzhiyun KBD_LED_OFF_TOKEN,
1169*4882a593Smuzhiyun KBD_LED_AUTO_25_TOKEN,
1170*4882a593Smuzhiyun KBD_LED_AUTO_50_TOKEN,
1171*4882a593Smuzhiyun KBD_LED_AUTO_75_TOKEN,
1172*4882a593Smuzhiyun KBD_LED_AUTO_100_TOKEN,
1173*4882a593Smuzhiyun KBD_LED_ON_TOKEN,
1174*4882a593Smuzhiyun };
1175*4882a593Smuzhiyun
1176*4882a593Smuzhiyun static u16 kbd_token_bits;
1177*4882a593Smuzhiyun
1178*4882a593Smuzhiyun static struct kbd_info kbd_info;
1179*4882a593Smuzhiyun static bool kbd_als_supported;
1180*4882a593Smuzhiyun static bool kbd_triggers_supported;
1181*4882a593Smuzhiyun static bool kbd_timeout_ac_supported;
1182*4882a593Smuzhiyun
1183*4882a593Smuzhiyun static u8 kbd_mode_levels[16];
1184*4882a593Smuzhiyun static int kbd_mode_levels_count;
1185*4882a593Smuzhiyun
1186*4882a593Smuzhiyun static u8 kbd_previous_level;
1187*4882a593Smuzhiyun static u8 kbd_previous_mode_bit;
1188*4882a593Smuzhiyun
1189*4882a593Smuzhiyun static bool kbd_led_present;
1190*4882a593Smuzhiyun static DEFINE_MUTEX(kbd_led_mutex);
1191*4882a593Smuzhiyun static enum led_brightness kbd_led_level;
1192*4882a593Smuzhiyun
1193*4882a593Smuzhiyun /*
1194*4882a593Smuzhiyun * NOTE: there are three ways to set the keyboard backlight level.
1195*4882a593Smuzhiyun * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value).
1196*4882a593Smuzhiyun * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels).
1197*4882a593Smuzhiyun * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens)
1198*4882a593Smuzhiyun *
1199*4882a593Smuzhiyun * There are laptops which support only one of these methods. If we want to
1200*4882a593Smuzhiyun * support as many machines as possible we need to implement all three methods.
1201*4882a593Smuzhiyun * The first two methods use the kbd_state structure. The third uses SMBIOS
1202*4882a593Smuzhiyun * tokens. If kbd_info.levels == 0, the machine does not support setting the
1203*4882a593Smuzhiyun * keyboard backlight level via kbd_state.level.
1204*4882a593Smuzhiyun */
1205*4882a593Smuzhiyun
kbd_get_info(struct kbd_info * info)1206*4882a593Smuzhiyun static int kbd_get_info(struct kbd_info *info)
1207*4882a593Smuzhiyun {
1208*4882a593Smuzhiyun struct calling_interface_buffer buffer;
1209*4882a593Smuzhiyun u8 units;
1210*4882a593Smuzhiyun int ret;
1211*4882a593Smuzhiyun
1212*4882a593Smuzhiyun dell_fill_request(&buffer, 0, 0, 0, 0);
1213*4882a593Smuzhiyun ret = dell_send_request(&buffer,
1214*4882a593Smuzhiyun CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
1215*4882a593Smuzhiyun if (ret)
1216*4882a593Smuzhiyun return ret;
1217*4882a593Smuzhiyun
1218*4882a593Smuzhiyun info->modes = buffer.output[1] & 0xFFFF;
1219*4882a593Smuzhiyun info->type = (buffer.output[1] >> 24) & 0xFF;
1220*4882a593Smuzhiyun info->triggers = buffer.output[2] & 0xFF;
1221*4882a593Smuzhiyun units = (buffer.output[2] >> 8) & 0xFF;
1222*4882a593Smuzhiyun info->levels = (buffer.output[2] >> 16) & 0xFF;
1223*4882a593Smuzhiyun
1224*4882a593Smuzhiyun if (quirks && quirks->kbd_led_levels_off_1 && info->levels)
1225*4882a593Smuzhiyun info->levels--;
1226*4882a593Smuzhiyun
1227*4882a593Smuzhiyun if (units & BIT(0))
1228*4882a593Smuzhiyun info->seconds = (buffer.output[3] >> 0) & 0xFF;
1229*4882a593Smuzhiyun if (units & BIT(1))
1230*4882a593Smuzhiyun info->minutes = (buffer.output[3] >> 8) & 0xFF;
1231*4882a593Smuzhiyun if (units & BIT(2))
1232*4882a593Smuzhiyun info->hours = (buffer.output[3] >> 16) & 0xFF;
1233*4882a593Smuzhiyun if (units & BIT(3))
1234*4882a593Smuzhiyun info->days = (buffer.output[3] >> 24) & 0xFF;
1235*4882a593Smuzhiyun
1236*4882a593Smuzhiyun return ret;
1237*4882a593Smuzhiyun }
1238*4882a593Smuzhiyun
kbd_get_max_level(void)1239*4882a593Smuzhiyun static unsigned int kbd_get_max_level(void)
1240*4882a593Smuzhiyun {
1241*4882a593Smuzhiyun if (kbd_info.levels != 0)
1242*4882a593Smuzhiyun return kbd_info.levels;
1243*4882a593Smuzhiyun if (kbd_mode_levels_count > 0)
1244*4882a593Smuzhiyun return kbd_mode_levels_count - 1;
1245*4882a593Smuzhiyun return 0;
1246*4882a593Smuzhiyun }
1247*4882a593Smuzhiyun
kbd_get_level(struct kbd_state * state)1248*4882a593Smuzhiyun static int kbd_get_level(struct kbd_state *state)
1249*4882a593Smuzhiyun {
1250*4882a593Smuzhiyun int i;
1251*4882a593Smuzhiyun
1252*4882a593Smuzhiyun if (kbd_info.levels != 0)
1253*4882a593Smuzhiyun return state->level;
1254*4882a593Smuzhiyun
1255*4882a593Smuzhiyun if (kbd_mode_levels_count > 0) {
1256*4882a593Smuzhiyun for (i = 0; i < kbd_mode_levels_count; ++i)
1257*4882a593Smuzhiyun if (kbd_mode_levels[i] == state->mode_bit)
1258*4882a593Smuzhiyun return i;
1259*4882a593Smuzhiyun return 0;
1260*4882a593Smuzhiyun }
1261*4882a593Smuzhiyun
1262*4882a593Smuzhiyun return -EINVAL;
1263*4882a593Smuzhiyun }
1264*4882a593Smuzhiyun
kbd_set_level(struct kbd_state * state,u8 level)1265*4882a593Smuzhiyun static int kbd_set_level(struct kbd_state *state, u8 level)
1266*4882a593Smuzhiyun {
1267*4882a593Smuzhiyun if (kbd_info.levels != 0) {
1268*4882a593Smuzhiyun if (level != 0)
1269*4882a593Smuzhiyun kbd_previous_level = level;
1270*4882a593Smuzhiyun if (state->level == level)
1271*4882a593Smuzhiyun return 0;
1272*4882a593Smuzhiyun state->level = level;
1273*4882a593Smuzhiyun if (level != 0 && state->mode_bit == KBD_MODE_BIT_OFF)
1274*4882a593Smuzhiyun state->mode_bit = kbd_previous_mode_bit;
1275*4882a593Smuzhiyun else if (level == 0 && state->mode_bit != KBD_MODE_BIT_OFF) {
1276*4882a593Smuzhiyun kbd_previous_mode_bit = state->mode_bit;
1277*4882a593Smuzhiyun state->mode_bit = KBD_MODE_BIT_OFF;
1278*4882a593Smuzhiyun }
1279*4882a593Smuzhiyun return 0;
1280*4882a593Smuzhiyun }
1281*4882a593Smuzhiyun
1282*4882a593Smuzhiyun if (kbd_mode_levels_count > 0 && level < kbd_mode_levels_count) {
1283*4882a593Smuzhiyun if (level != 0)
1284*4882a593Smuzhiyun kbd_previous_level = level;
1285*4882a593Smuzhiyun state->mode_bit = kbd_mode_levels[level];
1286*4882a593Smuzhiyun return 0;
1287*4882a593Smuzhiyun }
1288*4882a593Smuzhiyun
1289*4882a593Smuzhiyun return -EINVAL;
1290*4882a593Smuzhiyun }
1291*4882a593Smuzhiyun
kbd_get_state(struct kbd_state * state)1292*4882a593Smuzhiyun static int kbd_get_state(struct kbd_state *state)
1293*4882a593Smuzhiyun {
1294*4882a593Smuzhiyun struct calling_interface_buffer buffer;
1295*4882a593Smuzhiyun int ret;
1296*4882a593Smuzhiyun
1297*4882a593Smuzhiyun dell_fill_request(&buffer, 0x1, 0, 0, 0);
1298*4882a593Smuzhiyun ret = dell_send_request(&buffer,
1299*4882a593Smuzhiyun CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
1300*4882a593Smuzhiyun if (ret)
1301*4882a593Smuzhiyun return ret;
1302*4882a593Smuzhiyun
1303*4882a593Smuzhiyun state->mode_bit = ffs(buffer.output[1] & 0xFFFF);
1304*4882a593Smuzhiyun if (state->mode_bit != 0)
1305*4882a593Smuzhiyun state->mode_bit--;
1306*4882a593Smuzhiyun
1307*4882a593Smuzhiyun state->triggers = (buffer.output[1] >> 16) & 0xFF;
1308*4882a593Smuzhiyun state->timeout_value = (buffer.output[1] >> 24) & 0x3F;
1309*4882a593Smuzhiyun state->timeout_unit = (buffer.output[1] >> 30) & 0x3;
1310*4882a593Smuzhiyun state->als_setting = buffer.output[2] & 0xFF;
1311*4882a593Smuzhiyun state->als_value = (buffer.output[2] >> 8) & 0xFF;
1312*4882a593Smuzhiyun state->level = (buffer.output[2] >> 16) & 0xFF;
1313*4882a593Smuzhiyun state->timeout_value_ac = (buffer.output[2] >> 24) & 0x3F;
1314*4882a593Smuzhiyun state->timeout_unit_ac = (buffer.output[2] >> 30) & 0x3;
1315*4882a593Smuzhiyun
1316*4882a593Smuzhiyun return ret;
1317*4882a593Smuzhiyun }
1318*4882a593Smuzhiyun
kbd_set_state(struct kbd_state * state)1319*4882a593Smuzhiyun static int kbd_set_state(struct kbd_state *state)
1320*4882a593Smuzhiyun {
1321*4882a593Smuzhiyun struct calling_interface_buffer buffer;
1322*4882a593Smuzhiyun int ret;
1323*4882a593Smuzhiyun u32 input1;
1324*4882a593Smuzhiyun u32 input2;
1325*4882a593Smuzhiyun
1326*4882a593Smuzhiyun input1 = BIT(state->mode_bit) & 0xFFFF;
1327*4882a593Smuzhiyun input1 |= (state->triggers & 0xFF) << 16;
1328*4882a593Smuzhiyun input1 |= (state->timeout_value & 0x3F) << 24;
1329*4882a593Smuzhiyun input1 |= (state->timeout_unit & 0x3) << 30;
1330*4882a593Smuzhiyun input2 = state->als_setting & 0xFF;
1331*4882a593Smuzhiyun input2 |= (state->level & 0xFF) << 16;
1332*4882a593Smuzhiyun input2 |= (state->timeout_value_ac & 0x3F) << 24;
1333*4882a593Smuzhiyun input2 |= (state->timeout_unit_ac & 0x3) << 30;
1334*4882a593Smuzhiyun dell_fill_request(&buffer, 0x2, input1, input2, 0);
1335*4882a593Smuzhiyun ret = dell_send_request(&buffer,
1336*4882a593Smuzhiyun CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
1337*4882a593Smuzhiyun
1338*4882a593Smuzhiyun return ret;
1339*4882a593Smuzhiyun }
1340*4882a593Smuzhiyun
kbd_set_state_safe(struct kbd_state * state,struct kbd_state * old)1341*4882a593Smuzhiyun static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
1342*4882a593Smuzhiyun {
1343*4882a593Smuzhiyun int ret;
1344*4882a593Smuzhiyun
1345*4882a593Smuzhiyun ret = kbd_set_state(state);
1346*4882a593Smuzhiyun if (ret == 0)
1347*4882a593Smuzhiyun return 0;
1348*4882a593Smuzhiyun
1349*4882a593Smuzhiyun /*
1350*4882a593Smuzhiyun * When setting the new state fails,try to restore the previous one.
1351*4882a593Smuzhiyun * This is needed on some machines where BIOS sets a default state when
1352*4882a593Smuzhiyun * setting a new state fails. This default state could be all off.
1353*4882a593Smuzhiyun */
1354*4882a593Smuzhiyun
1355*4882a593Smuzhiyun if (kbd_set_state(old))
1356*4882a593Smuzhiyun pr_err("Setting old previous keyboard state failed\n");
1357*4882a593Smuzhiyun
1358*4882a593Smuzhiyun return ret;
1359*4882a593Smuzhiyun }
1360*4882a593Smuzhiyun
kbd_set_token_bit(u8 bit)1361*4882a593Smuzhiyun static int kbd_set_token_bit(u8 bit)
1362*4882a593Smuzhiyun {
1363*4882a593Smuzhiyun struct calling_interface_buffer buffer;
1364*4882a593Smuzhiyun struct calling_interface_token *token;
1365*4882a593Smuzhiyun int ret;
1366*4882a593Smuzhiyun
1367*4882a593Smuzhiyun if (bit >= ARRAY_SIZE(kbd_tokens))
1368*4882a593Smuzhiyun return -EINVAL;
1369*4882a593Smuzhiyun
1370*4882a593Smuzhiyun token = dell_smbios_find_token(kbd_tokens[bit]);
1371*4882a593Smuzhiyun if (!token)
1372*4882a593Smuzhiyun return -EINVAL;
1373*4882a593Smuzhiyun
1374*4882a593Smuzhiyun dell_fill_request(&buffer, token->location, token->value, 0, 0);
1375*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
1376*4882a593Smuzhiyun
1377*4882a593Smuzhiyun return ret;
1378*4882a593Smuzhiyun }
1379*4882a593Smuzhiyun
kbd_get_token_bit(u8 bit)1380*4882a593Smuzhiyun static int kbd_get_token_bit(u8 bit)
1381*4882a593Smuzhiyun {
1382*4882a593Smuzhiyun struct calling_interface_buffer buffer;
1383*4882a593Smuzhiyun struct calling_interface_token *token;
1384*4882a593Smuzhiyun int ret;
1385*4882a593Smuzhiyun int val;
1386*4882a593Smuzhiyun
1387*4882a593Smuzhiyun if (bit >= ARRAY_SIZE(kbd_tokens))
1388*4882a593Smuzhiyun return -EINVAL;
1389*4882a593Smuzhiyun
1390*4882a593Smuzhiyun token = dell_smbios_find_token(kbd_tokens[bit]);
1391*4882a593Smuzhiyun if (!token)
1392*4882a593Smuzhiyun return -EINVAL;
1393*4882a593Smuzhiyun
1394*4882a593Smuzhiyun dell_fill_request(&buffer, token->location, 0, 0, 0);
1395*4882a593Smuzhiyun ret = dell_send_request(&buffer, CLASS_TOKEN_READ, SELECT_TOKEN_STD);
1396*4882a593Smuzhiyun val = buffer.output[1];
1397*4882a593Smuzhiyun
1398*4882a593Smuzhiyun if (ret)
1399*4882a593Smuzhiyun return ret;
1400*4882a593Smuzhiyun
1401*4882a593Smuzhiyun return (val == token->value);
1402*4882a593Smuzhiyun }
1403*4882a593Smuzhiyun
kbd_get_first_active_token_bit(void)1404*4882a593Smuzhiyun static int kbd_get_first_active_token_bit(void)
1405*4882a593Smuzhiyun {
1406*4882a593Smuzhiyun int i;
1407*4882a593Smuzhiyun int ret;
1408*4882a593Smuzhiyun
1409*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) {
1410*4882a593Smuzhiyun ret = kbd_get_token_bit(i);
1411*4882a593Smuzhiyun if (ret == 1)
1412*4882a593Smuzhiyun return i;
1413*4882a593Smuzhiyun }
1414*4882a593Smuzhiyun
1415*4882a593Smuzhiyun return ret;
1416*4882a593Smuzhiyun }
1417*4882a593Smuzhiyun
kbd_get_valid_token_counts(void)1418*4882a593Smuzhiyun static int kbd_get_valid_token_counts(void)
1419*4882a593Smuzhiyun {
1420*4882a593Smuzhiyun return hweight16(kbd_token_bits);
1421*4882a593Smuzhiyun }
1422*4882a593Smuzhiyun
kbd_init_info(void)1423*4882a593Smuzhiyun static inline int kbd_init_info(void)
1424*4882a593Smuzhiyun {
1425*4882a593Smuzhiyun struct kbd_state state;
1426*4882a593Smuzhiyun int ret;
1427*4882a593Smuzhiyun int i;
1428*4882a593Smuzhiyun
1429*4882a593Smuzhiyun ret = kbd_get_info(&kbd_info);
1430*4882a593Smuzhiyun if (ret)
1431*4882a593Smuzhiyun return ret;
1432*4882a593Smuzhiyun
1433*4882a593Smuzhiyun /* NOTE: Old models without KBD_LED_AC_TOKEN token supports only one
1434*4882a593Smuzhiyun * timeout value which is shared for both battery and AC power
1435*4882a593Smuzhiyun * settings. So do not try to set AC values on old models.
1436*4882a593Smuzhiyun */
1437*4882a593Smuzhiyun if ((quirks && quirks->kbd_missing_ac_tag) ||
1438*4882a593Smuzhiyun dell_smbios_find_token(KBD_LED_AC_TOKEN))
1439*4882a593Smuzhiyun kbd_timeout_ac_supported = true;
1440*4882a593Smuzhiyun
1441*4882a593Smuzhiyun kbd_get_state(&state);
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun /* NOTE: timeout value is stored in 6 bits so max value is 63 */
1444*4882a593Smuzhiyun if (kbd_info.seconds > 63)
1445*4882a593Smuzhiyun kbd_info.seconds = 63;
1446*4882a593Smuzhiyun if (kbd_info.minutes > 63)
1447*4882a593Smuzhiyun kbd_info.minutes = 63;
1448*4882a593Smuzhiyun if (kbd_info.hours > 63)
1449*4882a593Smuzhiyun kbd_info.hours = 63;
1450*4882a593Smuzhiyun if (kbd_info.days > 63)
1451*4882a593Smuzhiyun kbd_info.days = 63;
1452*4882a593Smuzhiyun
1453*4882a593Smuzhiyun /* NOTE: On tested machines ON mode did not work and caused
1454*4882a593Smuzhiyun * problems (turned backlight off) so do not use it
1455*4882a593Smuzhiyun */
1456*4882a593Smuzhiyun kbd_info.modes &= ~BIT(KBD_MODE_BIT_ON);
1457*4882a593Smuzhiyun
1458*4882a593Smuzhiyun kbd_previous_level = kbd_get_level(&state);
1459*4882a593Smuzhiyun kbd_previous_mode_bit = state.mode_bit;
1460*4882a593Smuzhiyun
1461*4882a593Smuzhiyun if (kbd_previous_level == 0 && kbd_get_max_level() != 0)
1462*4882a593Smuzhiyun kbd_previous_level = 1;
1463*4882a593Smuzhiyun
1464*4882a593Smuzhiyun if (kbd_previous_mode_bit == KBD_MODE_BIT_OFF) {
1465*4882a593Smuzhiyun kbd_previous_mode_bit =
1466*4882a593Smuzhiyun ffs(kbd_info.modes & ~BIT(KBD_MODE_BIT_OFF));
1467*4882a593Smuzhiyun if (kbd_previous_mode_bit != 0)
1468*4882a593Smuzhiyun kbd_previous_mode_bit--;
1469*4882a593Smuzhiyun }
1470*4882a593Smuzhiyun
1471*4882a593Smuzhiyun if (kbd_info.modes & (BIT(KBD_MODE_BIT_ALS) |
1472*4882a593Smuzhiyun BIT(KBD_MODE_BIT_TRIGGER_ALS)))
1473*4882a593Smuzhiyun kbd_als_supported = true;
1474*4882a593Smuzhiyun
1475*4882a593Smuzhiyun if (kbd_info.modes & (
1476*4882a593Smuzhiyun BIT(KBD_MODE_BIT_TRIGGER_ALS) | BIT(KBD_MODE_BIT_TRIGGER) |
1477*4882a593Smuzhiyun BIT(KBD_MODE_BIT_TRIGGER_25) | BIT(KBD_MODE_BIT_TRIGGER_50) |
1478*4882a593Smuzhiyun BIT(KBD_MODE_BIT_TRIGGER_75) | BIT(KBD_MODE_BIT_TRIGGER_100)
1479*4882a593Smuzhiyun ))
1480*4882a593Smuzhiyun kbd_triggers_supported = true;
1481*4882a593Smuzhiyun
1482*4882a593Smuzhiyun /* kbd_mode_levels[0] is reserved, see below */
1483*4882a593Smuzhiyun for (i = 0; i < 16; ++i)
1484*4882a593Smuzhiyun if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes))
1485*4882a593Smuzhiyun kbd_mode_levels[1 + kbd_mode_levels_count++] = i;
1486*4882a593Smuzhiyun
1487*4882a593Smuzhiyun /*
1488*4882a593Smuzhiyun * Find the first supported mode and assign to kbd_mode_levels[0].
1489*4882a593Smuzhiyun * This should be 0 (off), but we cannot depend on the BIOS to
1490*4882a593Smuzhiyun * support 0.
1491*4882a593Smuzhiyun */
1492*4882a593Smuzhiyun if (kbd_mode_levels_count > 0) {
1493*4882a593Smuzhiyun for (i = 0; i < 16; ++i) {
1494*4882a593Smuzhiyun if (BIT(i) & kbd_info.modes) {
1495*4882a593Smuzhiyun kbd_mode_levels[0] = i;
1496*4882a593Smuzhiyun break;
1497*4882a593Smuzhiyun }
1498*4882a593Smuzhiyun }
1499*4882a593Smuzhiyun kbd_mode_levels_count++;
1500*4882a593Smuzhiyun }
1501*4882a593Smuzhiyun
1502*4882a593Smuzhiyun return 0;
1503*4882a593Smuzhiyun
1504*4882a593Smuzhiyun }
1505*4882a593Smuzhiyun
kbd_init_tokens(void)1506*4882a593Smuzhiyun static inline void kbd_init_tokens(void)
1507*4882a593Smuzhiyun {
1508*4882a593Smuzhiyun int i;
1509*4882a593Smuzhiyun
1510*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i)
1511*4882a593Smuzhiyun if (dell_smbios_find_token(kbd_tokens[i]))
1512*4882a593Smuzhiyun kbd_token_bits |= BIT(i);
1513*4882a593Smuzhiyun }
1514*4882a593Smuzhiyun
kbd_init(void)1515*4882a593Smuzhiyun static void kbd_init(void)
1516*4882a593Smuzhiyun {
1517*4882a593Smuzhiyun int ret;
1518*4882a593Smuzhiyun
1519*4882a593Smuzhiyun if (quirks && quirks->kbd_led_not_present)
1520*4882a593Smuzhiyun return;
1521*4882a593Smuzhiyun
1522*4882a593Smuzhiyun ret = kbd_init_info();
1523*4882a593Smuzhiyun kbd_init_tokens();
1524*4882a593Smuzhiyun
1525*4882a593Smuzhiyun /*
1526*4882a593Smuzhiyun * Only supports keyboard backlight when it has at least two modes.
1527*4882a593Smuzhiyun */
1528*4882a593Smuzhiyun if ((ret == 0 && (kbd_info.levels != 0 || kbd_mode_levels_count >= 2))
1529*4882a593Smuzhiyun || kbd_get_valid_token_counts() >= 2)
1530*4882a593Smuzhiyun kbd_led_present = true;
1531*4882a593Smuzhiyun }
1532*4882a593Smuzhiyun
kbd_led_timeout_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1533*4882a593Smuzhiyun static ssize_t kbd_led_timeout_store(struct device *dev,
1534*4882a593Smuzhiyun struct device_attribute *attr,
1535*4882a593Smuzhiyun const char *buf, size_t count)
1536*4882a593Smuzhiyun {
1537*4882a593Smuzhiyun struct kbd_state new_state;
1538*4882a593Smuzhiyun struct kbd_state state;
1539*4882a593Smuzhiyun bool convert;
1540*4882a593Smuzhiyun int value;
1541*4882a593Smuzhiyun int ret;
1542*4882a593Smuzhiyun char ch;
1543*4882a593Smuzhiyun u8 unit;
1544*4882a593Smuzhiyun int i;
1545*4882a593Smuzhiyun
1546*4882a593Smuzhiyun ret = sscanf(buf, "%d %c", &value, &ch);
1547*4882a593Smuzhiyun if (ret < 1)
1548*4882a593Smuzhiyun return -EINVAL;
1549*4882a593Smuzhiyun else if (ret == 1)
1550*4882a593Smuzhiyun ch = 's';
1551*4882a593Smuzhiyun
1552*4882a593Smuzhiyun if (value < 0)
1553*4882a593Smuzhiyun return -EINVAL;
1554*4882a593Smuzhiyun
1555*4882a593Smuzhiyun convert = false;
1556*4882a593Smuzhiyun
1557*4882a593Smuzhiyun switch (ch) {
1558*4882a593Smuzhiyun case 's':
1559*4882a593Smuzhiyun if (value > kbd_info.seconds)
1560*4882a593Smuzhiyun convert = true;
1561*4882a593Smuzhiyun unit = KBD_TIMEOUT_SECONDS;
1562*4882a593Smuzhiyun break;
1563*4882a593Smuzhiyun case 'm':
1564*4882a593Smuzhiyun if (value > kbd_info.minutes)
1565*4882a593Smuzhiyun convert = true;
1566*4882a593Smuzhiyun unit = KBD_TIMEOUT_MINUTES;
1567*4882a593Smuzhiyun break;
1568*4882a593Smuzhiyun case 'h':
1569*4882a593Smuzhiyun if (value > kbd_info.hours)
1570*4882a593Smuzhiyun convert = true;
1571*4882a593Smuzhiyun unit = KBD_TIMEOUT_HOURS;
1572*4882a593Smuzhiyun break;
1573*4882a593Smuzhiyun case 'd':
1574*4882a593Smuzhiyun if (value > kbd_info.days)
1575*4882a593Smuzhiyun convert = true;
1576*4882a593Smuzhiyun unit = KBD_TIMEOUT_DAYS;
1577*4882a593Smuzhiyun break;
1578*4882a593Smuzhiyun default:
1579*4882a593Smuzhiyun return -EINVAL;
1580*4882a593Smuzhiyun }
1581*4882a593Smuzhiyun
1582*4882a593Smuzhiyun if (quirks && quirks->needs_kbd_timeouts)
1583*4882a593Smuzhiyun convert = true;
1584*4882a593Smuzhiyun
1585*4882a593Smuzhiyun if (convert) {
1586*4882a593Smuzhiyun /* Convert value from current units to seconds */
1587*4882a593Smuzhiyun switch (unit) {
1588*4882a593Smuzhiyun case KBD_TIMEOUT_DAYS:
1589*4882a593Smuzhiyun value *= 24;
1590*4882a593Smuzhiyun fallthrough;
1591*4882a593Smuzhiyun case KBD_TIMEOUT_HOURS:
1592*4882a593Smuzhiyun value *= 60;
1593*4882a593Smuzhiyun fallthrough;
1594*4882a593Smuzhiyun case KBD_TIMEOUT_MINUTES:
1595*4882a593Smuzhiyun value *= 60;
1596*4882a593Smuzhiyun unit = KBD_TIMEOUT_SECONDS;
1597*4882a593Smuzhiyun }
1598*4882a593Smuzhiyun
1599*4882a593Smuzhiyun if (quirks && quirks->needs_kbd_timeouts) {
1600*4882a593Smuzhiyun for (i = 0; quirks->kbd_timeouts[i] != -1; i++) {
1601*4882a593Smuzhiyun if (value <= quirks->kbd_timeouts[i]) {
1602*4882a593Smuzhiyun value = quirks->kbd_timeouts[i];
1603*4882a593Smuzhiyun break;
1604*4882a593Smuzhiyun }
1605*4882a593Smuzhiyun }
1606*4882a593Smuzhiyun }
1607*4882a593Smuzhiyun
1608*4882a593Smuzhiyun if (value <= kbd_info.seconds && kbd_info.seconds) {
1609*4882a593Smuzhiyun unit = KBD_TIMEOUT_SECONDS;
1610*4882a593Smuzhiyun } else if (value / 60 <= kbd_info.minutes && kbd_info.minutes) {
1611*4882a593Smuzhiyun value /= 60;
1612*4882a593Smuzhiyun unit = KBD_TIMEOUT_MINUTES;
1613*4882a593Smuzhiyun } else if (value / (60 * 60) <= kbd_info.hours && kbd_info.hours) {
1614*4882a593Smuzhiyun value /= (60 * 60);
1615*4882a593Smuzhiyun unit = KBD_TIMEOUT_HOURS;
1616*4882a593Smuzhiyun } else if (value / (60 * 60 * 24) <= kbd_info.days && kbd_info.days) {
1617*4882a593Smuzhiyun value /= (60 * 60 * 24);
1618*4882a593Smuzhiyun unit = KBD_TIMEOUT_DAYS;
1619*4882a593Smuzhiyun } else {
1620*4882a593Smuzhiyun return -EINVAL;
1621*4882a593Smuzhiyun }
1622*4882a593Smuzhiyun }
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun mutex_lock(&kbd_led_mutex);
1625*4882a593Smuzhiyun
1626*4882a593Smuzhiyun ret = kbd_get_state(&state);
1627*4882a593Smuzhiyun if (ret)
1628*4882a593Smuzhiyun goto out;
1629*4882a593Smuzhiyun
1630*4882a593Smuzhiyun new_state = state;
1631*4882a593Smuzhiyun
1632*4882a593Smuzhiyun if (kbd_timeout_ac_supported && power_supply_is_system_supplied() > 0) {
1633*4882a593Smuzhiyun new_state.timeout_value_ac = value;
1634*4882a593Smuzhiyun new_state.timeout_unit_ac = unit;
1635*4882a593Smuzhiyun } else {
1636*4882a593Smuzhiyun new_state.timeout_value = value;
1637*4882a593Smuzhiyun new_state.timeout_unit = unit;
1638*4882a593Smuzhiyun }
1639*4882a593Smuzhiyun
1640*4882a593Smuzhiyun ret = kbd_set_state_safe(&new_state, &state);
1641*4882a593Smuzhiyun if (ret)
1642*4882a593Smuzhiyun goto out;
1643*4882a593Smuzhiyun
1644*4882a593Smuzhiyun ret = count;
1645*4882a593Smuzhiyun out:
1646*4882a593Smuzhiyun mutex_unlock(&kbd_led_mutex);
1647*4882a593Smuzhiyun return ret;
1648*4882a593Smuzhiyun }
1649*4882a593Smuzhiyun
kbd_led_timeout_show(struct device * dev,struct device_attribute * attr,char * buf)1650*4882a593Smuzhiyun static ssize_t kbd_led_timeout_show(struct device *dev,
1651*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
1652*4882a593Smuzhiyun {
1653*4882a593Smuzhiyun struct kbd_state state;
1654*4882a593Smuzhiyun int value;
1655*4882a593Smuzhiyun int ret;
1656*4882a593Smuzhiyun int len;
1657*4882a593Smuzhiyun u8 unit;
1658*4882a593Smuzhiyun
1659*4882a593Smuzhiyun ret = kbd_get_state(&state);
1660*4882a593Smuzhiyun if (ret)
1661*4882a593Smuzhiyun return ret;
1662*4882a593Smuzhiyun
1663*4882a593Smuzhiyun if (kbd_timeout_ac_supported && power_supply_is_system_supplied() > 0) {
1664*4882a593Smuzhiyun value = state.timeout_value_ac;
1665*4882a593Smuzhiyun unit = state.timeout_unit_ac;
1666*4882a593Smuzhiyun } else {
1667*4882a593Smuzhiyun value = state.timeout_value;
1668*4882a593Smuzhiyun unit = state.timeout_unit;
1669*4882a593Smuzhiyun }
1670*4882a593Smuzhiyun
1671*4882a593Smuzhiyun len = sprintf(buf, "%d", value);
1672*4882a593Smuzhiyun
1673*4882a593Smuzhiyun switch (unit) {
1674*4882a593Smuzhiyun case KBD_TIMEOUT_SECONDS:
1675*4882a593Smuzhiyun return len + sprintf(buf+len, "s\n");
1676*4882a593Smuzhiyun case KBD_TIMEOUT_MINUTES:
1677*4882a593Smuzhiyun return len + sprintf(buf+len, "m\n");
1678*4882a593Smuzhiyun case KBD_TIMEOUT_HOURS:
1679*4882a593Smuzhiyun return len + sprintf(buf+len, "h\n");
1680*4882a593Smuzhiyun case KBD_TIMEOUT_DAYS:
1681*4882a593Smuzhiyun return len + sprintf(buf+len, "d\n");
1682*4882a593Smuzhiyun default:
1683*4882a593Smuzhiyun return -EINVAL;
1684*4882a593Smuzhiyun }
1685*4882a593Smuzhiyun
1686*4882a593Smuzhiyun return len;
1687*4882a593Smuzhiyun }
1688*4882a593Smuzhiyun
1689*4882a593Smuzhiyun static DEVICE_ATTR(stop_timeout, S_IRUGO | S_IWUSR,
1690*4882a593Smuzhiyun kbd_led_timeout_show, kbd_led_timeout_store);
1691*4882a593Smuzhiyun
1692*4882a593Smuzhiyun static const char * const kbd_led_triggers[] = {
1693*4882a593Smuzhiyun "keyboard",
1694*4882a593Smuzhiyun "touchpad",
1695*4882a593Smuzhiyun /*"trackstick"*/ NULL, /* NOTE: trackstick is just alias for touchpad */
1696*4882a593Smuzhiyun "mouse",
1697*4882a593Smuzhiyun };
1698*4882a593Smuzhiyun
kbd_led_triggers_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1699*4882a593Smuzhiyun static ssize_t kbd_led_triggers_store(struct device *dev,
1700*4882a593Smuzhiyun struct device_attribute *attr,
1701*4882a593Smuzhiyun const char *buf, size_t count)
1702*4882a593Smuzhiyun {
1703*4882a593Smuzhiyun struct kbd_state new_state;
1704*4882a593Smuzhiyun struct kbd_state state;
1705*4882a593Smuzhiyun bool triggers_enabled = false;
1706*4882a593Smuzhiyun int trigger_bit = -1;
1707*4882a593Smuzhiyun char trigger[21];
1708*4882a593Smuzhiyun int i, ret;
1709*4882a593Smuzhiyun
1710*4882a593Smuzhiyun ret = sscanf(buf, "%20s", trigger);
1711*4882a593Smuzhiyun if (ret != 1)
1712*4882a593Smuzhiyun return -EINVAL;
1713*4882a593Smuzhiyun
1714*4882a593Smuzhiyun if (trigger[0] != '+' && trigger[0] != '-')
1715*4882a593Smuzhiyun return -EINVAL;
1716*4882a593Smuzhiyun
1717*4882a593Smuzhiyun mutex_lock(&kbd_led_mutex);
1718*4882a593Smuzhiyun
1719*4882a593Smuzhiyun ret = kbd_get_state(&state);
1720*4882a593Smuzhiyun if (ret)
1721*4882a593Smuzhiyun goto out;
1722*4882a593Smuzhiyun
1723*4882a593Smuzhiyun if (kbd_triggers_supported)
1724*4882a593Smuzhiyun triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
1725*4882a593Smuzhiyun
1726*4882a593Smuzhiyun if (kbd_triggers_supported) {
1727*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
1728*4882a593Smuzhiyun if (!(kbd_info.triggers & BIT(i)))
1729*4882a593Smuzhiyun continue;
1730*4882a593Smuzhiyun if (!kbd_led_triggers[i])
1731*4882a593Smuzhiyun continue;
1732*4882a593Smuzhiyun if (strcmp(trigger+1, kbd_led_triggers[i]) != 0)
1733*4882a593Smuzhiyun continue;
1734*4882a593Smuzhiyun if (trigger[0] == '+' &&
1735*4882a593Smuzhiyun triggers_enabled && (state.triggers & BIT(i))) {
1736*4882a593Smuzhiyun ret = count;
1737*4882a593Smuzhiyun goto out;
1738*4882a593Smuzhiyun }
1739*4882a593Smuzhiyun if (trigger[0] == '-' &&
1740*4882a593Smuzhiyun (!triggers_enabled || !(state.triggers & BIT(i)))) {
1741*4882a593Smuzhiyun ret = count;
1742*4882a593Smuzhiyun goto out;
1743*4882a593Smuzhiyun }
1744*4882a593Smuzhiyun trigger_bit = i;
1745*4882a593Smuzhiyun break;
1746*4882a593Smuzhiyun }
1747*4882a593Smuzhiyun }
1748*4882a593Smuzhiyun
1749*4882a593Smuzhiyun if (trigger_bit == -1) {
1750*4882a593Smuzhiyun ret = -EINVAL;
1751*4882a593Smuzhiyun goto out;
1752*4882a593Smuzhiyun }
1753*4882a593Smuzhiyun
1754*4882a593Smuzhiyun new_state = state;
1755*4882a593Smuzhiyun if (trigger[0] == '+')
1756*4882a593Smuzhiyun new_state.triggers |= BIT(trigger_bit);
1757*4882a593Smuzhiyun else {
1758*4882a593Smuzhiyun new_state.triggers &= ~BIT(trigger_bit);
1759*4882a593Smuzhiyun /*
1760*4882a593Smuzhiyun * NOTE: trackstick bit (2) must be disabled when
1761*4882a593Smuzhiyun * disabling touchpad bit (1), otherwise touchpad
1762*4882a593Smuzhiyun * bit (1) will not be disabled
1763*4882a593Smuzhiyun */
1764*4882a593Smuzhiyun if (trigger_bit == 1)
1765*4882a593Smuzhiyun new_state.triggers &= ~BIT(2);
1766*4882a593Smuzhiyun }
1767*4882a593Smuzhiyun if ((kbd_info.triggers & new_state.triggers) !=
1768*4882a593Smuzhiyun new_state.triggers) {
1769*4882a593Smuzhiyun ret = -EINVAL;
1770*4882a593Smuzhiyun goto out;
1771*4882a593Smuzhiyun }
1772*4882a593Smuzhiyun if (new_state.triggers && !triggers_enabled) {
1773*4882a593Smuzhiyun new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
1774*4882a593Smuzhiyun kbd_set_level(&new_state, kbd_previous_level);
1775*4882a593Smuzhiyun } else if (new_state.triggers == 0) {
1776*4882a593Smuzhiyun kbd_set_level(&new_state, 0);
1777*4882a593Smuzhiyun }
1778*4882a593Smuzhiyun if (!(kbd_info.modes & BIT(new_state.mode_bit))) {
1779*4882a593Smuzhiyun ret = -EINVAL;
1780*4882a593Smuzhiyun goto out;
1781*4882a593Smuzhiyun }
1782*4882a593Smuzhiyun ret = kbd_set_state_safe(&new_state, &state);
1783*4882a593Smuzhiyun if (ret)
1784*4882a593Smuzhiyun goto out;
1785*4882a593Smuzhiyun if (new_state.mode_bit != KBD_MODE_BIT_OFF)
1786*4882a593Smuzhiyun kbd_previous_mode_bit = new_state.mode_bit;
1787*4882a593Smuzhiyun ret = count;
1788*4882a593Smuzhiyun out:
1789*4882a593Smuzhiyun mutex_unlock(&kbd_led_mutex);
1790*4882a593Smuzhiyun return ret;
1791*4882a593Smuzhiyun }
1792*4882a593Smuzhiyun
kbd_led_triggers_show(struct device * dev,struct device_attribute * attr,char * buf)1793*4882a593Smuzhiyun static ssize_t kbd_led_triggers_show(struct device *dev,
1794*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
1795*4882a593Smuzhiyun {
1796*4882a593Smuzhiyun struct kbd_state state;
1797*4882a593Smuzhiyun bool triggers_enabled;
1798*4882a593Smuzhiyun int level, i, ret;
1799*4882a593Smuzhiyun int len = 0;
1800*4882a593Smuzhiyun
1801*4882a593Smuzhiyun ret = kbd_get_state(&state);
1802*4882a593Smuzhiyun if (ret)
1803*4882a593Smuzhiyun return ret;
1804*4882a593Smuzhiyun
1805*4882a593Smuzhiyun len = 0;
1806*4882a593Smuzhiyun
1807*4882a593Smuzhiyun if (kbd_triggers_supported) {
1808*4882a593Smuzhiyun triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
1809*4882a593Smuzhiyun level = kbd_get_level(&state);
1810*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) {
1811*4882a593Smuzhiyun if (!(kbd_info.triggers & BIT(i)))
1812*4882a593Smuzhiyun continue;
1813*4882a593Smuzhiyun if (!kbd_led_triggers[i])
1814*4882a593Smuzhiyun continue;
1815*4882a593Smuzhiyun if ((triggers_enabled || level <= 0) &&
1816*4882a593Smuzhiyun (state.triggers & BIT(i)))
1817*4882a593Smuzhiyun buf[len++] = '+';
1818*4882a593Smuzhiyun else
1819*4882a593Smuzhiyun buf[len++] = '-';
1820*4882a593Smuzhiyun len += sprintf(buf+len, "%s ", kbd_led_triggers[i]);
1821*4882a593Smuzhiyun }
1822*4882a593Smuzhiyun }
1823*4882a593Smuzhiyun
1824*4882a593Smuzhiyun if (len)
1825*4882a593Smuzhiyun buf[len - 1] = '\n';
1826*4882a593Smuzhiyun
1827*4882a593Smuzhiyun return len;
1828*4882a593Smuzhiyun }
1829*4882a593Smuzhiyun
1830*4882a593Smuzhiyun static DEVICE_ATTR(start_triggers, S_IRUGO | S_IWUSR,
1831*4882a593Smuzhiyun kbd_led_triggers_show, kbd_led_triggers_store);
1832*4882a593Smuzhiyun
kbd_led_als_enabled_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1833*4882a593Smuzhiyun static ssize_t kbd_led_als_enabled_store(struct device *dev,
1834*4882a593Smuzhiyun struct device_attribute *attr,
1835*4882a593Smuzhiyun const char *buf, size_t count)
1836*4882a593Smuzhiyun {
1837*4882a593Smuzhiyun struct kbd_state new_state;
1838*4882a593Smuzhiyun struct kbd_state state;
1839*4882a593Smuzhiyun bool triggers_enabled = false;
1840*4882a593Smuzhiyun int enable;
1841*4882a593Smuzhiyun int ret;
1842*4882a593Smuzhiyun
1843*4882a593Smuzhiyun ret = kstrtoint(buf, 0, &enable);
1844*4882a593Smuzhiyun if (ret)
1845*4882a593Smuzhiyun return ret;
1846*4882a593Smuzhiyun
1847*4882a593Smuzhiyun mutex_lock(&kbd_led_mutex);
1848*4882a593Smuzhiyun
1849*4882a593Smuzhiyun ret = kbd_get_state(&state);
1850*4882a593Smuzhiyun if (ret)
1851*4882a593Smuzhiyun goto out;
1852*4882a593Smuzhiyun
1853*4882a593Smuzhiyun if (enable == kbd_is_als_mode_bit(state.mode_bit)) {
1854*4882a593Smuzhiyun ret = count;
1855*4882a593Smuzhiyun goto out;
1856*4882a593Smuzhiyun }
1857*4882a593Smuzhiyun
1858*4882a593Smuzhiyun new_state = state;
1859*4882a593Smuzhiyun
1860*4882a593Smuzhiyun if (kbd_triggers_supported)
1861*4882a593Smuzhiyun triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit);
1862*4882a593Smuzhiyun
1863*4882a593Smuzhiyun if (enable) {
1864*4882a593Smuzhiyun if (triggers_enabled)
1865*4882a593Smuzhiyun new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS;
1866*4882a593Smuzhiyun else
1867*4882a593Smuzhiyun new_state.mode_bit = KBD_MODE_BIT_ALS;
1868*4882a593Smuzhiyun } else {
1869*4882a593Smuzhiyun if (triggers_enabled) {
1870*4882a593Smuzhiyun new_state.mode_bit = KBD_MODE_BIT_TRIGGER;
1871*4882a593Smuzhiyun kbd_set_level(&new_state, kbd_previous_level);
1872*4882a593Smuzhiyun } else {
1873*4882a593Smuzhiyun new_state.mode_bit = KBD_MODE_BIT_ON;
1874*4882a593Smuzhiyun }
1875*4882a593Smuzhiyun }
1876*4882a593Smuzhiyun if (!(kbd_info.modes & BIT(new_state.mode_bit))) {
1877*4882a593Smuzhiyun ret = -EINVAL;
1878*4882a593Smuzhiyun goto out;
1879*4882a593Smuzhiyun }
1880*4882a593Smuzhiyun
1881*4882a593Smuzhiyun ret = kbd_set_state_safe(&new_state, &state);
1882*4882a593Smuzhiyun if (ret)
1883*4882a593Smuzhiyun goto out;
1884*4882a593Smuzhiyun kbd_previous_mode_bit = new_state.mode_bit;
1885*4882a593Smuzhiyun
1886*4882a593Smuzhiyun ret = count;
1887*4882a593Smuzhiyun out:
1888*4882a593Smuzhiyun mutex_unlock(&kbd_led_mutex);
1889*4882a593Smuzhiyun return ret;
1890*4882a593Smuzhiyun }
1891*4882a593Smuzhiyun
kbd_led_als_enabled_show(struct device * dev,struct device_attribute * attr,char * buf)1892*4882a593Smuzhiyun static ssize_t kbd_led_als_enabled_show(struct device *dev,
1893*4882a593Smuzhiyun struct device_attribute *attr,
1894*4882a593Smuzhiyun char *buf)
1895*4882a593Smuzhiyun {
1896*4882a593Smuzhiyun struct kbd_state state;
1897*4882a593Smuzhiyun bool enabled = false;
1898*4882a593Smuzhiyun int ret;
1899*4882a593Smuzhiyun
1900*4882a593Smuzhiyun ret = kbd_get_state(&state);
1901*4882a593Smuzhiyun if (ret)
1902*4882a593Smuzhiyun return ret;
1903*4882a593Smuzhiyun enabled = kbd_is_als_mode_bit(state.mode_bit);
1904*4882a593Smuzhiyun
1905*4882a593Smuzhiyun return sprintf(buf, "%d\n", enabled ? 1 : 0);
1906*4882a593Smuzhiyun }
1907*4882a593Smuzhiyun
1908*4882a593Smuzhiyun static DEVICE_ATTR(als_enabled, S_IRUGO | S_IWUSR,
1909*4882a593Smuzhiyun kbd_led_als_enabled_show, kbd_led_als_enabled_store);
1910*4882a593Smuzhiyun
kbd_led_als_setting_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1911*4882a593Smuzhiyun static ssize_t kbd_led_als_setting_store(struct device *dev,
1912*4882a593Smuzhiyun struct device_attribute *attr,
1913*4882a593Smuzhiyun const char *buf, size_t count)
1914*4882a593Smuzhiyun {
1915*4882a593Smuzhiyun struct kbd_state state;
1916*4882a593Smuzhiyun struct kbd_state new_state;
1917*4882a593Smuzhiyun u8 setting;
1918*4882a593Smuzhiyun int ret;
1919*4882a593Smuzhiyun
1920*4882a593Smuzhiyun ret = kstrtou8(buf, 10, &setting);
1921*4882a593Smuzhiyun if (ret)
1922*4882a593Smuzhiyun return ret;
1923*4882a593Smuzhiyun
1924*4882a593Smuzhiyun mutex_lock(&kbd_led_mutex);
1925*4882a593Smuzhiyun
1926*4882a593Smuzhiyun ret = kbd_get_state(&state);
1927*4882a593Smuzhiyun if (ret)
1928*4882a593Smuzhiyun goto out;
1929*4882a593Smuzhiyun
1930*4882a593Smuzhiyun new_state = state;
1931*4882a593Smuzhiyun new_state.als_setting = setting;
1932*4882a593Smuzhiyun
1933*4882a593Smuzhiyun ret = kbd_set_state_safe(&new_state, &state);
1934*4882a593Smuzhiyun if (ret)
1935*4882a593Smuzhiyun goto out;
1936*4882a593Smuzhiyun
1937*4882a593Smuzhiyun ret = count;
1938*4882a593Smuzhiyun out:
1939*4882a593Smuzhiyun mutex_unlock(&kbd_led_mutex);
1940*4882a593Smuzhiyun return ret;
1941*4882a593Smuzhiyun }
1942*4882a593Smuzhiyun
kbd_led_als_setting_show(struct device * dev,struct device_attribute * attr,char * buf)1943*4882a593Smuzhiyun static ssize_t kbd_led_als_setting_show(struct device *dev,
1944*4882a593Smuzhiyun struct device_attribute *attr,
1945*4882a593Smuzhiyun char *buf)
1946*4882a593Smuzhiyun {
1947*4882a593Smuzhiyun struct kbd_state state;
1948*4882a593Smuzhiyun int ret;
1949*4882a593Smuzhiyun
1950*4882a593Smuzhiyun ret = kbd_get_state(&state);
1951*4882a593Smuzhiyun if (ret)
1952*4882a593Smuzhiyun return ret;
1953*4882a593Smuzhiyun
1954*4882a593Smuzhiyun return sprintf(buf, "%d\n", state.als_setting);
1955*4882a593Smuzhiyun }
1956*4882a593Smuzhiyun
1957*4882a593Smuzhiyun static DEVICE_ATTR(als_setting, S_IRUGO | S_IWUSR,
1958*4882a593Smuzhiyun kbd_led_als_setting_show, kbd_led_als_setting_store);
1959*4882a593Smuzhiyun
1960*4882a593Smuzhiyun static struct attribute *kbd_led_attrs[] = {
1961*4882a593Smuzhiyun &dev_attr_stop_timeout.attr,
1962*4882a593Smuzhiyun &dev_attr_start_triggers.attr,
1963*4882a593Smuzhiyun NULL,
1964*4882a593Smuzhiyun };
1965*4882a593Smuzhiyun
1966*4882a593Smuzhiyun static const struct attribute_group kbd_led_group = {
1967*4882a593Smuzhiyun .attrs = kbd_led_attrs,
1968*4882a593Smuzhiyun };
1969*4882a593Smuzhiyun
1970*4882a593Smuzhiyun static struct attribute *kbd_led_als_attrs[] = {
1971*4882a593Smuzhiyun &dev_attr_als_enabled.attr,
1972*4882a593Smuzhiyun &dev_attr_als_setting.attr,
1973*4882a593Smuzhiyun NULL,
1974*4882a593Smuzhiyun };
1975*4882a593Smuzhiyun
1976*4882a593Smuzhiyun static const struct attribute_group kbd_led_als_group = {
1977*4882a593Smuzhiyun .attrs = kbd_led_als_attrs,
1978*4882a593Smuzhiyun };
1979*4882a593Smuzhiyun
1980*4882a593Smuzhiyun static const struct attribute_group *kbd_led_groups[] = {
1981*4882a593Smuzhiyun &kbd_led_group,
1982*4882a593Smuzhiyun &kbd_led_als_group,
1983*4882a593Smuzhiyun NULL,
1984*4882a593Smuzhiyun };
1985*4882a593Smuzhiyun
kbd_led_level_get(struct led_classdev * led_cdev)1986*4882a593Smuzhiyun static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev)
1987*4882a593Smuzhiyun {
1988*4882a593Smuzhiyun int ret;
1989*4882a593Smuzhiyun u16 num;
1990*4882a593Smuzhiyun struct kbd_state state;
1991*4882a593Smuzhiyun
1992*4882a593Smuzhiyun if (kbd_get_max_level()) {
1993*4882a593Smuzhiyun ret = kbd_get_state(&state);
1994*4882a593Smuzhiyun if (ret)
1995*4882a593Smuzhiyun return 0;
1996*4882a593Smuzhiyun ret = kbd_get_level(&state);
1997*4882a593Smuzhiyun if (ret < 0)
1998*4882a593Smuzhiyun return 0;
1999*4882a593Smuzhiyun return ret;
2000*4882a593Smuzhiyun }
2001*4882a593Smuzhiyun
2002*4882a593Smuzhiyun if (kbd_get_valid_token_counts()) {
2003*4882a593Smuzhiyun ret = kbd_get_first_active_token_bit();
2004*4882a593Smuzhiyun if (ret < 0)
2005*4882a593Smuzhiyun return 0;
2006*4882a593Smuzhiyun for (num = kbd_token_bits; num != 0 && ret > 0; --ret)
2007*4882a593Smuzhiyun num &= num - 1; /* clear the first bit set */
2008*4882a593Smuzhiyun if (num == 0)
2009*4882a593Smuzhiyun return 0;
2010*4882a593Smuzhiyun return ffs(num) - 1;
2011*4882a593Smuzhiyun }
2012*4882a593Smuzhiyun
2013*4882a593Smuzhiyun pr_warn("Keyboard brightness level control not supported\n");
2014*4882a593Smuzhiyun return 0;
2015*4882a593Smuzhiyun }
2016*4882a593Smuzhiyun
kbd_led_level_set(struct led_classdev * led_cdev,enum led_brightness value)2017*4882a593Smuzhiyun static int kbd_led_level_set(struct led_classdev *led_cdev,
2018*4882a593Smuzhiyun enum led_brightness value)
2019*4882a593Smuzhiyun {
2020*4882a593Smuzhiyun enum led_brightness new_value = value;
2021*4882a593Smuzhiyun struct kbd_state state;
2022*4882a593Smuzhiyun struct kbd_state new_state;
2023*4882a593Smuzhiyun u16 num;
2024*4882a593Smuzhiyun int ret;
2025*4882a593Smuzhiyun
2026*4882a593Smuzhiyun mutex_lock(&kbd_led_mutex);
2027*4882a593Smuzhiyun
2028*4882a593Smuzhiyun if (kbd_get_max_level()) {
2029*4882a593Smuzhiyun ret = kbd_get_state(&state);
2030*4882a593Smuzhiyun if (ret)
2031*4882a593Smuzhiyun goto out;
2032*4882a593Smuzhiyun new_state = state;
2033*4882a593Smuzhiyun ret = kbd_set_level(&new_state, value);
2034*4882a593Smuzhiyun if (ret)
2035*4882a593Smuzhiyun goto out;
2036*4882a593Smuzhiyun ret = kbd_set_state_safe(&new_state, &state);
2037*4882a593Smuzhiyun } else if (kbd_get_valid_token_counts()) {
2038*4882a593Smuzhiyun for (num = kbd_token_bits; num != 0 && value > 0; --value)
2039*4882a593Smuzhiyun num &= num - 1; /* clear the first bit set */
2040*4882a593Smuzhiyun if (num == 0)
2041*4882a593Smuzhiyun ret = 0;
2042*4882a593Smuzhiyun else
2043*4882a593Smuzhiyun ret = kbd_set_token_bit(ffs(num) - 1);
2044*4882a593Smuzhiyun } else {
2045*4882a593Smuzhiyun pr_warn("Keyboard brightness level control not supported\n");
2046*4882a593Smuzhiyun ret = -ENXIO;
2047*4882a593Smuzhiyun }
2048*4882a593Smuzhiyun
2049*4882a593Smuzhiyun out:
2050*4882a593Smuzhiyun if (ret == 0)
2051*4882a593Smuzhiyun kbd_led_level = new_value;
2052*4882a593Smuzhiyun
2053*4882a593Smuzhiyun mutex_unlock(&kbd_led_mutex);
2054*4882a593Smuzhiyun return ret;
2055*4882a593Smuzhiyun }
2056*4882a593Smuzhiyun
2057*4882a593Smuzhiyun static struct led_classdev kbd_led = {
2058*4882a593Smuzhiyun .name = "dell::kbd_backlight",
2059*4882a593Smuzhiyun .flags = LED_BRIGHT_HW_CHANGED,
2060*4882a593Smuzhiyun .brightness_set_blocking = kbd_led_level_set,
2061*4882a593Smuzhiyun .brightness_get = kbd_led_level_get,
2062*4882a593Smuzhiyun .groups = kbd_led_groups,
2063*4882a593Smuzhiyun };
2064*4882a593Smuzhiyun
kbd_led_init(struct device * dev)2065*4882a593Smuzhiyun static int __init kbd_led_init(struct device *dev)
2066*4882a593Smuzhiyun {
2067*4882a593Smuzhiyun int ret;
2068*4882a593Smuzhiyun
2069*4882a593Smuzhiyun kbd_init();
2070*4882a593Smuzhiyun if (!kbd_led_present)
2071*4882a593Smuzhiyun return -ENODEV;
2072*4882a593Smuzhiyun if (!kbd_als_supported)
2073*4882a593Smuzhiyun kbd_led_groups[1] = NULL;
2074*4882a593Smuzhiyun kbd_led.max_brightness = kbd_get_max_level();
2075*4882a593Smuzhiyun if (!kbd_led.max_brightness) {
2076*4882a593Smuzhiyun kbd_led.max_brightness = kbd_get_valid_token_counts();
2077*4882a593Smuzhiyun if (kbd_led.max_brightness)
2078*4882a593Smuzhiyun kbd_led.max_brightness--;
2079*4882a593Smuzhiyun }
2080*4882a593Smuzhiyun
2081*4882a593Smuzhiyun kbd_led_level = kbd_led_level_get(NULL);
2082*4882a593Smuzhiyun
2083*4882a593Smuzhiyun ret = led_classdev_register(dev, &kbd_led);
2084*4882a593Smuzhiyun if (ret)
2085*4882a593Smuzhiyun kbd_led_present = false;
2086*4882a593Smuzhiyun
2087*4882a593Smuzhiyun return ret;
2088*4882a593Smuzhiyun }
2089*4882a593Smuzhiyun
brightness_set_exit(struct led_classdev * led_cdev,enum led_brightness value)2090*4882a593Smuzhiyun static void brightness_set_exit(struct led_classdev *led_cdev,
2091*4882a593Smuzhiyun enum led_brightness value)
2092*4882a593Smuzhiyun {
2093*4882a593Smuzhiyun /* Don't change backlight level on exit */
2094*4882a593Smuzhiyun };
2095*4882a593Smuzhiyun
kbd_led_exit(void)2096*4882a593Smuzhiyun static void kbd_led_exit(void)
2097*4882a593Smuzhiyun {
2098*4882a593Smuzhiyun if (!kbd_led_present)
2099*4882a593Smuzhiyun return;
2100*4882a593Smuzhiyun kbd_led.brightness_set = brightness_set_exit;
2101*4882a593Smuzhiyun led_classdev_unregister(&kbd_led);
2102*4882a593Smuzhiyun }
2103*4882a593Smuzhiyun
dell_laptop_notifier_call(struct notifier_block * nb,unsigned long action,void * data)2104*4882a593Smuzhiyun static int dell_laptop_notifier_call(struct notifier_block *nb,
2105*4882a593Smuzhiyun unsigned long action, void *data)
2106*4882a593Smuzhiyun {
2107*4882a593Smuzhiyun bool changed = false;
2108*4882a593Smuzhiyun enum led_brightness new_kbd_led_level;
2109*4882a593Smuzhiyun
2110*4882a593Smuzhiyun switch (action) {
2111*4882a593Smuzhiyun case DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED:
2112*4882a593Smuzhiyun if (!kbd_led_present)
2113*4882a593Smuzhiyun break;
2114*4882a593Smuzhiyun
2115*4882a593Smuzhiyun mutex_lock(&kbd_led_mutex);
2116*4882a593Smuzhiyun new_kbd_led_level = kbd_led_level_get(&kbd_led);
2117*4882a593Smuzhiyun if (kbd_led_level != new_kbd_led_level) {
2118*4882a593Smuzhiyun kbd_led_level = new_kbd_led_level;
2119*4882a593Smuzhiyun changed = true;
2120*4882a593Smuzhiyun }
2121*4882a593Smuzhiyun mutex_unlock(&kbd_led_mutex);
2122*4882a593Smuzhiyun
2123*4882a593Smuzhiyun if (changed)
2124*4882a593Smuzhiyun led_classdev_notify_brightness_hw_changed(&kbd_led,
2125*4882a593Smuzhiyun kbd_led_level);
2126*4882a593Smuzhiyun break;
2127*4882a593Smuzhiyun }
2128*4882a593Smuzhiyun
2129*4882a593Smuzhiyun return NOTIFY_OK;
2130*4882a593Smuzhiyun }
2131*4882a593Smuzhiyun
2132*4882a593Smuzhiyun static struct notifier_block dell_laptop_notifier = {
2133*4882a593Smuzhiyun .notifier_call = dell_laptop_notifier_call,
2134*4882a593Smuzhiyun };
2135*4882a593Smuzhiyun
micmute_led_set(struct led_classdev * led_cdev,enum led_brightness brightness)2136*4882a593Smuzhiyun static int micmute_led_set(struct led_classdev *led_cdev,
2137*4882a593Smuzhiyun enum led_brightness brightness)
2138*4882a593Smuzhiyun {
2139*4882a593Smuzhiyun struct calling_interface_buffer buffer;
2140*4882a593Smuzhiyun struct calling_interface_token *token;
2141*4882a593Smuzhiyun int state = brightness != LED_OFF;
2142*4882a593Smuzhiyun
2143*4882a593Smuzhiyun if (state == 0)
2144*4882a593Smuzhiyun token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE);
2145*4882a593Smuzhiyun else
2146*4882a593Smuzhiyun token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE);
2147*4882a593Smuzhiyun
2148*4882a593Smuzhiyun if (!token)
2149*4882a593Smuzhiyun return -ENODEV;
2150*4882a593Smuzhiyun
2151*4882a593Smuzhiyun dell_fill_request(&buffer, token->location, token->value, 0, 0);
2152*4882a593Smuzhiyun dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
2153*4882a593Smuzhiyun
2154*4882a593Smuzhiyun return 0;
2155*4882a593Smuzhiyun }
2156*4882a593Smuzhiyun
2157*4882a593Smuzhiyun static struct led_classdev micmute_led_cdev = {
2158*4882a593Smuzhiyun .name = "platform::micmute",
2159*4882a593Smuzhiyun .max_brightness = 1,
2160*4882a593Smuzhiyun .brightness_set_blocking = micmute_led_set,
2161*4882a593Smuzhiyun .default_trigger = "audio-micmute",
2162*4882a593Smuzhiyun };
2163*4882a593Smuzhiyun
dell_init(void)2164*4882a593Smuzhiyun static int __init dell_init(void)
2165*4882a593Smuzhiyun {
2166*4882a593Smuzhiyun struct calling_interface_token *token;
2167*4882a593Smuzhiyun int max_intensity = 0;
2168*4882a593Smuzhiyun int ret;
2169*4882a593Smuzhiyun
2170*4882a593Smuzhiyun if (!dmi_check_system(dell_device_table))
2171*4882a593Smuzhiyun return -ENODEV;
2172*4882a593Smuzhiyun
2173*4882a593Smuzhiyun quirks = NULL;
2174*4882a593Smuzhiyun /* find if this machine support other functions */
2175*4882a593Smuzhiyun dmi_check_system(dell_quirks);
2176*4882a593Smuzhiyun
2177*4882a593Smuzhiyun ret = platform_driver_register(&platform_driver);
2178*4882a593Smuzhiyun if (ret)
2179*4882a593Smuzhiyun goto fail_platform_driver;
2180*4882a593Smuzhiyun platform_device = platform_device_alloc("dell-laptop", -1);
2181*4882a593Smuzhiyun if (!platform_device) {
2182*4882a593Smuzhiyun ret = -ENOMEM;
2183*4882a593Smuzhiyun goto fail_platform_device1;
2184*4882a593Smuzhiyun }
2185*4882a593Smuzhiyun ret = platform_device_add(platform_device);
2186*4882a593Smuzhiyun if (ret)
2187*4882a593Smuzhiyun goto fail_platform_device2;
2188*4882a593Smuzhiyun
2189*4882a593Smuzhiyun ret = dell_setup_rfkill();
2190*4882a593Smuzhiyun
2191*4882a593Smuzhiyun if (ret) {
2192*4882a593Smuzhiyun pr_warn("Unable to setup rfkill\n");
2193*4882a593Smuzhiyun goto fail_rfkill;
2194*4882a593Smuzhiyun }
2195*4882a593Smuzhiyun
2196*4882a593Smuzhiyun if (quirks && quirks->touchpad_led)
2197*4882a593Smuzhiyun touchpad_led_init(&platform_device->dev);
2198*4882a593Smuzhiyun
2199*4882a593Smuzhiyun kbd_led_init(&platform_device->dev);
2200*4882a593Smuzhiyun
2201*4882a593Smuzhiyun dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
2202*4882a593Smuzhiyun debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
2203*4882a593Smuzhiyun &dell_debugfs_fops);
2204*4882a593Smuzhiyun
2205*4882a593Smuzhiyun dell_laptop_register_notifier(&dell_laptop_notifier);
2206*4882a593Smuzhiyun
2207*4882a593Smuzhiyun if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) &&
2208*4882a593Smuzhiyun dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE)) {
2209*4882a593Smuzhiyun micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE);
2210*4882a593Smuzhiyun ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev);
2211*4882a593Smuzhiyun if (ret < 0)
2212*4882a593Smuzhiyun goto fail_led;
2213*4882a593Smuzhiyun }
2214*4882a593Smuzhiyun
2215*4882a593Smuzhiyun if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
2216*4882a593Smuzhiyun return 0;
2217*4882a593Smuzhiyun
2218*4882a593Smuzhiyun token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
2219*4882a593Smuzhiyun if (token) {
2220*4882a593Smuzhiyun struct calling_interface_buffer buffer;
2221*4882a593Smuzhiyun
2222*4882a593Smuzhiyun dell_fill_request(&buffer, token->location, 0, 0, 0);
2223*4882a593Smuzhiyun ret = dell_send_request(&buffer,
2224*4882a593Smuzhiyun CLASS_TOKEN_READ, SELECT_TOKEN_AC);
2225*4882a593Smuzhiyun if (ret == 0)
2226*4882a593Smuzhiyun max_intensity = buffer.output[3];
2227*4882a593Smuzhiyun }
2228*4882a593Smuzhiyun
2229*4882a593Smuzhiyun if (max_intensity) {
2230*4882a593Smuzhiyun struct backlight_properties props;
2231*4882a593Smuzhiyun memset(&props, 0, sizeof(struct backlight_properties));
2232*4882a593Smuzhiyun props.type = BACKLIGHT_PLATFORM;
2233*4882a593Smuzhiyun props.max_brightness = max_intensity;
2234*4882a593Smuzhiyun dell_backlight_device = backlight_device_register("dell_backlight",
2235*4882a593Smuzhiyun &platform_device->dev,
2236*4882a593Smuzhiyun NULL,
2237*4882a593Smuzhiyun &dell_ops,
2238*4882a593Smuzhiyun &props);
2239*4882a593Smuzhiyun
2240*4882a593Smuzhiyun if (IS_ERR(dell_backlight_device)) {
2241*4882a593Smuzhiyun ret = PTR_ERR(dell_backlight_device);
2242*4882a593Smuzhiyun dell_backlight_device = NULL;
2243*4882a593Smuzhiyun goto fail_backlight;
2244*4882a593Smuzhiyun }
2245*4882a593Smuzhiyun
2246*4882a593Smuzhiyun dell_backlight_device->props.brightness =
2247*4882a593Smuzhiyun dell_get_intensity(dell_backlight_device);
2248*4882a593Smuzhiyun if (dell_backlight_device->props.brightness < 0) {
2249*4882a593Smuzhiyun ret = dell_backlight_device->props.brightness;
2250*4882a593Smuzhiyun goto fail_get_brightness;
2251*4882a593Smuzhiyun }
2252*4882a593Smuzhiyun backlight_update_status(dell_backlight_device);
2253*4882a593Smuzhiyun }
2254*4882a593Smuzhiyun
2255*4882a593Smuzhiyun return 0;
2256*4882a593Smuzhiyun
2257*4882a593Smuzhiyun fail_get_brightness:
2258*4882a593Smuzhiyun backlight_device_unregister(dell_backlight_device);
2259*4882a593Smuzhiyun fail_backlight:
2260*4882a593Smuzhiyun led_classdev_unregister(&micmute_led_cdev);
2261*4882a593Smuzhiyun fail_led:
2262*4882a593Smuzhiyun dell_cleanup_rfkill();
2263*4882a593Smuzhiyun fail_rfkill:
2264*4882a593Smuzhiyun platform_device_del(platform_device);
2265*4882a593Smuzhiyun fail_platform_device2:
2266*4882a593Smuzhiyun platform_device_put(platform_device);
2267*4882a593Smuzhiyun fail_platform_device1:
2268*4882a593Smuzhiyun platform_driver_unregister(&platform_driver);
2269*4882a593Smuzhiyun fail_platform_driver:
2270*4882a593Smuzhiyun return ret;
2271*4882a593Smuzhiyun }
2272*4882a593Smuzhiyun
dell_exit(void)2273*4882a593Smuzhiyun static void __exit dell_exit(void)
2274*4882a593Smuzhiyun {
2275*4882a593Smuzhiyun dell_laptop_unregister_notifier(&dell_laptop_notifier);
2276*4882a593Smuzhiyun debugfs_remove_recursive(dell_laptop_dir);
2277*4882a593Smuzhiyun if (quirks && quirks->touchpad_led)
2278*4882a593Smuzhiyun touchpad_led_exit();
2279*4882a593Smuzhiyun kbd_led_exit();
2280*4882a593Smuzhiyun backlight_device_unregister(dell_backlight_device);
2281*4882a593Smuzhiyun led_classdev_unregister(&micmute_led_cdev);
2282*4882a593Smuzhiyun dell_cleanup_rfkill();
2283*4882a593Smuzhiyun if (platform_device) {
2284*4882a593Smuzhiyun platform_device_unregister(platform_device);
2285*4882a593Smuzhiyun platform_driver_unregister(&platform_driver);
2286*4882a593Smuzhiyun }
2287*4882a593Smuzhiyun }
2288*4882a593Smuzhiyun
2289*4882a593Smuzhiyun /* dell-rbtn.c driver export functions which will not work correctly (and could
2290*4882a593Smuzhiyun * cause kernel crash) if they are called before dell-rbtn.c init code. This is
2291*4882a593Smuzhiyun * not problem when dell-rbtn.c is compiled as external module. When both files
2292*4882a593Smuzhiyun * (dell-rbtn.c and dell-laptop.c) are compiled statically into kernel, then we
2293*4882a593Smuzhiyun * need to ensure that dell_init() will be called after initializing dell-rbtn.
2294*4882a593Smuzhiyun * This can be achieved by late_initcall() instead module_init().
2295*4882a593Smuzhiyun */
2296*4882a593Smuzhiyun late_initcall(dell_init);
2297*4882a593Smuzhiyun module_exit(dell_exit);
2298*4882a593Smuzhiyun
2299*4882a593Smuzhiyun MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
2300*4882a593Smuzhiyun MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
2301*4882a593Smuzhiyun MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
2302*4882a593Smuzhiyun MODULE_DESCRIPTION("Dell laptop driver");
2303*4882a593Smuzhiyun MODULE_LICENSE("GPL");
2304