summaryrefslogtreecommitdiffstats
path: root/0003-NOT_FOR_UPSTREAM-staging-nvec-add-support-for-lid-an.patch
blob: 3bf54c5cb4a14fae0404c178410460244b994bc6 (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
220
221
From febeab9b47096c0dce5eaeaceba4a1b8950e563c Mon Sep 17 00:00:00 2001
From: Marc Dietrich <marvin24@gmx.de>
Date: Tue, 11 Mar 2014 13:00:38 +0100
Subject: [PATCH 3/6] NOT_FOR_UPSTREAM: staging: nvec: add support for lid and
 power button events

The support is hardcoded in the board specific driver for now until we
get proper DT support.

Signed-off-by: Marc Dietrich <marvin24@gmx.de>
---
 drivers/staging/nvec/nvec_paz00.c | 154 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 152 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/nvec/nvec_paz00.c b/drivers/staging/nvec/nvec_paz00.c
index a10385d..732e9ff 100644
--- a/drivers/staging/nvec/nvec_paz00.c
+++ b/drivers/staging/nvec/nvec_paz00.c
@@ -12,6 +12,7 @@
  */
 
 #include <linux/err.h>
+#include <linux/input.h>
 #include <linux/leds.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -19,6 +20,7 @@
 #include "nvec.h"
 
 #define NVEC_LED_MAX 8
+#define NVEC_SYSTEM_EVENT_VAR_LENGTH (0xC5 & 0x8F)
 
 enum nvec_oem0_subcmds {
 	EXEC_EC_CMD = 0x10,
@@ -28,12 +30,47 @@ enum nvec_oem0_ec_cmds {
 	SET_DEVICE_STATUS = 0x45,
 };
 
+enum nvec_sys_ec_cmds {
+	CONF_EV_REPORTING = 1,
+};
+
 struct nvec_paz00_struct {
 	struct nvec_chip *nvec;
 	struct led_classdev *led_dev;
+	struct notifier_block notifier;
+};
+
+struct nvec_paz00_event {
+	char name[32];
+	struct input_dev *dev;
+	int input_type;
+	int key_code;
+	unsigned long status_mask;
+};
+
+struct nvec_sys_event {
+	unsigned char command;
+	unsigned char length;
+	unsigned long payload;
 };
 
-struct nvec_paz00_struct nvec_paz00;
+static struct nvec_paz00_struct nvec_paz00;
+
+static struct nvec_paz00_event nvec_paz00_events[] = {
+	{
+		.name = "lid switch",
+		.input_type = EV_SW,
+		.key_code = SW_LID,
+		.status_mask = BIT(1),
+	}, {
+		.name = "power key",
+		.input_type = EV_KEY,
+		.key_code = KEY_POWER,
+		.status_mask = BIT(7),
+	}, {
+	/* sentinel */
+	},
+};
 
 static void nvec_led_brightness_set(struct led_classdev *led_cdev,
 				    enum led_brightness value)
@@ -61,20 +98,133 @@ static int paz00_init_leds(struct device *dev)
 	return led_classdev_register(dev, nvec_paz00.led_dev);
 }
 
+static int nvec_event_notifier(struct notifier_block *nb,
+				unsigned long event_type, void *data)
+{
+	struct nvec_sys_event *event = data;
+	struct nvec_paz00_event *e = nvec_paz00_events;
+
+	if (event_type != NVEC_SYSTEM_EVENT_VAR_LENGTH ||
+		(event->command & (NVEC_VAR_SIZE << 5)) == 0 ||
+		 event->length != 4)
+		return NOTIFY_DONE;
+
+	print_hex_dump(KERN_DEBUG, "payload: ", DUMP_PREFIX_NONE, 16, 1,
+		&event->command, event->length + 2, false);
+
+	for (; e->name[0]; e++) {
+		if (e->status_mask & event->payload) {
+			if (test_bit(EV_KEY, e->dev->evbit)) {
+				input_report_key(e->dev, e->key_code, 1);
+				input_sync(e->dev);
+				input_report_key(e->dev, e->key_code, 0);
+			} else if (test_bit(EV_SW, e->dev->evbit)) {
+				input_report_switch(e->dev, e->key_code, 1);
+			} else {
+				pr_err("unknown event type\n");
+				return NOTIFY_OK;
+			}
+		} else if (event->payload == 0)
+			input_report_switch(e->dev, e->key_code, 0);
+
+		input_sync(e->dev);
+	}
+
+	return NOTIFY_STOP;
+}
+
+static void nvec_configure_event(struct nvec_chip *nvec, long mask, int state)
+{
+	char buf[7] = { NVEC_SYS, CONF_EV_REPORTING, state };
+
+	buf[3] = (mask >> 16) & 0xff;
+	buf[4] = (mask >> 24) & 0xff;
+	buf[5] = (mask >> 0) & 0xff;
+	buf[6] = (mask >> 8) & 0xff;
+
+	nvec_write_async(nvec, buf, 7);
+};
+
+static int paz00_init_events(struct device *dev)
+{
+	struct nvec_paz00_event *event = nvec_paz00_events;
+	int err;
+
+	for (; event->name[0]; event++) {
+
+		event->dev = input_allocate_device();
+		if (event->dev == NULL) {
+			dev_err(dev, "failed to allocate input device\n");
+			break;
+		}
+
+		event->dev->name = event->name;
+		event->dev->phys = "NVEC";
+		event->dev->evbit[0] = BIT_MASK(event->input_type);
+
+		if (event->input_type == EV_KEY)
+			set_bit(event->key_code, event->dev->keybit);
+		else if (event->input_type == EV_SW)
+			set_bit(event->key_code, event->dev->swbit);
+		else {
+			dev_err(dev, "unsupported event type %d\n",
+				event->input_type);
+			input_free_device(event->dev);
+			break;
+		}
+
+		err = input_register_device(event->dev);
+		if (err) {
+			dev_err(dev, "failed to register input device (%d)\n",
+				err);
+			input_free_device(event->dev);
+			break;
+		}
+
+		nvec_configure_event(nvec_paz00.nvec, event->status_mask, 1);
+	}
+
+	nvec_paz00.notifier.notifier_call = nvec_event_notifier;
+	nvec_register_notifier(nvec_paz00.nvec, &nvec_paz00.notifier, 0);
+
+	return err;
+}
+
 static int nvec_paz00_probe(struct platform_device *pdev)
 {
 	struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
+	int ret;
 
 	platform_set_drvdata(pdev, &nvec_paz00);
 	nvec_paz00.nvec = nvec;
 
-	return paz00_init_leds(&pdev->dev);
+	ret = paz00_init_leds(&pdev->dev);
+	if (!ret)
+		dev_err(&pdev->dev, "error registrating led device %d\n",
+			ret);
+
+	ret = paz00_init_events(&pdev->dev);
+	if (!ret)
+		dev_err(&pdev->dev, "error registrating input device %d\n",
+			ret);
+
+	return ret;
 }
 
 static int nvec_paz00_remove(struct platform_device *pdev)
 {
+	struct nvec_paz00_event *event = nvec_paz00_events;
+
 	led_classdev_unregister(nvec_paz00.led_dev);
 
+	nvec_unregister_notifier(nvec_paz00.nvec, &nvec_paz00.notifier);
+
+	for (; event->name[0]; event++) {
+		nvec_configure_event(nvec_paz00.nvec, event->status_mask, 0);
+		input_unregister_device(event->dev);
+		input_free_device(event->dev);
+	}
+
 	return 0;
 }
 
-- 
1.8.3.1