xref: /OK3568_Linux_fs/kernel/drivers/hid/hid-google-hammer.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  HID driver for Google Hammer device.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (c) 2017 Google Inc.
6*4882a593Smuzhiyun  *  Author: Wei-Ning Huang <wnhuang@google.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun /*
10*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify it
11*4882a593Smuzhiyun  * under the terms of the GNU General Public License as published by the Free
12*4882a593Smuzhiyun  * Software Foundation; either version 2 of the License, or (at your option)
13*4882a593Smuzhiyun  * any later version.
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <linux/acpi.h>
17*4882a593Smuzhiyun #include <linux/hid.h>
18*4882a593Smuzhiyun #include <linux/leds.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/platform_data/cros_ec_commands.h>
21*4882a593Smuzhiyun #include <linux/platform_data/cros_ec_proto.h>
22*4882a593Smuzhiyun #include <linux/platform_device.h>
23*4882a593Smuzhiyun #include <linux/pm_wakeup.h>
24*4882a593Smuzhiyun #include <asm/unaligned.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #include "hid-ids.h"
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun  * C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting
30*4882a593Smuzhiyun  * state of the "Whiskers" base - attached or detached. Whiskers USB device also
31*4882a593Smuzhiyun  * reports position of the keyboard - folded or not. Combining base state and
32*4882a593Smuzhiyun  * position allows us to generate proper "Tablet mode" events.
33*4882a593Smuzhiyun  */
34*4882a593Smuzhiyun struct cbas_ec {
35*4882a593Smuzhiyun 	struct device *dev;	/* The platform device (EC) */
36*4882a593Smuzhiyun 	struct input_dev *input;
37*4882a593Smuzhiyun 	bool base_present;
38*4882a593Smuzhiyun 	bool base_folded;
39*4882a593Smuzhiyun 	struct notifier_block notifier;
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun static struct cbas_ec cbas_ec;
43*4882a593Smuzhiyun static DEFINE_SPINLOCK(cbas_ec_lock);
44*4882a593Smuzhiyun static DEFINE_MUTEX(cbas_ec_reglock);
45*4882a593Smuzhiyun 
cbas_parse_base_state(const void * data)46*4882a593Smuzhiyun static bool cbas_parse_base_state(const void *data)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	u32 switches = get_unaligned_le32(data);
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	return !!(switches & BIT(EC_MKBP_BASE_ATTACHED));
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
cbas_ec_query_base(struct cros_ec_device * ec_dev,bool get_state,bool * state)53*4882a593Smuzhiyun static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state,
54*4882a593Smuzhiyun 				  bool *state)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	struct ec_params_mkbp_info *params;
57*4882a593Smuzhiyun 	struct cros_ec_command *msg;
58*4882a593Smuzhiyun 	int ret;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	msg = kzalloc(sizeof(*msg) + max(sizeof(u32), sizeof(*params)),
61*4882a593Smuzhiyun 		      GFP_KERNEL);
62*4882a593Smuzhiyun 	if (!msg)
63*4882a593Smuzhiyun 		return -ENOMEM;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	msg->command = EC_CMD_MKBP_INFO;
66*4882a593Smuzhiyun 	msg->version = 1;
67*4882a593Smuzhiyun 	msg->outsize = sizeof(*params);
68*4882a593Smuzhiyun 	msg->insize = sizeof(u32);
69*4882a593Smuzhiyun 	params = (struct ec_params_mkbp_info *)msg->data;
70*4882a593Smuzhiyun 	params->info_type = get_state ?
71*4882a593Smuzhiyun 		EC_MKBP_INFO_CURRENT : EC_MKBP_INFO_SUPPORTED;
72*4882a593Smuzhiyun 	params->event_type = EC_MKBP_EVENT_SWITCH;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	ret = cros_ec_cmd_xfer_status(ec_dev, msg);
75*4882a593Smuzhiyun 	if (ret >= 0) {
76*4882a593Smuzhiyun 		if (ret != sizeof(u32)) {
77*4882a593Smuzhiyun 			dev_warn(ec_dev->dev, "wrong result size: %d != %zu\n",
78*4882a593Smuzhiyun 				 ret, sizeof(u32));
79*4882a593Smuzhiyun 			ret = -EPROTO;
80*4882a593Smuzhiyun 		} else {
81*4882a593Smuzhiyun 			*state = cbas_parse_base_state(msg->data);
82*4882a593Smuzhiyun 			ret = 0;
83*4882a593Smuzhiyun 		}
84*4882a593Smuzhiyun 	}
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	kfree(msg);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	return ret;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
cbas_ec_notify(struct notifier_block * nb,unsigned long queued_during_suspend,void * _notify)91*4882a593Smuzhiyun static int cbas_ec_notify(struct notifier_block *nb,
92*4882a593Smuzhiyun 			      unsigned long queued_during_suspend,
93*4882a593Smuzhiyun 			      void *_notify)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	struct cros_ec_device *ec = _notify;
96*4882a593Smuzhiyun 	unsigned long flags;
97*4882a593Smuzhiyun 	bool base_present;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (ec->event_data.event_type == EC_MKBP_EVENT_SWITCH) {
100*4882a593Smuzhiyun 		base_present = cbas_parse_base_state(
101*4882a593Smuzhiyun 					&ec->event_data.data.switches);
102*4882a593Smuzhiyun 		dev_dbg(cbas_ec.dev,
103*4882a593Smuzhiyun 			"%s: base: %d\n", __func__, base_present);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 		if (device_may_wakeup(cbas_ec.dev) ||
106*4882a593Smuzhiyun 		    !queued_during_suspend) {
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 			pm_wakeup_event(cbas_ec.dev, 0);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 			spin_lock_irqsave(&cbas_ec_lock, flags);
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 			/*
113*4882a593Smuzhiyun 			 * While input layer dedupes the events, we do not want
114*4882a593Smuzhiyun 			 * to disrupt the state reported by the base by
115*4882a593Smuzhiyun 			 * overriding it with state reported by the LID. Only
116*4882a593Smuzhiyun 			 * report changes, as we assume that on attach the base
117*4882a593Smuzhiyun 			 * is not folded.
118*4882a593Smuzhiyun 			 */
119*4882a593Smuzhiyun 			if (base_present != cbas_ec.base_present) {
120*4882a593Smuzhiyun 				input_report_switch(cbas_ec.input,
121*4882a593Smuzhiyun 						    SW_TABLET_MODE,
122*4882a593Smuzhiyun 						    !base_present);
123*4882a593Smuzhiyun 				input_sync(cbas_ec.input);
124*4882a593Smuzhiyun 				cbas_ec.base_present = base_present;
125*4882a593Smuzhiyun 			}
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 			spin_unlock_irqrestore(&cbas_ec_lock, flags);
128*4882a593Smuzhiyun 		}
129*4882a593Smuzhiyun 	}
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	return NOTIFY_OK;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
cbas_ec_resume(struct device * dev)134*4882a593Smuzhiyun static __maybe_unused int cbas_ec_resume(struct device *dev)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	struct cros_ec_device *ec = dev_get_drvdata(dev->parent);
137*4882a593Smuzhiyun 	bool base_present;
138*4882a593Smuzhiyun 	int error;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	error = cbas_ec_query_base(ec, true, &base_present);
141*4882a593Smuzhiyun 	if (error) {
142*4882a593Smuzhiyun 		dev_warn(dev, "failed to fetch base state on resume: %d\n",
143*4882a593Smuzhiyun 			 error);
144*4882a593Smuzhiyun 	} else {
145*4882a593Smuzhiyun 		spin_lock_irq(&cbas_ec_lock);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 		cbas_ec.base_present = base_present;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 		/*
150*4882a593Smuzhiyun 		 * Only report if base is disconnected. If base is connected,
151*4882a593Smuzhiyun 		 * it will resend its state on resume, and we'll update it
152*4882a593Smuzhiyun 		 * in hammer_event().
153*4882a593Smuzhiyun 		 */
154*4882a593Smuzhiyun 		if (!cbas_ec.base_present) {
155*4882a593Smuzhiyun 			input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1);
156*4882a593Smuzhiyun 			input_sync(cbas_ec.input);
157*4882a593Smuzhiyun 		}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 		spin_unlock_irq(&cbas_ec_lock);
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	return 0;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(cbas_ec_pm_ops, NULL, cbas_ec_resume);
166*4882a593Smuzhiyun 
cbas_ec_set_input(struct input_dev * input)167*4882a593Smuzhiyun static void cbas_ec_set_input(struct input_dev *input)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun 	/* Take the lock so hammer_event() does not race with us here */
170*4882a593Smuzhiyun 	spin_lock_irq(&cbas_ec_lock);
171*4882a593Smuzhiyun 	cbas_ec.input = input;
172*4882a593Smuzhiyun 	spin_unlock_irq(&cbas_ec_lock);
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun 
__cbas_ec_probe(struct platform_device * pdev)175*4882a593Smuzhiyun static int __cbas_ec_probe(struct platform_device *pdev)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun 	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
178*4882a593Smuzhiyun 	struct input_dev *input;
179*4882a593Smuzhiyun 	bool base_supported;
180*4882a593Smuzhiyun 	int error;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	error = cbas_ec_query_base(ec, false, &base_supported);
183*4882a593Smuzhiyun 	if (error)
184*4882a593Smuzhiyun 		return error;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	if (!base_supported)
187*4882a593Smuzhiyun 		return -ENXIO;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	input = devm_input_allocate_device(&pdev->dev);
190*4882a593Smuzhiyun 	if (!input)
191*4882a593Smuzhiyun 		return -ENOMEM;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	input->name = "Whiskers Tablet Mode Switch";
194*4882a593Smuzhiyun 	input->id.bustype = BUS_HOST;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	input_set_capability(input, EV_SW, SW_TABLET_MODE);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	error = input_register_device(input);
199*4882a593Smuzhiyun 	if (error) {
200*4882a593Smuzhiyun 		dev_err(&pdev->dev, "cannot register input device: %d\n",
201*4882a593Smuzhiyun 			error);
202*4882a593Smuzhiyun 		return error;
203*4882a593Smuzhiyun 	}
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	/* Seed the state */
206*4882a593Smuzhiyun 	error = cbas_ec_query_base(ec, true, &cbas_ec.base_present);
207*4882a593Smuzhiyun 	if (error) {
208*4882a593Smuzhiyun 		dev_err(&pdev->dev, "cannot query base state: %d\n", error);
209*4882a593Smuzhiyun 		return error;
210*4882a593Smuzhiyun 	}
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	if (!cbas_ec.base_present)
213*4882a593Smuzhiyun 		cbas_ec.base_folded = false;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "%s: base: %d, folded: %d\n", __func__,
216*4882a593Smuzhiyun 		cbas_ec.base_present, cbas_ec.base_folded);
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	input_report_switch(input, SW_TABLET_MODE,
219*4882a593Smuzhiyun 			    !cbas_ec.base_present || cbas_ec.base_folded);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	cbas_ec_set_input(input);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	cbas_ec.dev = &pdev->dev;
224*4882a593Smuzhiyun 	cbas_ec.notifier.notifier_call = cbas_ec_notify;
225*4882a593Smuzhiyun 	error = blocking_notifier_chain_register(&ec->event_notifier,
226*4882a593Smuzhiyun 						 &cbas_ec.notifier);
227*4882a593Smuzhiyun 	if (error) {
228*4882a593Smuzhiyun 		dev_err(&pdev->dev, "cannot register notifier: %d\n", error);
229*4882a593Smuzhiyun 		cbas_ec_set_input(NULL);
230*4882a593Smuzhiyun 		return error;
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	device_init_wakeup(&pdev->dev, true);
234*4882a593Smuzhiyun 	return 0;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun 
cbas_ec_probe(struct platform_device * pdev)237*4882a593Smuzhiyun static int cbas_ec_probe(struct platform_device *pdev)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun 	int retval;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	mutex_lock(&cbas_ec_reglock);
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	if (cbas_ec.input) {
244*4882a593Smuzhiyun 		retval = -EBUSY;
245*4882a593Smuzhiyun 		goto out;
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	retval = __cbas_ec_probe(pdev);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun out:
251*4882a593Smuzhiyun 	mutex_unlock(&cbas_ec_reglock);
252*4882a593Smuzhiyun 	return retval;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun 
cbas_ec_remove(struct platform_device * pdev)255*4882a593Smuzhiyun static int cbas_ec_remove(struct platform_device *pdev)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	mutex_lock(&cbas_ec_reglock);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	blocking_notifier_chain_unregister(&ec->event_notifier,
262*4882a593Smuzhiyun 					   &cbas_ec.notifier);
263*4882a593Smuzhiyun 	cbas_ec_set_input(NULL);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	mutex_unlock(&cbas_ec_reglock);
266*4882a593Smuzhiyun 	return 0;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun static const struct acpi_device_id cbas_ec_acpi_ids[] = {
270*4882a593Smuzhiyun 	{ "GOOG000B", 0 },
271*4882a593Smuzhiyun 	{ }
272*4882a593Smuzhiyun };
273*4882a593Smuzhiyun MODULE_DEVICE_TABLE(acpi, cbas_ec_acpi_ids);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun static struct platform_driver cbas_ec_driver = {
276*4882a593Smuzhiyun 	.probe = cbas_ec_probe,
277*4882a593Smuzhiyun 	.remove = cbas_ec_remove,
278*4882a593Smuzhiyun 	.driver = {
279*4882a593Smuzhiyun 		.name = "cbas_ec",
280*4882a593Smuzhiyun 		.acpi_match_table = ACPI_PTR(cbas_ec_acpi_ids),
281*4882a593Smuzhiyun 		.pm = &cbas_ec_pm_ops,
282*4882a593Smuzhiyun 	},
283*4882a593Smuzhiyun };
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun #define MAX_BRIGHTNESS 100
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun struct hammer_kbd_leds {
288*4882a593Smuzhiyun 	struct led_classdev cdev;
289*4882a593Smuzhiyun 	struct hid_device *hdev;
290*4882a593Smuzhiyun 	u8 buf[2] ____cacheline_aligned;
291*4882a593Smuzhiyun };
292*4882a593Smuzhiyun 
hammer_kbd_brightness_set_blocking(struct led_classdev * cdev,enum led_brightness br)293*4882a593Smuzhiyun static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
294*4882a593Smuzhiyun 		enum led_brightness br)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun 	struct hammer_kbd_leds *led = container_of(cdev,
297*4882a593Smuzhiyun 						   struct hammer_kbd_leds,
298*4882a593Smuzhiyun 						   cdev);
299*4882a593Smuzhiyun 	int ret;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	led->buf[0] = 0;
302*4882a593Smuzhiyun 	led->buf[1] = br;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	/*
305*4882a593Smuzhiyun 	 * Request USB HID device to be in Full On mode, so that sending
306*4882a593Smuzhiyun 	 * hardware output report and hardware raw request won't fail.
307*4882a593Smuzhiyun 	 */
308*4882a593Smuzhiyun 	ret = hid_hw_power(led->hdev, PM_HINT_FULLON);
309*4882a593Smuzhiyun 	if (ret < 0) {
310*4882a593Smuzhiyun 		hid_err(led->hdev, "failed: device not resumed %d\n", ret);
311*4882a593Smuzhiyun 		return ret;
312*4882a593Smuzhiyun 	}
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	ret = hid_hw_output_report(led->hdev, led->buf, sizeof(led->buf));
315*4882a593Smuzhiyun 	if (ret == -ENOSYS)
316*4882a593Smuzhiyun 		ret = hid_hw_raw_request(led->hdev, 0, led->buf,
317*4882a593Smuzhiyun 					 sizeof(led->buf),
318*4882a593Smuzhiyun 					 HID_OUTPUT_REPORT,
319*4882a593Smuzhiyun 					 HID_REQ_SET_REPORT);
320*4882a593Smuzhiyun 	if (ret < 0)
321*4882a593Smuzhiyun 		hid_err(led->hdev, "failed to set keyboard backlight: %d\n",
322*4882a593Smuzhiyun 			ret);
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	/* Request USB HID device back to Normal Mode. */
325*4882a593Smuzhiyun 	hid_hw_power(led->hdev, PM_HINT_NORMAL);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	return ret;
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun 
hammer_register_leds(struct hid_device * hdev)330*4882a593Smuzhiyun static int hammer_register_leds(struct hid_device *hdev)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun 	struct hammer_kbd_leds *kbd_backlight;
333*4882a593Smuzhiyun 	int error;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL);
336*4882a593Smuzhiyun 	if (!kbd_backlight)
337*4882a593Smuzhiyun 		return -ENOMEM;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	kbd_backlight->hdev = hdev;
340*4882a593Smuzhiyun 	kbd_backlight->cdev.name = "hammer::kbd_backlight";
341*4882a593Smuzhiyun 	kbd_backlight->cdev.max_brightness = MAX_BRIGHTNESS;
342*4882a593Smuzhiyun 	kbd_backlight->cdev.brightness_set_blocking =
343*4882a593Smuzhiyun 		hammer_kbd_brightness_set_blocking;
344*4882a593Smuzhiyun 	kbd_backlight->cdev.flags = LED_HW_PLUGGABLE;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	/* Set backlight to 0% initially. */
347*4882a593Smuzhiyun 	hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
350*4882a593Smuzhiyun 	if (error)
351*4882a593Smuzhiyun 		goto err_free_mem;
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	hid_set_drvdata(hdev, kbd_backlight);
354*4882a593Smuzhiyun 	return 0;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun err_free_mem:
357*4882a593Smuzhiyun 	kfree(kbd_backlight);
358*4882a593Smuzhiyun 	return error;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun 
hammer_unregister_leds(struct hid_device * hdev)361*4882a593Smuzhiyun static void hammer_unregister_leds(struct hid_device *hdev)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun 	struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev);
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	if (kbd_backlight) {
366*4882a593Smuzhiyun 		led_classdev_unregister(&kbd_backlight->cdev);
367*4882a593Smuzhiyun 		kfree(kbd_backlight);
368*4882a593Smuzhiyun 	}
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun #define HID_UP_GOOGLEVENDOR	0xffd10000
372*4882a593Smuzhiyun #define HID_VD_KBD_FOLDED	0x00000019
373*4882a593Smuzhiyun #define HID_USAGE_KBD_FOLDED	(HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED)
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun /* HID usage for keyboard backlight (Alphanumeric display brightness) */
376*4882a593Smuzhiyun #define HID_AD_BRIGHTNESS	0x00140046
377*4882a593Smuzhiyun 
hammer_input_mapping(struct hid_device * hdev,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)378*4882a593Smuzhiyun static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi,
379*4882a593Smuzhiyun 				struct hid_field *field,
380*4882a593Smuzhiyun 				struct hid_usage *usage,
381*4882a593Smuzhiyun 				unsigned long **bit, int *max)
382*4882a593Smuzhiyun {
383*4882a593Smuzhiyun 	if (usage->hid == HID_USAGE_KBD_FOLDED) {
384*4882a593Smuzhiyun 		/*
385*4882a593Smuzhiyun 		 * We do not want to have this usage mapped as it will get
386*4882a593Smuzhiyun 		 * mixed in with "base attached" signal and delivered over
387*4882a593Smuzhiyun 		 * separate input device for tablet switch mode.
388*4882a593Smuzhiyun 		 */
389*4882a593Smuzhiyun 		return -1;
390*4882a593Smuzhiyun 	}
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	return 0;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun 
hammer_event(struct hid_device * hid,struct hid_field * field,struct hid_usage * usage,__s32 value)395*4882a593Smuzhiyun static int hammer_event(struct hid_device *hid, struct hid_field *field,
396*4882a593Smuzhiyun 			struct hid_usage *usage, __s32 value)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun 	unsigned long flags;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	if (usage->hid == HID_USAGE_KBD_FOLDED) {
401*4882a593Smuzhiyun 		spin_lock_irqsave(&cbas_ec_lock, flags);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 		/*
404*4882a593Smuzhiyun 		 * If we are getting events from Whiskers that means that it
405*4882a593Smuzhiyun 		 * is attached to the lid.
406*4882a593Smuzhiyun 		 */
407*4882a593Smuzhiyun 		cbas_ec.base_present = true;
408*4882a593Smuzhiyun 		cbas_ec.base_folded = value;
409*4882a593Smuzhiyun 		hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
410*4882a593Smuzhiyun 			cbas_ec.base_present, cbas_ec.base_folded);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 		if (cbas_ec.input) {
413*4882a593Smuzhiyun 			input_report_switch(cbas_ec.input,
414*4882a593Smuzhiyun 					    SW_TABLET_MODE, value);
415*4882a593Smuzhiyun 			input_sync(cbas_ec.input);
416*4882a593Smuzhiyun 		}
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 		spin_unlock_irqrestore(&cbas_ec_lock, flags);
419*4882a593Smuzhiyun 		return 1; /* We handled this event */
420*4882a593Smuzhiyun 	}
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	return 0;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun 
hammer_has_usage(struct hid_device * hdev,unsigned int report_type,unsigned application,unsigned usage)425*4882a593Smuzhiyun static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type,
426*4882a593Smuzhiyun 			unsigned application, unsigned usage)
427*4882a593Smuzhiyun {
428*4882a593Smuzhiyun 	struct hid_report_enum *re = &hdev->report_enum[report_type];
429*4882a593Smuzhiyun 	struct hid_report *report;
430*4882a593Smuzhiyun 	int i, j;
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	list_for_each_entry(report, &re->report_list, list) {
433*4882a593Smuzhiyun 		if (report->application != application)
434*4882a593Smuzhiyun 			continue;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 		for (i = 0; i < report->maxfield; i++) {
437*4882a593Smuzhiyun 			struct hid_field *field = report->field[i];
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 			for (j = 0; j < field->maxusage; j++)
440*4882a593Smuzhiyun 				if (field->usage[j].hid == usage)
441*4882a593Smuzhiyun 					return true;
442*4882a593Smuzhiyun 		}
443*4882a593Smuzhiyun 	}
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	return false;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun 
hammer_has_folded_event(struct hid_device * hdev)448*4882a593Smuzhiyun static bool hammer_has_folded_event(struct hid_device *hdev)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun 	return hammer_has_usage(hdev, HID_INPUT_REPORT,
451*4882a593Smuzhiyun 				HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED);
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun 
hammer_has_backlight_control(struct hid_device * hdev)454*4882a593Smuzhiyun static bool hammer_has_backlight_control(struct hid_device *hdev)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun 	return hammer_has_usage(hdev, HID_OUTPUT_REPORT,
457*4882a593Smuzhiyun 				HID_GD_KEYBOARD, HID_AD_BRIGHTNESS);
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun 
hammer_probe(struct hid_device * hdev,const struct hid_device_id * id)460*4882a593Smuzhiyun static int hammer_probe(struct hid_device *hdev,
461*4882a593Smuzhiyun 			const struct hid_device_id *id)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun 	int error;
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	error = hid_parse(hdev);
466*4882a593Smuzhiyun 	if (error)
467*4882a593Smuzhiyun 		return error;
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	error = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
470*4882a593Smuzhiyun 	if (error)
471*4882a593Smuzhiyun 		return error;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	/*
474*4882a593Smuzhiyun 	 * We always want to poll for, and handle tablet mode events from
475*4882a593Smuzhiyun 	 * devices that have folded usage, even when nobody has opened the input
476*4882a593Smuzhiyun 	 * device. This also prevents the hid core from dropping early tablet
477*4882a593Smuzhiyun 	 * mode events from the device.
478*4882a593Smuzhiyun 	 */
479*4882a593Smuzhiyun 	if (hammer_has_folded_event(hdev)) {
480*4882a593Smuzhiyun 		hdev->quirks |= HID_QUIRK_ALWAYS_POLL;
481*4882a593Smuzhiyun 		error = hid_hw_open(hdev);
482*4882a593Smuzhiyun 		if (error)
483*4882a593Smuzhiyun 			return error;
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	if (hammer_has_backlight_control(hdev)) {
487*4882a593Smuzhiyun 		error = hammer_register_leds(hdev);
488*4882a593Smuzhiyun 		if (error)
489*4882a593Smuzhiyun 			hid_warn(hdev,
490*4882a593Smuzhiyun 				"Failed to register keyboard backlight: %d\n",
491*4882a593Smuzhiyun 				error);
492*4882a593Smuzhiyun 	}
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	return 0;
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun 
hammer_remove(struct hid_device * hdev)497*4882a593Smuzhiyun static void hammer_remove(struct hid_device *hdev)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 	unsigned long flags;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	if (hammer_has_folded_event(hdev)) {
502*4882a593Smuzhiyun 		hid_hw_close(hdev);
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 		/*
505*4882a593Smuzhiyun 		 * If we are disconnecting then most likely Whiskers is
506*4882a593Smuzhiyun 		 * being removed. Even if it is not removed, without proper
507*4882a593Smuzhiyun 		 * keyboard we should not stay in clamshell mode.
508*4882a593Smuzhiyun 		 *
509*4882a593Smuzhiyun 		 * The reason for doing it here and not waiting for signal
510*4882a593Smuzhiyun 		 * from EC, is that on some devices there are high leakage
511*4882a593Smuzhiyun 		 * on Whiskers pins and we do not detect disconnect reliably,
512*4882a593Smuzhiyun 		 * resulting in devices being stuck in clamshell mode.
513*4882a593Smuzhiyun 		 */
514*4882a593Smuzhiyun 		spin_lock_irqsave(&cbas_ec_lock, flags);
515*4882a593Smuzhiyun 		if (cbas_ec.input && cbas_ec.base_present) {
516*4882a593Smuzhiyun 			input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1);
517*4882a593Smuzhiyun 			input_sync(cbas_ec.input);
518*4882a593Smuzhiyun 		}
519*4882a593Smuzhiyun 		cbas_ec.base_present = false;
520*4882a593Smuzhiyun 		spin_unlock_irqrestore(&cbas_ec_lock, flags);
521*4882a593Smuzhiyun 	}
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	hammer_unregister_leds(hdev);
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	hid_hw_stop(hdev);
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun static const struct hid_device_id hammer_devices[] = {
529*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
530*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_DON) },
531*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
532*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_EEL) },
533*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
534*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
535*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
536*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MAGNEMITE) },
537*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
538*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MASTERBALL) },
539*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
540*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MOONBALL) },
541*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
542*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) },
543*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
544*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WAND) },
545*4882a593Smuzhiyun 	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
546*4882a593Smuzhiyun 		     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WHISKERS) },
547*4882a593Smuzhiyun 	{ }
548*4882a593Smuzhiyun };
549*4882a593Smuzhiyun MODULE_DEVICE_TABLE(hid, hammer_devices);
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun static struct hid_driver hammer_driver = {
552*4882a593Smuzhiyun 	.name = "hammer",
553*4882a593Smuzhiyun 	.id_table = hammer_devices,
554*4882a593Smuzhiyun 	.probe = hammer_probe,
555*4882a593Smuzhiyun 	.remove = hammer_remove,
556*4882a593Smuzhiyun 	.input_mapping = hammer_input_mapping,
557*4882a593Smuzhiyun 	.event = hammer_event,
558*4882a593Smuzhiyun };
559*4882a593Smuzhiyun 
hammer_init(void)560*4882a593Smuzhiyun static int __init hammer_init(void)
561*4882a593Smuzhiyun {
562*4882a593Smuzhiyun 	int error;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	error = platform_driver_register(&cbas_ec_driver);
565*4882a593Smuzhiyun 	if (error)
566*4882a593Smuzhiyun 		return error;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	error = hid_register_driver(&hammer_driver);
569*4882a593Smuzhiyun 	if (error) {
570*4882a593Smuzhiyun 		platform_driver_unregister(&cbas_ec_driver);
571*4882a593Smuzhiyun 		return error;
572*4882a593Smuzhiyun 	}
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	return 0;
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun module_init(hammer_init);
577*4882a593Smuzhiyun 
hammer_exit(void)578*4882a593Smuzhiyun static void __exit hammer_exit(void)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun 	hid_unregister_driver(&hammer_driver);
581*4882a593Smuzhiyun 	platform_driver_unregister(&cbas_ec_driver);
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun module_exit(hammer_exit);
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun MODULE_LICENSE("GPL");
586