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