summarylogtreecommitdiffstats
path: root/kernel.patch
blob: 2cdae0082f1239b750bf7496ce9ba00426df2b6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
diff -ur old/drivers/input/mouse/Kconfig new/drivers/input/mouse/Kconfig
--- old/drivers/input/mouse/Kconfig
+++ new/drivers/input/mouse/Kconfig
@@ -101,6 +101,15 @@
 
 	  If unsure, say Y.
 
+config MOUSE_PS2_SYNAPTICS_LED
+	bool "Support embedded LED on Synaptics devices"
+	default y
+	depends on MOUSE_PS2_SYNAPTICS
+	depends on LEDS_CLASS=y || LEDS_CLASS=MOUSE_PS2
+	help
+	  Say Y here if you have a Synaptics device with an embedded LED.
+	  This will enable LED class driver to control the LED device.
+
 config MOUSE_PS2_LIFEBOOK
 	bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
 	default y
diff -ur old/drivers/input/mouse/synaptics.c new/drivers/input/mouse/synaptics.c
--- old/drivers/input/mouse/synaptics.c
+++ new/drivers/input/mouse/synaptics.c
@@ -26,6 +26,7 @@
 #include <linux/input/mt.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
+#include <linux/leds.h>
 #include <linux/rmi.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
@@ -728,6 +729,141 @@
 	serio_register_port(serio);
 }
 
+#if 1
+/*
+ * LED handling:
+ * Some Synaptics devices have an embeded LED at the top-left corner.
+ */
+
+struct synaptics_led {
+	struct psmouse *psmouse;
+	struct work_struct work;
+	struct led_classdev cdev;
+};
+
+static void synaptics_set_led(struct psmouse *psmouse, int on)
+{
+	int i;
+	unsigned char cmd = on ? 0x88 : 0x10;
+
+	ps2_begin_command(&psmouse->ps2dev);
+	if (__ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
+		goto out;
+	for (i = 6; i >= 0; i -= 2) {
+		unsigned char d = (cmd >> i) & 3;
+		if (__ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
+			goto out;
+	}
+	cmd = 0x0a;
+	__ps2_command(&psmouse->ps2dev, &cmd, PSMOUSE_CMD_SETRATE);
+ out:
+	ps2_end_command(&psmouse->ps2dev);
+}
+
+static void synaptics_led_work(struct work_struct *work)
+{
+	struct synaptics_led *led;
+
+	led = container_of(work, struct synaptics_led, work);
+	synaptics_set_led(led->psmouse, led->cdev.brightness);
+}
+
+static void synaptics_led_cdev_brightness_set(struct led_classdev *cdev,
+					      enum led_brightness value)
+{
+	struct synaptics_led *led;
+
+	led = container_of(cdev, struct synaptics_led, cdev);
+	schedule_work(&led->work);
+}
+
+static void synaptics_sync_led(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (priv->led)
+		synaptics_set_led(psmouse, priv->led->cdev.brightness);
+}
+
+static bool synaptics_has_led(struct synaptics_data *priv)
+{
+	printk(KERN_NOTICE "synaptics: led: checking for led\n");
+
+	if (!priv->info.ext_cap_0c) {
+		printk(KERN_NOTICE "synaptics: led: !priv->ext_cap_0c is true... no led\n");
+		return false;
+	}
+
+	printk(KERN_NOTICE "synaptics: led: your product ID is %2lx\n", SYN_CAP_PRODUCT_ID(priv->info.ext_cap));
+
+	/* FIXME: LED is supposedly detectable in cap0c[1] 0x20, but it seems
+	 * not working on real machines.
+	 * So we check the product id to be sure.
+	 * Added ID 0x24 experimental.
+	 */
+
+	if (SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0x24 &&
+       SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0xe4 &&
+	    SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0x64 &&
+	    SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0x84) {
+		printk(KERN_NOTICE "synaptics: led: your product ID is not in the whitelist\n");
+		return false;
+	}
+
+	if (!(priv->info.ext_cap_0c & 0x2000) &&
+	    (priv->info.capabilities & 0xd000fd) != 0xd00071) {
+		printk(KERN_NOTICE "synaptics: led: failed capabilities check\n");
+		return false;
+	}
+
+	printk(KERN_NOTICE "synaptics: led: looks like you have one\n");
+
+	return true;
+}
+
+static int synaptics_init_led(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct synaptics_led *led;
+	int err;
+
+	if (!synaptics_has_led(priv))
+		return 0;
+	printk(KERN_INFO "synaptics: support LED control\n");
+	led = kzalloc(sizeof(struct synaptics_led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+	led->psmouse = psmouse;
+	INIT_WORK(&led->work, synaptics_led_work);
+	led->cdev.name = "psmouse::synaptics";
+	led->cdev.brightness_set = synaptics_led_cdev_brightness_set;
+	led->cdev.flags = LED_CORE_SUSPENDRESUME;
+	err = led_classdev_register(NULL, &led->cdev);
+	if (err < 0) {
+		kfree(led);
+		return err;
+	}
+	priv->led = led;
+	return 0;
+}
+
+static void synaptics_free_led(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (!priv->led)
+		return;
+	cancel_work_sync(&priv->led->work);
+	synaptics_set_led(psmouse, 0);
+	led_classdev_unregister(&priv->led->cdev);
+	kfree(priv->led);
+}
+#else
+#define synaptics_init_led(ps)	0
+#define synaptics_free_led(ps)	do {} while (0)
+#define synaptics_sync_led(ps)	do {} while (0)
+#endif
+
 /*****************************************************************************
  *	Functions to interpret the absolute mode packets
  ****************************************************************************/
@@ -1415,6 +1551,7 @@
 		device_remove_file(&psmouse->ps2dev.serio->dev,
 				   &psmouse_attr_disable_gesture.dattr);
 
+	synaptics_free_led(psmouse);
 	synaptics_reset(psmouse);
 	kfree(priv);
 	psmouse->private = NULL;
@@ -1475,6 +1612,8 @@
 		return -ENXIO;
 	}
 
+	synaptics_sync_led(psmouse);
+
 	return 0;
 }
 
@@ -1593,6 +1732,9 @@
 		     info->capabilities, info->ext_cap, info->ext_cap_0c,
 		     info->ext_cap_10, info->board_id, info->firmware_id);
 
+	if (synaptics_init_led(psmouse) < 0)
+		goto init_fail;
+
 	err = set_input_params(psmouse, priv);
 	if (err) {
 		psmouse_err(psmouse,
diff -ur old/drivers/input/mouse/synaptics.h new/drivers/input/mouse/synaptics.h
--- old/drivers/input/mouse/synaptics.h
+++ new/drivers/input/mouse/synaptics.h
@@ -177,6 +177,8 @@
 	u32 x_min, y_min;	/* Min coordinates (from FW) */
 };
 
+struct synaptics_led;
+
 struct synaptics_data {
 	struct synaptics_device_info info;
 
@@ -201,6 +203,7 @@
 	bool					press;
 	bool					report_press;
 	bool					is_forcepad;
+	struct synaptics_led *led;
 };
 
 void synaptics_module_init(void);