diff options
author | Joonyoung Shim <jy0922.shim@samsung.com> | 2012-07-13 03:25:45 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-07-18 02:16:17 -0400 |
commit | 07b8481d4aff73d6f451f25e74ea10240ff5131e (patch) | |
tree | 3b69fbd4bc82b4741e14f484e76a796ced7f2429 | |
parent | 69690bec400e4c6cc89ef37376da1e633e14ad0f (diff) |
Input: add MELFAS mms114 touchscreen driver
This is a initial driver for new touchscreen chip mms114 of MELFAS.
It uses I2C interface and supports 10 multi touch.
Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/mms114.c | 544 | ||||
-rw-r--r-- | include/linux/i2c/mms114.h | 24 |
4 files changed, 581 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 98d263504eea..73bd2f6b82ec 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
@@ -369,6 +369,18 @@ config TOUCHSCREEN_MCS5000 | |||
369 | To compile this driver as a module, choose M here: the | 369 | To compile this driver as a module, choose M here: the |
370 | module will be called mcs5000_ts. | 370 | module will be called mcs5000_ts. |
371 | 371 | ||
372 | config TOUCHSCREEN_MMS114 | ||
373 | tristate "MELFAS MMS114 touchscreen" | ||
374 | depends on I2C | ||
375 | help | ||
376 | Say Y here if you have the MELFAS MMS114 touchscreen controller | ||
377 | chip in your system. | ||
378 | |||
379 | If unsure, say N. | ||
380 | |||
381 | To compile this driver as a module, choose M here: the | ||
382 | module will be called mms114. | ||
383 | |||
372 | config TOUCHSCREEN_MTOUCH | 384 | config TOUCHSCREEN_MTOUCH |
373 | tristate "MicroTouch serial touchscreens" | 385 | tristate "MicroTouch serial touchscreens" |
374 | select SERIO | 386 | select SERIO |
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index eb8bfe1c1a46..5920c60f999d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile | |||
@@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o | |||
38 | obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o | 38 | obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o |
39 | obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o | 39 | obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o |
40 | obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o | 40 | obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o |
41 | obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o | ||
41 | obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o | 42 | obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o |
42 | obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o | 43 | obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o |
43 | obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o | 44 | obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o |
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c new file mode 100644 index 000000000000..49c44bbf548d --- /dev/null +++ b/drivers/input/touchscreen/mms114.c | |||
@@ -0,0 +1,544 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
3 | * Author: Joonyoung Shim <jy0922.shim@samsung.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/i2c.h> | ||
14 | #include <linux/i2c/mms114.h> | ||
15 | #include <linux/input/mt.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/regulator/consumer.h> | ||
18 | #include <linux/slab.h> | ||
19 | |||
20 | /* Write only registers */ | ||
21 | #define MMS114_MODE_CONTROL 0x01 | ||
22 | #define MMS114_OPERATION_MODE_MASK 0xE | ||
23 | #define MMS114_ACTIVE (1 << 1) | ||
24 | |||
25 | #define MMS114_XY_RESOLUTION_H 0x02 | ||
26 | #define MMS114_X_RESOLUTION 0x03 | ||
27 | #define MMS114_Y_RESOLUTION 0x04 | ||
28 | #define MMS114_CONTACT_THRESHOLD 0x05 | ||
29 | #define MMS114_MOVING_THRESHOLD 0x06 | ||
30 | |||
31 | /* Read only registers */ | ||
32 | #define MMS114_PACKET_SIZE 0x0F | ||
33 | #define MMS114_INFOMATION 0x10 | ||
34 | #define MMS114_TSP_REV 0xF0 | ||
35 | |||
36 | /* Minimum delay time is 50us between stop and start signal of i2c */ | ||
37 | #define MMS114_I2C_DELAY 50 | ||
38 | |||
39 | /* 200ms needs after power on */ | ||
40 | #define MMS114_POWERON_DELAY 200 | ||
41 | |||
42 | /* Touchscreen absolute values */ | ||
43 | #define MMS114_MAX_AREA 0xff | ||
44 | |||
45 | #define MMS114_MAX_TOUCH 10 | ||
46 | #define MMS114_PACKET_NUM 8 | ||
47 | |||
48 | /* Touch type */ | ||
49 | #define MMS114_TYPE_NONE 0 | ||
50 | #define MMS114_TYPE_TOUCHSCREEN 1 | ||
51 | #define MMS114_TYPE_TOUCHKEY 2 | ||
52 | |||
53 | struct mms114_data { | ||
54 | struct i2c_client *client; | ||
55 | struct input_dev *input_dev; | ||
56 | struct regulator *core_reg; | ||
57 | struct regulator *io_reg; | ||
58 | const struct mms114_platform_data *pdata; | ||
59 | |||
60 | /* Use cache data for mode control register(write only) */ | ||
61 | u8 cache_mode_control; | ||
62 | }; | ||
63 | |||
64 | struct mms114_touch { | ||
65 | u8 id:4, reserved_bit4:1, type:2, pressed:1; | ||
66 | u8 x_hi:4, y_hi:4; | ||
67 | u8 x_lo; | ||
68 | u8 y_lo; | ||
69 | u8 width; | ||
70 | u8 strength; | ||
71 | u8 reserved[2]; | ||
72 | } __packed; | ||
73 | |||
74 | static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, | ||
75 | unsigned int len, u8 *val) | ||
76 | { | ||
77 | struct i2c_client *client = data->client; | ||
78 | struct i2c_msg xfer[2]; | ||
79 | u8 buf = reg & 0xff; | ||
80 | int error; | ||
81 | |||
82 | if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL) | ||
83 | BUG(); | ||
84 | |||
85 | /* Write register: use repeated start */ | ||
86 | xfer[0].addr = client->addr; | ||
87 | xfer[0].flags = I2C_M_TEN | I2C_M_NOSTART; | ||
88 | xfer[0].len = 1; | ||
89 | xfer[0].buf = &buf; | ||
90 | |||
91 | /* Read data */ | ||
92 | xfer[1].addr = client->addr; | ||
93 | xfer[1].flags = I2C_M_RD; | ||
94 | xfer[1].len = len; | ||
95 | xfer[1].buf = val; | ||
96 | |||
97 | error = i2c_transfer(client->adapter, xfer, 2); | ||
98 | if (error != 2) { | ||
99 | dev_err(&client->dev, | ||
100 | "%s: i2c transfer failed (%d)\n", __func__, error); | ||
101 | return error < 0 ? error : -EIO; | ||
102 | } | ||
103 | udelay(MMS114_I2C_DELAY); | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int mms114_read_reg(struct mms114_data *data, unsigned int reg) | ||
109 | { | ||
110 | u8 val; | ||
111 | int error; | ||
112 | |||
113 | if (reg == MMS114_MODE_CONTROL) | ||
114 | return data->cache_mode_control; | ||
115 | |||
116 | error = __mms114_read_reg(data, reg, 1, &val); | ||
117 | return error < 0 ? error : val; | ||
118 | } | ||
119 | |||
120 | static int mms114_write_reg(struct mms114_data *data, unsigned int reg, | ||
121 | unsigned int val) | ||
122 | { | ||
123 | struct i2c_client *client = data->client; | ||
124 | u8 buf[2]; | ||
125 | int error; | ||
126 | |||
127 | buf[0] = reg & 0xff; | ||
128 | buf[1] = val & 0xff; | ||
129 | |||
130 | error = i2c_master_send(client, buf, 2); | ||
131 | if (error != 2) { | ||
132 | dev_err(&client->dev, | ||
133 | "%s: i2c send failed (%d)\n", __func__, error); | ||
134 | return error < 0 ? error : -EIO; | ||
135 | } | ||
136 | udelay(MMS114_I2C_DELAY); | ||
137 | |||
138 | if (reg == MMS114_MODE_CONTROL) | ||
139 | data->cache_mode_control = val; | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch) | ||
145 | { | ||
146 | const struct mms114_platform_data *pdata = data->pdata; | ||
147 | struct i2c_client *client = data->client; | ||
148 | struct input_dev *input_dev = data->input_dev; | ||
149 | unsigned int id; | ||
150 | unsigned int x; | ||
151 | unsigned int y; | ||
152 | |||
153 | if (touch->id > MMS114_MAX_TOUCH) { | ||
154 | dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); | ||
155 | return; | ||
156 | } | ||
157 | |||
158 | if (touch->type != MMS114_TYPE_TOUCHSCREEN) { | ||
159 | dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type); | ||
160 | return; | ||
161 | } | ||
162 | |||
163 | id = touch->id - 1; | ||
164 | x = touch->x_lo | touch->x_hi << 8; | ||
165 | y = touch->y_lo | touch->y_hi << 8; | ||
166 | if (x > pdata->x_size || y > pdata->y_size) { | ||
167 | dev_dbg(&client->dev, | ||
168 | "Wrong touch coordinates (%d, %d)\n", x, y); | ||
169 | return; | ||
170 | } | ||
171 | |||
172 | if (pdata->x_invert) | ||
173 | x = pdata->x_size - x; | ||
174 | if (pdata->y_invert) | ||
175 | y = pdata->y_size - y; | ||
176 | |||
177 | dev_dbg(&client->dev, | ||
178 | "id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n", | ||
179 | id, touch->type, touch->pressed, | ||
180 | x, y, touch->width, touch->strength); | ||
181 | |||
182 | input_mt_slot(input_dev, id); | ||
183 | input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed); | ||
184 | |||
185 | if (touch->pressed) { | ||
186 | input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width); | ||
187 | input_report_abs(input_dev, ABS_MT_POSITION_X, x); | ||
188 | input_report_abs(input_dev, ABS_MT_POSITION_Y, y); | ||
189 | input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static irqreturn_t mms114_interrupt(int irq, void *dev_id) | ||
194 | { | ||
195 | struct mms114_data *data = dev_id; | ||
196 | struct input_dev *input_dev = data->input_dev; | ||
197 | struct mms114_touch touch[MMS114_MAX_TOUCH]; | ||
198 | int packet_size; | ||
199 | int touch_size; | ||
200 | int index; | ||
201 | int error; | ||
202 | |||
203 | mutex_lock(&input_dev->mutex); | ||
204 | if (!input_dev->users) { | ||
205 | mutex_unlock(&input_dev->mutex); | ||
206 | goto out; | ||
207 | } | ||
208 | mutex_unlock(&input_dev->mutex); | ||
209 | |||
210 | packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); | ||
211 | if (packet_size <= 0) | ||
212 | goto out; | ||
213 | |||
214 | touch_size = packet_size / MMS114_PACKET_NUM; | ||
215 | |||
216 | error = __mms114_read_reg(data, MMS114_INFOMATION, packet_size, | ||
217 | (u8 *)touch); | ||
218 | if (error < 0) | ||
219 | goto out; | ||
220 | |||
221 | for (index = 0; index < touch_size; index++) | ||
222 | mms114_process_mt(data, touch + index); | ||
223 | |||
224 | input_mt_report_pointer_emulation(data->input_dev, true); | ||
225 | input_sync(data->input_dev); | ||
226 | |||
227 | out: | ||
228 | return IRQ_HANDLED; | ||
229 | } | ||
230 | |||
231 | static int mms114_set_active(struct mms114_data *data, bool active) | ||
232 | { | ||
233 | int val; | ||
234 | |||
235 | val = mms114_read_reg(data, MMS114_MODE_CONTROL); | ||
236 | if (val < 0) | ||
237 | return val; | ||
238 | |||
239 | val &= ~MMS114_OPERATION_MODE_MASK; | ||
240 | |||
241 | /* If active is false, sleep mode */ | ||
242 | if (active) | ||
243 | val |= MMS114_ACTIVE; | ||
244 | |||
245 | return mms114_write_reg(data, MMS114_MODE_CONTROL, val); | ||
246 | } | ||
247 | |||
248 | static int mms114_get_version(struct mms114_data *data) | ||
249 | { | ||
250 | struct device *dev = &data->client->dev; | ||
251 | u8 buf[6]; | ||
252 | int error; | ||
253 | |||
254 | error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); | ||
255 | if (error < 0) | ||
256 | return error; | ||
257 | |||
258 | dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", | ||
259 | buf[0], buf[1], buf[3]); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int mms114_setup_regs(struct mms114_data *data) | ||
265 | { | ||
266 | const struct mms114_platform_data *pdata = data->pdata; | ||
267 | int val; | ||
268 | int error; | ||
269 | |||
270 | error = mms114_get_version(data); | ||
271 | if (error < 0) | ||
272 | return error; | ||
273 | |||
274 | error = mms114_set_active(data, true); | ||
275 | if (error < 0) | ||
276 | return error; | ||
277 | |||
278 | val = (pdata->x_size >> 8) & 0xf; | ||
279 | val |= ((pdata->y_size >> 8) & 0xf) << 4; | ||
280 | error = mms114_write_reg(data, MMS114_XY_RESOLUTION_H, val); | ||
281 | if (error < 0) | ||
282 | return error; | ||
283 | |||
284 | val = pdata->x_size & 0xff; | ||
285 | error = mms114_write_reg(data, MMS114_X_RESOLUTION, val); | ||
286 | if (error < 0) | ||
287 | return error; | ||
288 | |||
289 | val = pdata->y_size & 0xff; | ||
290 | error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val); | ||
291 | if (error < 0) | ||
292 | return error; | ||
293 | |||
294 | if (pdata->contact_threshold) { | ||
295 | error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD, | ||
296 | pdata->contact_threshold); | ||
297 | if (error < 0) | ||
298 | return error; | ||
299 | } | ||
300 | |||
301 | if (pdata->moving_threshold) { | ||
302 | error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD, | ||
303 | pdata->moving_threshold); | ||
304 | if (error < 0) | ||
305 | return error; | ||
306 | } | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int mms114_start(struct mms114_data *data) | ||
312 | { | ||
313 | struct i2c_client *client = data->client; | ||
314 | int error; | ||
315 | |||
316 | if (data->core_reg) | ||
317 | regulator_enable(data->core_reg); | ||
318 | if (data->io_reg) | ||
319 | regulator_enable(data->io_reg); | ||
320 | mdelay(MMS114_POWERON_DELAY); | ||
321 | |||
322 | error = mms114_setup_regs(data); | ||
323 | if (error < 0) | ||
324 | return error; | ||
325 | |||
326 | if (data->pdata->cfg_pin) | ||
327 | data->pdata->cfg_pin(true); | ||
328 | |||
329 | enable_irq(client->irq); | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static void mms114_stop(struct mms114_data *data) | ||
335 | { | ||
336 | struct i2c_client *client = data->client; | ||
337 | |||
338 | disable_irq(client->irq); | ||
339 | |||
340 | if (data->pdata->cfg_pin) | ||
341 | data->pdata->cfg_pin(false); | ||
342 | |||
343 | if (data->io_reg) | ||
344 | regulator_disable(data->io_reg); | ||
345 | if (data->core_reg) | ||
346 | regulator_disable(data->core_reg); | ||
347 | } | ||
348 | |||
349 | static int mms114_input_open(struct input_dev *dev) | ||
350 | { | ||
351 | struct mms114_data *data = input_get_drvdata(dev); | ||
352 | |||
353 | return mms114_start(data); | ||
354 | } | ||
355 | |||
356 | static void mms114_input_close(struct input_dev *dev) | ||
357 | { | ||
358 | struct mms114_data *data = input_get_drvdata(dev); | ||
359 | |||
360 | mms114_stop(data); | ||
361 | } | ||
362 | |||
363 | static int __devinit mms114_probe(struct i2c_client *client, | ||
364 | const struct i2c_device_id *id) | ||
365 | { | ||
366 | struct mms114_data *data; | ||
367 | struct input_dev *input_dev; | ||
368 | int error; | ||
369 | |||
370 | if (!client->dev.platform_data) { | ||
371 | dev_err(&client->dev, "Need platform data\n"); | ||
372 | return -EINVAL; | ||
373 | } | ||
374 | |||
375 | if (!i2c_check_functionality(client->adapter, | ||
376 | I2C_FUNC_PROTOCOL_MANGLING)) { | ||
377 | dev_err(&client->dev, | ||
378 | "Need i2c bus that supports protocol mangling\n"); | ||
379 | return -ENODEV; | ||
380 | } | ||
381 | |||
382 | data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL); | ||
383 | input_dev = input_allocate_device(); | ||
384 | if (!data || !input_dev) { | ||
385 | dev_err(&client->dev, "Failed to allocate memory\n"); | ||
386 | error = -ENOMEM; | ||
387 | goto err_free_mem; | ||
388 | } | ||
389 | |||
390 | data->client = client; | ||
391 | data->input_dev = input_dev; | ||
392 | data->pdata = client->dev.platform_data; | ||
393 | |||
394 | input_dev->name = "MELPAS MMS114 Touchscreen"; | ||
395 | input_dev->id.bustype = BUS_I2C; | ||
396 | input_dev->dev.parent = &client->dev; | ||
397 | input_dev->open = mms114_input_open; | ||
398 | input_dev->close = mms114_input_close; | ||
399 | |||
400 | __set_bit(EV_ABS, input_dev->evbit); | ||
401 | __set_bit(EV_KEY, input_dev->evbit); | ||
402 | __set_bit(BTN_TOUCH, input_dev->keybit); | ||
403 | input_set_abs_params(input_dev, ABS_X, 0, data->pdata->x_size, 0, 0); | ||
404 | input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0); | ||
405 | |||
406 | /* For multi touch */ | ||
407 | input_mt_init_slots(input_dev, MMS114_MAX_TOUCH); | ||
408 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | ||
409 | 0, MMS114_MAX_AREA, 0, 0); | ||
410 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, | ||
411 | 0, data->pdata->x_size, 0, 0); | ||
412 | input_set_abs_params(input_dev, ABS_MT_POSITION_Y, | ||
413 | 0, data->pdata->y_size, 0, 0); | ||
414 | input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); | ||
415 | |||
416 | input_set_drvdata(input_dev, data); | ||
417 | i2c_set_clientdata(client, data); | ||
418 | |||
419 | data->core_reg = regulator_get(&client->dev, "avdd"); | ||
420 | if (IS_ERR(data->core_reg)) { | ||
421 | error = PTR_ERR(data->core_reg); | ||
422 | dev_err(&client->dev, | ||
423 | "Unable to get the Core regulator (%d)\n", error); | ||
424 | goto err_free_mem; | ||
425 | } | ||
426 | |||
427 | data->io_reg = regulator_get(&client->dev, "vdd"); | ||
428 | if (IS_ERR(data->io_reg)) { | ||
429 | error = PTR_ERR(data->io_reg); | ||
430 | dev_err(&client->dev, | ||
431 | "Unable to get the IO regulator (%d)\n", error); | ||
432 | goto err_core_reg; | ||
433 | } | ||
434 | |||
435 | error = request_threaded_irq(client->irq, NULL, mms114_interrupt, | ||
436 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data); | ||
437 | if (error) { | ||
438 | dev_err(&client->dev, "Failed to register interrupt\n"); | ||
439 | goto err_io_reg; | ||
440 | } | ||
441 | disable_irq(client->irq); | ||
442 | |||
443 | error = input_register_device(data->input_dev); | ||
444 | if (error) | ||
445 | goto err_free_irq; | ||
446 | |||
447 | return 0; | ||
448 | |||
449 | err_free_irq: | ||
450 | free_irq(client->irq, data); | ||
451 | err_io_reg: | ||
452 | regulator_put(data->io_reg); | ||
453 | err_core_reg: | ||
454 | regulator_put(data->core_reg); | ||
455 | err_free_mem: | ||
456 | input_free_device(input_dev); | ||
457 | kfree(data); | ||
458 | return error; | ||
459 | } | ||
460 | |||
461 | static int __devexit mms114_remove(struct i2c_client *client) | ||
462 | { | ||
463 | struct mms114_data *data = i2c_get_clientdata(client); | ||
464 | |||
465 | free_irq(client->irq, data); | ||
466 | regulator_put(data->io_reg); | ||
467 | regulator_put(data->core_reg); | ||
468 | input_unregister_device(data->input_dev); | ||
469 | kfree(data); | ||
470 | |||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | #ifdef CONFIG_PM_SLEEP | ||
475 | static int mms114_suspend(struct device *dev) | ||
476 | { | ||
477 | struct i2c_client *client = to_i2c_client(dev); | ||
478 | struct mms114_data *data = i2c_get_clientdata(client); | ||
479 | struct input_dev *input_dev = data->input_dev; | ||
480 | int id; | ||
481 | |||
482 | /* Release all touch */ | ||
483 | for (id = 0; id < MMS114_MAX_TOUCH; id++) { | ||
484 | input_mt_slot(input_dev, id); | ||
485 | input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); | ||
486 | } | ||
487 | |||
488 | input_mt_report_pointer_emulation(input_dev, true); | ||
489 | input_sync(input_dev); | ||
490 | |||
491 | mutex_lock(&input_dev->mutex); | ||
492 | if (input_dev->users) | ||
493 | mms114_stop(data); | ||
494 | mutex_unlock(&input_dev->mutex); | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int mms114_resume(struct device *dev) | ||
500 | { | ||
501 | struct i2c_client *client = to_i2c_client(dev); | ||
502 | struct mms114_data *data = i2c_get_clientdata(client); | ||
503 | struct input_dev *input_dev = data->input_dev; | ||
504 | int error; | ||
505 | |||
506 | mutex_lock(&input_dev->mutex); | ||
507 | if (input_dev->users) { | ||
508 | error = mms114_start(data); | ||
509 | if (error < 0) { | ||
510 | mutex_unlock(&input_dev->mutex); | ||
511 | return error; | ||
512 | } | ||
513 | } | ||
514 | mutex_unlock(&input_dev->mutex); | ||
515 | |||
516 | return 0; | ||
517 | } | ||
518 | #endif | ||
519 | |||
520 | static SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); | ||
521 | |||
522 | static const struct i2c_device_id mms114_id[] = { | ||
523 | { "mms114", 0 }, | ||
524 | { } | ||
525 | }; | ||
526 | MODULE_DEVICE_TABLE(i2c, mms114_id); | ||
527 | |||
528 | static struct i2c_driver mms114_driver = { | ||
529 | .driver = { | ||
530 | .name = "mms114", | ||
531 | .owner = THIS_MODULE, | ||
532 | .pm = &mms114_pm_ops, | ||
533 | }, | ||
534 | .probe = mms114_probe, | ||
535 | .remove = __devexit_p(mms114_remove), | ||
536 | .id_table = mms114_id, | ||
537 | }; | ||
538 | |||
539 | module_i2c_driver(mms114_driver); | ||
540 | |||
541 | /* Module information */ | ||
542 | MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); | ||
543 | MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver"); | ||
544 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/i2c/mms114.h b/include/linux/i2c/mms114.h new file mode 100644 index 000000000000..5722ebfb2738 --- /dev/null +++ b/include/linux/i2c/mms114.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
3 | * Author: Joonyoung Shim <jy0922.shim@samsung.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundationr | ||
8 | */ | ||
9 | |||
10 | #ifndef __LINUX_MMS114_H | ||
11 | #define __LINUX_MMS114_H | ||
12 | |||
13 | struct mms114_platform_data { | ||
14 | unsigned int x_size; | ||
15 | unsigned int y_size; | ||
16 | unsigned int contact_threshold; | ||
17 | unsigned int moving_threshold; | ||
18 | bool x_invert; | ||
19 | bool y_invert; | ||
20 | |||
21 | void (*cfg_pin)(bool); | ||
22 | }; | ||
23 | |||
24 | #endif /* __LINUX_MMS114_H */ | ||