blob: 2d9cacb044b7688f9a9fafcf81265fe13643c602 [file] [log] [blame]
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001/*
Iiro Valkonen7686b102011-02-02 23:21:58 -08002 * Atmel maXTouch Touchscreen driver
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07003 *
4 * Copyright (C) 2010 Samsung Electronics Co.Ltd
5 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
Jing Lin2f863172011-10-17 10:56:58 -07006 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07007 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/delay.h>
18#include <linux/firmware.h>
19#include <linux/i2c.h>
Dmitry Torokhov964de522011-02-02 23:21:58 -080020#include <linux/i2c/atmel_mxt_ts.h>
Amy Maloche2b59bab2011-10-13 16:08:16 -070021#include <linux/input.h>
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070022#include <linux/interrupt.h>
23#include <linux/slab.h>
Anirudh Ghayala498e4d2011-08-09 19:10:12 +053024#include <linux/regulator/consumer.h>
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070025
Anirudh Ghayal253ce122011-08-09 19:32:57 +053026#if defined(CONFIG_HAS_EARLYSUSPEND)
27#include <linux/earlysuspend.h>
28/* Early-suspend level */
29#define MXT_SUSPEND_LEVEL 1
30#endif
31
Iiro Valkonen4ac053c2011-09-08 11:10:52 -070032/* Family ID */
33#define MXT224_ID 0x80
34#define MXT1386_ID 0xA0
35
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070036/* Version */
Iiro Valkonen7686b102011-02-02 23:21:58 -080037#define MXT_VER_20 20
38#define MXT_VER_21 21
39#define MXT_VER_22 22
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070040
41/* Slave addresses */
Iiro Valkonen7686b102011-02-02 23:21:58 -080042#define MXT_APP_LOW 0x4a
43#define MXT_APP_HIGH 0x4b
44#define MXT_BOOT_LOW 0x24
45#define MXT_BOOT_HIGH 0x25
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070046
47/* Firmware */
Iiro Valkonen7686b102011-02-02 23:21:58 -080048#define MXT_FW_NAME "maxtouch.fw"
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070049
50/* Registers */
Iiro Valkonen7686b102011-02-02 23:21:58 -080051#define MXT_FAMILY_ID 0x00
52#define MXT_VARIANT_ID 0x01
53#define MXT_VERSION 0x02
54#define MXT_BUILD 0x03
55#define MXT_MATRIX_X_SIZE 0x04
56#define MXT_MATRIX_Y_SIZE 0x05
57#define MXT_OBJECT_NUM 0x06
58#define MXT_OBJECT_START 0x07
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070059
Iiro Valkonen7686b102011-02-02 23:21:58 -080060#define MXT_OBJECT_SIZE 6
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070061
62/* Object types */
Iiro Valkonen7686b102011-02-02 23:21:58 -080063#define MXT_DEBUG_DIAGNOSTIC 37
64#define MXT_GEN_MESSAGE 5
65#define MXT_GEN_COMMAND 6
66#define MXT_GEN_POWER 7
67#define MXT_GEN_ACQUIRE 8
68#define MXT_TOUCH_MULTI 9
69#define MXT_TOUCH_KEYARRAY 15
70#define MXT_TOUCH_PROXIMITY 23
71#define MXT_PROCI_GRIPFACE 20
72#define MXT_PROCG_NOISE 22
73#define MXT_PROCI_ONETOUCH 24
74#define MXT_PROCI_TWOTOUCH 27
Joonyoung Shim4c75de32011-03-14 21:41:40 -070075#define MXT_PROCI_GRIP 40
76#define MXT_PROCI_PALM 41
Joonyoung Shim979a72d2011-03-14 21:41:34 -070077#define MXT_SPT_COMMSCONFIG 18
Iiro Valkonen7686b102011-02-02 23:21:58 -080078#define MXT_SPT_GPIOPWM 19
79#define MXT_SPT_SELFTEST 25
80#define MXT_SPT_CTECONFIG 28
Joonyoung Shim979a72d2011-03-14 21:41:34 -070081#define MXT_SPT_USERDATA 38
Joonyoung Shim4c75de32011-03-14 21:41:40 -070082#define MXT_SPT_DIGITIZER 43
83#define MXT_SPT_MESSAGECOUNT 44
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070084
Iiro Valkonen7686b102011-02-02 23:21:58 -080085/* MXT_GEN_COMMAND field */
86#define MXT_COMMAND_RESET 0
87#define MXT_COMMAND_BACKUPNV 1
88#define MXT_COMMAND_CALIBRATE 2
89#define MXT_COMMAND_REPORTALL 3
90#define MXT_COMMAND_DIAGNOSTIC 5
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070091
Iiro Valkonen7686b102011-02-02 23:21:58 -080092/* MXT_GEN_POWER field */
93#define MXT_POWER_IDLEACQINT 0
94#define MXT_POWER_ACTVACQINT 1
95#define MXT_POWER_ACTV2IDLETO 2
Joonyoung Shim4cf51c32010-07-14 21:55:30 -070096
Iiro Valkonen7686b102011-02-02 23:21:58 -080097/* MXT_GEN_ACQUIRE field */
98#define MXT_ACQUIRE_CHRGTIME 0
99#define MXT_ACQUIRE_TCHDRIFT 2
100#define MXT_ACQUIRE_DRIFTST 3
101#define MXT_ACQUIRE_TCHAUTOCAL 4
102#define MXT_ACQUIRE_SYNC 5
103#define MXT_ACQUIRE_ATCHCALST 6
104#define MXT_ACQUIRE_ATCHCALSTHR 7
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700105
Iiro Valkonen7686b102011-02-02 23:21:58 -0800106/* MXT_TOUCH_MULTI field */
107#define MXT_TOUCH_CTRL 0
108#define MXT_TOUCH_XORIGIN 1
109#define MXT_TOUCH_YORIGIN 2
110#define MXT_TOUCH_XSIZE 3
111#define MXT_TOUCH_YSIZE 4
112#define MXT_TOUCH_BLEN 6
113#define MXT_TOUCH_TCHTHR 7
114#define MXT_TOUCH_TCHDI 8
115#define MXT_TOUCH_ORIENT 9
116#define MXT_TOUCH_MOVHYSTI 11
117#define MXT_TOUCH_MOVHYSTN 12
118#define MXT_TOUCH_NUMTOUCH 14
119#define MXT_TOUCH_MRGHYST 15
120#define MXT_TOUCH_MRGTHR 16
121#define MXT_TOUCH_AMPHYST 17
122#define MXT_TOUCH_XRANGE_LSB 18
123#define MXT_TOUCH_XRANGE_MSB 19
124#define MXT_TOUCH_YRANGE_LSB 20
125#define MXT_TOUCH_YRANGE_MSB 21
126#define MXT_TOUCH_XLOCLIP 22
127#define MXT_TOUCH_XHICLIP 23
128#define MXT_TOUCH_YLOCLIP 24
129#define MXT_TOUCH_YHICLIP 25
130#define MXT_TOUCH_XEDGECTRL 26
131#define MXT_TOUCH_XEDGEDIST 27
132#define MXT_TOUCH_YEDGECTRL 28
133#define MXT_TOUCH_YEDGEDIST 29
Joonyoung Shim979a72d2011-03-14 21:41:34 -0700134#define MXT_TOUCH_JUMPLIMIT 30
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700135
Iiro Valkonen7686b102011-02-02 23:21:58 -0800136/* MXT_PROCI_GRIPFACE field */
137#define MXT_GRIPFACE_CTRL 0
138#define MXT_GRIPFACE_XLOGRIP 1
139#define MXT_GRIPFACE_XHIGRIP 2
140#define MXT_GRIPFACE_YLOGRIP 3
141#define MXT_GRIPFACE_YHIGRIP 4
142#define MXT_GRIPFACE_MAXTCHS 5
143#define MXT_GRIPFACE_SZTHR1 7
144#define MXT_GRIPFACE_SZTHR2 8
145#define MXT_GRIPFACE_SHPTHR1 9
146#define MXT_GRIPFACE_SHPTHR2 10
147#define MXT_GRIPFACE_SUPEXTTO 11
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700148
Iiro Valkonen7686b102011-02-02 23:21:58 -0800149/* MXT_PROCI_NOISE field */
150#define MXT_NOISE_CTRL 0
151#define MXT_NOISE_OUTFLEN 1
152#define MXT_NOISE_GCAFUL_LSB 3
153#define MXT_NOISE_GCAFUL_MSB 4
154#define MXT_NOISE_GCAFLL_LSB 5
155#define MXT_NOISE_GCAFLL_MSB 6
156#define MXT_NOISE_ACTVGCAFVALID 7
157#define MXT_NOISE_NOISETHR 8
158#define MXT_NOISE_FREQHOPSCALE 10
159#define MXT_NOISE_FREQ0 11
160#define MXT_NOISE_FREQ1 12
161#define MXT_NOISE_FREQ2 13
162#define MXT_NOISE_FREQ3 14
163#define MXT_NOISE_FREQ4 15
164#define MXT_NOISE_IDLEGCAFVALID 16
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700165
Iiro Valkonen7686b102011-02-02 23:21:58 -0800166/* MXT_SPT_COMMSCONFIG */
167#define MXT_COMMS_CTRL 0
168#define MXT_COMMS_CMD 1
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700169
Iiro Valkonen7686b102011-02-02 23:21:58 -0800170/* MXT_SPT_CTECONFIG field */
171#define MXT_CTE_CTRL 0
172#define MXT_CTE_CMD 1
173#define MXT_CTE_MODE 2
174#define MXT_CTE_IDLEGCAFDEPTH 3
175#define MXT_CTE_ACTVGCAFDEPTH 4
Joonyoung Shim979a72d2011-03-14 21:41:34 -0700176#define MXT_CTE_VOLTAGE 5
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700177
Iiro Valkonen7686b102011-02-02 23:21:58 -0800178#define MXT_VOLTAGE_DEFAULT 2700000
179#define MXT_VOLTAGE_STEP 10000
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700180
Amy Maloche21115eb2011-11-02 09:04:37 -0700181/* Analog voltage @2.7 V */
Anirudh Ghayala498e4d2011-08-09 19:10:12 +0530182#define MXT_VTG_MIN_UV 2700000
183#define MXT_VTG_MAX_UV 3300000
184#define MXT_ACTIVE_LOAD_UA 15000
Jing Linbace50b2011-10-18 22:55:47 -0700185#define MXT_LPM_LOAD_UA 10
Amy Maloche21115eb2011-11-02 09:04:37 -0700186/* Digital voltage @1.8 V */
187#define MXT_VTG_DIG_MIN_UV 1800000
188#define MXT_VTG_DIG_MAX_UV 1800000
189#define MXT_ACTIVE_LOAD_DIG_UA 10000
190#define MXT_LPM_LOAD_DIG_UA 10
Anirudh Ghayala498e4d2011-08-09 19:10:12 +0530191
192#define MXT_I2C_VTG_MIN_UV 1800000
193#define MXT_I2C_VTG_MAX_UV 1800000
194#define MXT_I2C_LOAD_UA 10000
Jing Linbace50b2011-10-18 22:55:47 -0700195#define MXT_I2C_LPM_LOAD_UA 10
Anirudh Ghayala498e4d2011-08-09 19:10:12 +0530196
Iiro Valkonen7686b102011-02-02 23:21:58 -0800197/* Define for MXT_GEN_COMMAND */
198#define MXT_BOOT_VALUE 0xa5
199#define MXT_BACKUP_VALUE 0x55
200#define MXT_BACKUP_TIME 25 /* msec */
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700201#define MXT224_RESET_TIME 65 /* msec */
Amy Maloche7e447432011-09-14 11:36:30 -0700202#define MXT1386_RESET_TIME 250 /* msec */
203#define MXT_RESET_TIME 250 /* msec */
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700204#define MXT_RESET_NOCHGREAD 400 /* msec */
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700205
Iiro Valkonen7686b102011-02-02 23:21:58 -0800206#define MXT_FWRESET_TIME 175 /* msec */
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700207
Jing Lin36aee812011-10-17 17:17:28 -0700208#define MXT_WAKE_TIME 25
209
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700210/* Command to unlock bootloader */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800211#define MXT_UNLOCK_CMD_MSB 0xaa
212#define MXT_UNLOCK_CMD_LSB 0xdc
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700213
214/* Bootloader mode status */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800215#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
216#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
217#define MXT_FRAME_CRC_CHECK 0x02
218#define MXT_FRAME_CRC_FAIL 0x03
219#define MXT_FRAME_CRC_PASS 0x04
220#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
221#define MXT_BOOT_STATUS_MASK 0x3f
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700222
223/* Touch status */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800224#define MXT_SUPPRESS (1 << 1)
225#define MXT_AMP (1 << 2)
226#define MXT_VECTOR (1 << 3)
227#define MXT_MOVE (1 << 4)
228#define MXT_RELEASE (1 << 5)
229#define MXT_PRESS (1 << 6)
230#define MXT_DETECT (1 << 7)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700231
Joonyoung Shim910d8052011-04-12 23:14:38 -0700232/* Touch orient bits */
233#define MXT_XY_SWITCH (1 << 0)
234#define MXT_X_INVERT (1 << 1)
235#define MXT_Y_INVERT (1 << 2)
236
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700237/* Touchscreen absolute values */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800238#define MXT_MAX_AREA 0xff
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700239
Iiro Valkonen7686b102011-02-02 23:21:58 -0800240#define MXT_MAX_FINGER 10
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700241
Jing Lin36aee812011-10-17 17:17:28 -0700242#define T7_DATA_SIZE 3
243#define MXT_MAX_RW_TRIES 3
244#define MXT_BLOCK_SIZE 256
Mohan Pallakaab51f2b2011-09-29 18:17:35 +0530245
Iiro Valkonen7686b102011-02-02 23:21:58 -0800246struct mxt_info {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700247 u8 family_id;
248 u8 variant_id;
249 u8 version;
250 u8 build;
251 u8 matrix_xsize;
252 u8 matrix_ysize;
253 u8 object_num;
254};
255
Iiro Valkonen7686b102011-02-02 23:21:58 -0800256struct mxt_object {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700257 u8 type;
258 u16 start_address;
259 u8 size;
260 u8 instances;
261 u8 num_report_ids;
262
263 /* to map object and message */
264 u8 max_reportid;
265};
266
Iiro Valkonen7686b102011-02-02 23:21:58 -0800267struct mxt_message {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700268 u8 reportid;
269 u8 message[7];
270 u8 checksum;
271};
272
Iiro Valkonen7686b102011-02-02 23:21:58 -0800273struct mxt_finger {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700274 int status;
275 int x;
276 int y;
277 int area;
Yufeng Shene6eb36a2011-10-11 12:28:21 -0700278 int pressure;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700279};
280
281/* Each client has this additional data */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800282struct mxt_data {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700283 struct i2c_client *client;
284 struct input_dev *input_dev;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800285 const struct mxt_platform_data *pdata;
286 struct mxt_object *object_table;
287 struct mxt_info info;
288 struct mxt_finger finger[MXT_MAX_FINGER];
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700289 unsigned int irq;
Amy Maloche21115eb2011-11-02 09:04:37 -0700290 struct regulator *vcc_ana;
291 struct regulator *vcc_dig;
Anirudh Ghayala498e4d2011-08-09 19:10:12 +0530292 struct regulator *vcc_i2c;
Anirudh Ghayal253ce122011-08-09 19:32:57 +0530293#if defined(CONFIG_HAS_EARLYSUSPEND)
294 struct early_suspend early_suspend;
295#endif
Jing Lin36aee812011-10-17 17:17:28 -0700296
Amy Maloche52262212011-09-15 16:46:57 -0700297 u8 t7_data[T7_DATA_SIZE];
Jing Lin36aee812011-10-17 17:17:28 -0700298 u16 t7_start_addr;
Amy Maloche52262212011-09-15 16:46:57 -0700299 u8 t9_ctrl;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700300};
301
Iiro Valkonen7686b102011-02-02 23:21:58 -0800302static bool mxt_object_readable(unsigned int type)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700303{
304 switch (type) {
Iiro Valkonen7686b102011-02-02 23:21:58 -0800305 case MXT_GEN_MESSAGE:
306 case MXT_GEN_COMMAND:
307 case MXT_GEN_POWER:
308 case MXT_GEN_ACQUIRE:
309 case MXT_TOUCH_MULTI:
310 case MXT_TOUCH_KEYARRAY:
311 case MXT_TOUCH_PROXIMITY:
312 case MXT_PROCI_GRIPFACE:
313 case MXT_PROCG_NOISE:
314 case MXT_PROCI_ONETOUCH:
315 case MXT_PROCI_TWOTOUCH:
Joonyoung Shim4c75de32011-03-14 21:41:40 -0700316 case MXT_PROCI_GRIP:
317 case MXT_PROCI_PALM:
Iiro Valkonen7686b102011-02-02 23:21:58 -0800318 case MXT_SPT_COMMSCONFIG:
319 case MXT_SPT_GPIOPWM:
320 case MXT_SPT_SELFTEST:
321 case MXT_SPT_CTECONFIG:
322 case MXT_SPT_USERDATA:
Anirudh Ghayalba3bc7a2011-09-05 18:34:40 +0530323 case MXT_SPT_DIGITIZER:
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700324 return true;
325 default:
326 return false;
327 }
328}
329
Iiro Valkonen7686b102011-02-02 23:21:58 -0800330static bool mxt_object_writable(unsigned int type)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700331{
332 switch (type) {
Iiro Valkonen7686b102011-02-02 23:21:58 -0800333 case MXT_GEN_COMMAND:
334 case MXT_GEN_POWER:
335 case MXT_GEN_ACQUIRE:
336 case MXT_TOUCH_MULTI:
337 case MXT_TOUCH_KEYARRAY:
338 case MXT_TOUCH_PROXIMITY:
339 case MXT_PROCI_GRIPFACE:
340 case MXT_PROCG_NOISE:
341 case MXT_PROCI_ONETOUCH:
342 case MXT_PROCI_TWOTOUCH:
Joonyoung Shim4c75de32011-03-14 21:41:40 -0700343 case MXT_PROCI_GRIP:
344 case MXT_PROCI_PALM:
Iiro Valkonen7686b102011-02-02 23:21:58 -0800345 case MXT_SPT_GPIOPWM:
346 case MXT_SPT_SELFTEST:
347 case MXT_SPT_CTECONFIG:
Anirudh Ghayalf1071c02011-08-09 19:39:36 +0530348 case MXT_SPT_USERDATA:
Anirudh Ghayalba3bc7a2011-09-05 18:34:40 +0530349 case MXT_SPT_DIGITIZER:
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700350 return true;
351 default:
352 return false;
353 }
354}
355
Iiro Valkonen7686b102011-02-02 23:21:58 -0800356static void mxt_dump_message(struct device *dev,
357 struct mxt_message *message)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700358{
359 dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
360 dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
361 dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
362 dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
363 dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
364 dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
365 dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
366 dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
367 dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
368}
369
Iiro Valkonen7686b102011-02-02 23:21:58 -0800370static int mxt_check_bootloader(struct i2c_client *client,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700371 unsigned int state)
372{
373 u8 val;
374
375recheck:
376 if (i2c_master_recv(client, &val, 1) != 1) {
377 dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
378 return -EIO;
379 }
380
381 switch (state) {
Iiro Valkonen7686b102011-02-02 23:21:58 -0800382 case MXT_WAITING_BOOTLOAD_CMD:
383 case MXT_WAITING_FRAME_DATA:
384 val &= ~MXT_BOOT_STATUS_MASK;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700385 break;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800386 case MXT_FRAME_CRC_PASS:
387 if (val == MXT_FRAME_CRC_CHECK)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700388 goto recheck;
389 break;
390 default:
391 return -EINVAL;
392 }
393
394 if (val != state) {
395 dev_err(&client->dev, "Unvalid bootloader mode state\n");
396 return -EINVAL;
397 }
398
399 return 0;
400}
401
Iiro Valkonen7686b102011-02-02 23:21:58 -0800402static int mxt_unlock_bootloader(struct i2c_client *client)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700403{
404 u8 buf[2];
405
Iiro Valkonen7686b102011-02-02 23:21:58 -0800406 buf[0] = MXT_UNLOCK_CMD_LSB;
407 buf[1] = MXT_UNLOCK_CMD_MSB;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700408
409 if (i2c_master_send(client, buf, 2) != 2) {
410 dev_err(&client->dev, "%s: i2c send failed\n", __func__);
411 return -EIO;
412 }
413
414 return 0;
415}
416
Iiro Valkonen7686b102011-02-02 23:21:58 -0800417static int mxt_fw_write(struct i2c_client *client,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700418 const u8 *data, unsigned int frame_size)
419{
420 if (i2c_master_send(client, data, frame_size) != frame_size) {
421 dev_err(&client->dev, "%s: i2c send failed\n", __func__);
422 return -EIO;
423 }
424
425 return 0;
426}
427
Iiro Valkonen7686b102011-02-02 23:21:58 -0800428static int __mxt_read_reg(struct i2c_client *client,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700429 u16 reg, u16 len, void *val)
430{
431 struct i2c_msg xfer[2];
432 u8 buf[2];
Jing Lin36aee812011-10-17 17:17:28 -0700433 int i = 0;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700434
435 buf[0] = reg & 0xff;
436 buf[1] = (reg >> 8) & 0xff;
437
438 /* Write register */
439 xfer[0].addr = client->addr;
440 xfer[0].flags = 0;
441 xfer[0].len = 2;
442 xfer[0].buf = buf;
443
444 /* Read data */
445 xfer[1].addr = client->addr;
446 xfer[1].flags = I2C_M_RD;
447 xfer[1].len = len;
448 xfer[1].buf = val;
449
Jing Lin36aee812011-10-17 17:17:28 -0700450 do {
451 if (i2c_transfer(client->adapter, xfer, 2) == 2)
452 return 0;
453 msleep(MXT_WAKE_TIME);
454 } while (++i < MXT_MAX_RW_TRIES);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700455
Jing Lin36aee812011-10-17 17:17:28 -0700456 dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
457 return -EIO;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700458}
459
Iiro Valkonen7686b102011-02-02 23:21:58 -0800460static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700461{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800462 return __mxt_read_reg(client, reg, 1, val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700463}
464
Jing Lin36aee812011-10-17 17:17:28 -0700465static int __mxt_write_reg(struct i2c_client *client,
466 u16 addr, u16 length, u8 *value)
467{
468 u8 buf[MXT_BLOCK_SIZE + 2];
469 int i, tries = 0;
470
471 if (length > MXT_BLOCK_SIZE)
472 return -EINVAL;
473
474 buf[0] = addr & 0xff;
475 buf[1] = (addr >> 8) & 0xff;
476 for (i = 0; i < length; i++)
477 buf[i + 2] = *value++;
478
479 do {
480 if (i2c_master_send(client, buf, length + 2) == (length + 2))
481 return 0;
482 msleep(MXT_WAKE_TIME);
483 } while (++tries < MXT_MAX_RW_TRIES);
484
485 dev_err(&client->dev, "%s: i2c send failed\n", __func__);
486 return -EIO;
487}
488
Iiro Valkonen7686b102011-02-02 23:21:58 -0800489static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700490{
Jing Lin36aee812011-10-17 17:17:28 -0700491 return __mxt_write_reg(client, reg, 1, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700492}
493
Iiro Valkonen7686b102011-02-02 23:21:58 -0800494static int mxt_read_object_table(struct i2c_client *client,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700495 u16 reg, u8 *object_buf)
496{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800497 return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700498 object_buf);
499}
500
Iiro Valkonen7686b102011-02-02 23:21:58 -0800501static struct mxt_object *
502mxt_get_object(struct mxt_data *data, u8 type)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700503{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800504 struct mxt_object *object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700505 int i;
506
507 for (i = 0; i < data->info.object_num; i++) {
508 object = data->object_table + i;
509 if (object->type == type)
510 return object;
511 }
512
513 dev_err(&data->client->dev, "Invalid object type\n");
514 return NULL;
515}
516
Iiro Valkonen7686b102011-02-02 23:21:58 -0800517static int mxt_read_message(struct mxt_data *data,
518 struct mxt_message *message)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700519{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800520 struct mxt_object *object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700521 u16 reg;
522
Iiro Valkonen7686b102011-02-02 23:21:58 -0800523 object = mxt_get_object(data, MXT_GEN_MESSAGE);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700524 if (!object)
525 return -EINVAL;
526
527 reg = object->start_address;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800528 return __mxt_read_reg(data->client, reg,
529 sizeof(struct mxt_message), message);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700530}
531
Iiro Valkonen7686b102011-02-02 23:21:58 -0800532static int mxt_read_object(struct mxt_data *data,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700533 u8 type, u8 offset, u8 *val)
534{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800535 struct mxt_object *object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700536 u16 reg;
537
Iiro Valkonen7686b102011-02-02 23:21:58 -0800538 object = mxt_get_object(data, type);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700539 if (!object)
540 return -EINVAL;
541
542 reg = object->start_address;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800543 return __mxt_read_reg(data->client, reg + offset, 1, val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700544}
545
Iiro Valkonen7686b102011-02-02 23:21:58 -0800546static int mxt_write_object(struct mxt_data *data,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700547 u8 type, u8 offset, u8 val)
548{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800549 struct mxt_object *object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700550 u16 reg;
551
Iiro Valkonen7686b102011-02-02 23:21:58 -0800552 object = mxt_get_object(data, type);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700553 if (!object)
554 return -EINVAL;
555
556 reg = object->start_address;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800557 return mxt_write_reg(data->client, reg + offset, val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700558}
559
Iiro Valkonen7686b102011-02-02 23:21:58 -0800560static void mxt_input_report(struct mxt_data *data, int single_id)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700561{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800562 struct mxt_finger *finger = data->finger;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700563 struct input_dev *input_dev = data->input_dev;
564 int status = finger[single_id].status;
565 int finger_num = 0;
566 int id;
567
Iiro Valkonen7686b102011-02-02 23:21:58 -0800568 for (id = 0; id < MXT_MAX_FINGER; id++) {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700569 if (!finger[id].status)
570 continue;
571
Amy Maloche2b59bab2011-10-13 16:08:16 -0700572 input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
573 finger[id].status != MXT_RELEASE ?
574 finger[id].area : 0);
575 input_report_abs(input_dev, ABS_MT_POSITION_X,
576 finger[id].x);
577 input_report_abs(input_dev, ABS_MT_POSITION_Y,
578 finger[id].y);
Yufeng Shene6eb36a2011-10-11 12:28:21 -0700579 input_report_abs(input_dev, ABS_MT_PRESSURE,
580 finger[id].pressure);
Amy Maloche2b59bab2011-10-13 16:08:16 -0700581 input_mt_sync(input_dev);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700582
Amy Maloche2b59bab2011-10-13 16:08:16 -0700583 if (finger[id].status == MXT_RELEASE)
Joonyoung Shim8b86c1c2011-04-12 23:18:59 -0700584 finger[id].status = 0;
Amy Maloche2b59bab2011-10-13 16:08:16 -0700585 else
586 finger_num++;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700587 }
588
589 input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
590
Iiro Valkonen7686b102011-02-02 23:21:58 -0800591 if (status != MXT_RELEASE) {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700592 input_report_abs(input_dev, ABS_X, finger[single_id].x);
593 input_report_abs(input_dev, ABS_Y, finger[single_id].y);
Yufeng Shene6eb36a2011-10-11 12:28:21 -0700594 input_report_abs(input_dev,
595 ABS_PRESSURE, finger[single_id].pressure);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700596 }
597
598 input_sync(input_dev);
599}
600
Iiro Valkonen7686b102011-02-02 23:21:58 -0800601static void mxt_input_touchevent(struct mxt_data *data,
602 struct mxt_message *message, int id)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700603{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800604 struct mxt_finger *finger = data->finger;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700605 struct device *dev = &data->client->dev;
606 u8 status = message->message[0];
607 int x;
608 int y;
609 int area;
Yufeng Shene6eb36a2011-10-11 12:28:21 -0700610 int pressure;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700611
612 /* Check the touch is present on the screen */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800613 if (!(status & MXT_DETECT)) {
614 if (status & MXT_RELEASE) {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700615 dev_dbg(dev, "[%d] released\n", id);
616
Iiro Valkonen7686b102011-02-02 23:21:58 -0800617 finger[id].status = MXT_RELEASE;
618 mxt_input_report(data, id);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700619 }
620 return;
621 }
622
623 /* Check only AMP detection */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800624 if (!(status & (MXT_PRESS | MXT_MOVE)))
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700625 return;
626
Joonyoung Shim910d8052011-04-12 23:14:38 -0700627 x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
628 y = (message->message[2] << 4) | ((message->message[3] & 0xf));
Jing Lin2f863172011-10-17 10:56:58 -0700629 if (data->pdata->x_size < 1024)
Joonyoung Shim910d8052011-04-12 23:14:38 -0700630 x = x >> 2;
Jing Lin2f863172011-10-17 10:56:58 -0700631 if (data->pdata->y_size < 1024)
Joonyoung Shim910d8052011-04-12 23:14:38 -0700632 y = y >> 2;
633
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700634 area = message->message[4];
Yufeng Shene6eb36a2011-10-11 12:28:21 -0700635 pressure = message->message[5];
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700636
637 dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
Iiro Valkonen7686b102011-02-02 23:21:58 -0800638 status & MXT_MOVE ? "moved" : "pressed",
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700639 x, y, area);
640
Iiro Valkonen7686b102011-02-02 23:21:58 -0800641 finger[id].status = status & MXT_MOVE ?
642 MXT_MOVE : MXT_PRESS;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700643 finger[id].x = x;
644 finger[id].y = y;
645 finger[id].area = area;
Yufeng Shene6eb36a2011-10-11 12:28:21 -0700646 finger[id].pressure = pressure;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700647
Iiro Valkonen7686b102011-02-02 23:21:58 -0800648 mxt_input_report(data, id);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700649}
650
Iiro Valkonen7686b102011-02-02 23:21:58 -0800651static irqreturn_t mxt_interrupt(int irq, void *dev_id)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700652{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800653 struct mxt_data *data = dev_id;
654 struct mxt_message message;
655 struct mxt_object *object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700656 struct device *dev = &data->client->dev;
657 int id;
658 u8 reportid;
659 u8 max_reportid;
660 u8 min_reportid;
661
662 do {
Iiro Valkonen7686b102011-02-02 23:21:58 -0800663 if (mxt_read_message(data, &message)) {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700664 dev_err(dev, "Failed to read message\n");
665 goto end;
666 }
667
668 reportid = message.reportid;
669
Iiro Valkonen7686b102011-02-02 23:21:58 -0800670 /* whether reportid is thing of MXT_TOUCH_MULTI */
671 object = mxt_get_object(data, MXT_TOUCH_MULTI);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700672 if (!object)
673 goto end;
674
675 max_reportid = object->max_reportid;
676 min_reportid = max_reportid - object->num_report_ids + 1;
677 id = reportid - min_reportid;
678
679 if (reportid >= min_reportid && reportid <= max_reportid)
Iiro Valkonen7686b102011-02-02 23:21:58 -0800680 mxt_input_touchevent(data, &message, id);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700681 else
Iiro Valkonen7686b102011-02-02 23:21:58 -0800682 mxt_dump_message(dev, &message);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700683 } while (reportid != 0xff);
684
685end:
686 return IRQ_HANDLED;
687}
688
Iiro Valkonen7686b102011-02-02 23:21:58 -0800689static int mxt_check_reg_init(struct mxt_data *data)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700690{
Iiro Valkonen71749f52011-02-15 13:36:52 -0800691 const struct mxt_platform_data *pdata = data->pdata;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800692 struct mxt_object *object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700693 struct device *dev = &data->client->dev;
694 int index = 0;
Iiro Valkonen71749f52011-02-15 13:36:52 -0800695 int i, j, config_offset;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700696
Iiro Valkonen71749f52011-02-15 13:36:52 -0800697 if (!pdata->config) {
698 dev_dbg(dev, "No cfg data defined, skipping reg init\n");
699 return 0;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700700 }
701
702 for (i = 0; i < data->info.object_num; i++) {
703 object = data->object_table + i;
704
Iiro Valkonen7686b102011-02-02 23:21:58 -0800705 if (!mxt_object_writable(object->type))
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700706 continue;
707
Iiro Valkonen71749f52011-02-15 13:36:52 -0800708 for (j = 0; j < object->size + 1; j++) {
709 config_offset = index + j;
710 if (config_offset > pdata->config_length) {
711 dev_err(dev, "Not enough config data!\n");
712 return -EINVAL;
713 }
Iiro Valkonen7686b102011-02-02 23:21:58 -0800714 mxt_write_object(data, object->type, j,
Iiro Valkonen71749f52011-02-15 13:36:52 -0800715 pdata->config[config_offset]);
716 }
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700717 index += object->size + 1;
718 }
719
720 return 0;
721}
722
Iiro Valkonen7686b102011-02-02 23:21:58 -0800723static int mxt_make_highchg(struct mxt_data *data)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700724{
725 struct device *dev = &data->client->dev;
Iiro Valkonen26cdb1a2011-02-04 00:51:05 -0800726 struct mxt_message message;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700727 int count = 10;
728 int error;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700729
730 /* Read dummy message to make high CHG pin */
731 do {
Iiro Valkonen26cdb1a2011-02-04 00:51:05 -0800732 error = mxt_read_message(data, &message);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700733 if (error)
734 return error;
Iiro Valkonen26cdb1a2011-02-04 00:51:05 -0800735 } while (message.reportid != 0xff && --count);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700736
737 if (!count) {
738 dev_err(dev, "CHG pin isn't cleared\n");
739 return -EBUSY;
740 }
741
742 return 0;
743}
744
Iiro Valkonen7686b102011-02-02 23:21:58 -0800745static int mxt_get_info(struct mxt_data *data)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700746{
747 struct i2c_client *client = data->client;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800748 struct mxt_info *info = &data->info;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700749 int error;
750 u8 val;
751
Iiro Valkonen7686b102011-02-02 23:21:58 -0800752 error = mxt_read_reg(client, MXT_FAMILY_ID, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700753 if (error)
754 return error;
755 info->family_id = val;
756
Iiro Valkonen7686b102011-02-02 23:21:58 -0800757 error = mxt_read_reg(client, MXT_VARIANT_ID, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700758 if (error)
759 return error;
760 info->variant_id = val;
761
Iiro Valkonen7686b102011-02-02 23:21:58 -0800762 error = mxt_read_reg(client, MXT_VERSION, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700763 if (error)
764 return error;
765 info->version = val;
766
Iiro Valkonen7686b102011-02-02 23:21:58 -0800767 error = mxt_read_reg(client, MXT_BUILD, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700768 if (error)
769 return error;
770 info->build = val;
771
Iiro Valkonen7686b102011-02-02 23:21:58 -0800772 error = mxt_read_reg(client, MXT_OBJECT_NUM, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700773 if (error)
774 return error;
775 info->object_num = val;
776
777 return 0;
778}
779
Iiro Valkonen7686b102011-02-02 23:21:58 -0800780static int mxt_get_object_table(struct mxt_data *data)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700781{
782 int error;
783 int i;
784 u16 reg;
785 u8 reportid = 0;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800786 u8 buf[MXT_OBJECT_SIZE];
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700787
788 for (i = 0; i < data->info.object_num; i++) {
Iiro Valkonen7686b102011-02-02 23:21:58 -0800789 struct mxt_object *object = data->object_table + i;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700790
Iiro Valkonen7686b102011-02-02 23:21:58 -0800791 reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i;
792 error = mxt_read_object_table(data->client, reg, buf);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700793 if (error)
794 return error;
795
796 object->type = buf[0];
797 object->start_address = (buf[2] << 8) | buf[1];
798 object->size = buf[3];
799 object->instances = buf[4];
800 object->num_report_ids = buf[5];
801
802 if (object->num_report_ids) {
803 reportid += object->num_report_ids *
804 (object->instances + 1);
805 object->max_reportid = reportid;
806 }
807 }
808
809 return 0;
810}
811
Amy Maloche7e447432011-09-14 11:36:30 -0700812static void mxt_reset_delay(struct mxt_data *data)
813{
814 struct mxt_info *info = &data->info;
815
816 switch (info->family_id) {
817 case MXT224_ID:
818 msleep(MXT224_RESET_TIME);
819 break;
820 case MXT1386_ID:
821 msleep(MXT1386_RESET_TIME);
822 break;
823 default:
824 msleep(MXT_RESET_TIME);
825 }
826}
827
Iiro Valkonen7686b102011-02-02 23:21:58 -0800828static int mxt_initialize(struct mxt_data *data)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700829{
830 struct i2c_client *client = data->client;
Iiro Valkonen7686b102011-02-02 23:21:58 -0800831 struct mxt_info *info = &data->info;
Jing Lin36aee812011-10-17 17:17:28 -0700832 int error;
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700833 int timeout_counter = 0;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700834 u8 val;
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700835 u8 command_register;
Jing Lin36aee812011-10-17 17:17:28 -0700836 struct mxt_object *t7_object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700837
Iiro Valkonen7686b102011-02-02 23:21:58 -0800838 error = mxt_get_info(data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700839 if (error)
840 return error;
841
842 data->object_table = kcalloc(info->object_num,
Iiro Valkonen7686b102011-02-02 23:21:58 -0800843 sizeof(struct mxt_object),
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700844 GFP_KERNEL);
845 if (!data->object_table) {
846 dev_err(&client->dev, "Failed to allocate memory\n");
847 return -ENOMEM;
848 }
849
850 /* Get object table information */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800851 error = mxt_get_object_table(data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700852 if (error)
Jing Lin32c72532011-11-03 12:02:33 -0700853 goto free_object_table;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700854
855 /* Check register init values */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800856 error = mxt_check_reg_init(data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700857 if (error)
Jing Lin32c72532011-11-03 12:02:33 -0700858 goto free_object_table;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700859
Amy Maloche52262212011-09-15 16:46:57 -0700860 /* Store T7 and T9 locally, used in suspend/resume operations */
Jing Lin36aee812011-10-17 17:17:28 -0700861 t7_object = mxt_get_object(data, MXT_GEN_POWER);
862 if (!t7_object) {
863 dev_err(&client->dev, "Failed to get T7 object\n");
Jing Lin32c72532011-11-03 12:02:33 -0700864 error = -EINVAL;
865 goto free_object_table;
Jing Lin36aee812011-10-17 17:17:28 -0700866 }
867
868 data->t7_start_addr = t7_object->start_address;
869 error = __mxt_read_reg(client, data->t7_start_addr,
870 T7_DATA_SIZE, data->t7_data);
871 if (error < 0) {
872 dev_err(&client->dev,
Jing Lin32c72532011-11-03 12:02:33 -0700873 "Failed to save current power state\n");
874 goto free_object_table;
Amy Maloche52262212011-09-15 16:46:57 -0700875 }
876 error = mxt_read_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_CTRL,
877 &data->t9_ctrl);
878 if (error < 0) {
Jing Lin32c72532011-11-03 12:02:33 -0700879 dev_err(&client->dev, "Failed to save current touch object\n");
880 goto free_object_table;
Amy Maloche52262212011-09-15 16:46:57 -0700881 }
882
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700883 /* Backup to memory */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800884 mxt_write_object(data, MXT_GEN_COMMAND,
885 MXT_COMMAND_BACKUPNV,
886 MXT_BACKUP_VALUE);
887 msleep(MXT_BACKUP_TIME);
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700888 do {
889 error = mxt_read_object(data, MXT_GEN_COMMAND,
890 MXT_COMMAND_BACKUPNV,
891 &command_register);
892 if (error)
Jing Lin32c72532011-11-03 12:02:33 -0700893 goto free_object_table;
Amy Maloche7e447432011-09-14 11:36:30 -0700894 usleep_range(1000, 2000);
895 } while ((command_register != 0) && (++timeout_counter <= 100));
896 if (timeout_counter > 100) {
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700897 dev_err(&client->dev, "No response after backup!\n");
Jing Lin32c72532011-11-03 12:02:33 -0700898 error = -EIO;
899 goto free_object_table;
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700900 }
901
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700902
903 /* Soft reset */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800904 mxt_write_object(data, MXT_GEN_COMMAND,
905 MXT_COMMAND_RESET, 1);
Iiro Valkonen4ac053c2011-09-08 11:10:52 -0700906
Amy Maloche7e447432011-09-14 11:36:30 -0700907 mxt_reset_delay(data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700908
909 /* Update matrix size at info struct */
Iiro Valkonen7686b102011-02-02 23:21:58 -0800910 error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700911 if (error)
Jing Lin32c72532011-11-03 12:02:33 -0700912 goto free_object_table;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700913 info->matrix_xsize = val;
914
Iiro Valkonen7686b102011-02-02 23:21:58 -0800915 error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700916 if (error)
Jing Lin32c72532011-11-03 12:02:33 -0700917 goto free_object_table;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700918 info->matrix_ysize = val;
919
920 dev_info(&client->dev,
921 "Family ID: %d Variant ID: %d Version: %d Build: %d\n",
922 info->family_id, info->variant_id, info->version,
923 info->build);
924
925 dev_info(&client->dev,
926 "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
927 info->matrix_xsize, info->matrix_ysize,
928 info->object_num);
929
930 return 0;
Jing Lin32c72532011-11-03 12:02:33 -0700931
932free_object_table:
933 kfree(data->object_table);
934 return error;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700935}
936
Iiro Valkonen7686b102011-02-02 23:21:58 -0800937static ssize_t mxt_object_show(struct device *dev,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700938 struct device_attribute *attr, char *buf)
939{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800940 struct mxt_data *data = dev_get_drvdata(dev);
941 struct mxt_object *object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700942 int count = 0;
943 int i, j;
944 int error;
945 u8 val;
946
947 for (i = 0; i < data->info.object_num; i++) {
948 object = data->object_table + i;
949
Daniel Kurtz4ef11a82011-11-02 10:43:08 -0700950 count += snprintf(buf + count, PAGE_SIZE - count,
951 "Object[%d] (Type %d)\n",
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700952 i + 1, object->type);
Daniel Kurtz4ef11a82011-11-02 10:43:08 -0700953 if (count >= PAGE_SIZE)
954 return PAGE_SIZE - 1;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700955
Iiro Valkonen7686b102011-02-02 23:21:58 -0800956 if (!mxt_object_readable(object->type)) {
Daniel Kurtz4ef11a82011-11-02 10:43:08 -0700957 count += snprintf(buf + count, PAGE_SIZE - count,
958 "\n");
959 if (count >= PAGE_SIZE)
960 return PAGE_SIZE - 1;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700961 continue;
962 }
963
964 for (j = 0; j < object->size + 1; j++) {
Iiro Valkonen7686b102011-02-02 23:21:58 -0800965 error = mxt_read_object(data,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700966 object->type, j, &val);
967 if (error)
968 return error;
969
Daniel Kurtz4ef11a82011-11-02 10:43:08 -0700970 count += snprintf(buf + count, PAGE_SIZE - count,
971 "\t[%2d]: %02x (%d)\n", j, val, val);
972 if (count >= PAGE_SIZE)
973 return PAGE_SIZE - 1;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700974 }
975
Daniel Kurtz4ef11a82011-11-02 10:43:08 -0700976 count += snprintf(buf + count, PAGE_SIZE - count, "\n");
977 if (count >= PAGE_SIZE)
978 return PAGE_SIZE - 1;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700979 }
980
981 return count;
982}
983
Iiro Valkonen7686b102011-02-02 23:21:58 -0800984static int mxt_load_fw(struct device *dev, const char *fn)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700985{
Iiro Valkonen7686b102011-02-02 23:21:58 -0800986 struct mxt_data *data = dev_get_drvdata(dev);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -0700987 struct i2c_client *client = data->client;
988 const struct firmware *fw = NULL;
989 unsigned int frame_size;
990 unsigned int pos = 0;
991 int ret;
992
993 ret = request_firmware(&fw, fn, dev);
994 if (ret) {
995 dev_err(dev, "Unable to open firmware %s\n", fn);
996 return ret;
997 }
998
999 /* Change to the bootloader mode */
Iiro Valkonen7686b102011-02-02 23:21:58 -08001000 mxt_write_object(data, MXT_GEN_COMMAND,
1001 MXT_COMMAND_RESET, MXT_BOOT_VALUE);
Amy Maloche7e447432011-09-14 11:36:30 -07001002
1003 mxt_reset_delay(data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001004
1005 /* Change to slave address of bootloader */
Iiro Valkonen7686b102011-02-02 23:21:58 -08001006 if (client->addr == MXT_APP_LOW)
1007 client->addr = MXT_BOOT_LOW;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001008 else
Iiro Valkonen7686b102011-02-02 23:21:58 -08001009 client->addr = MXT_BOOT_HIGH;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001010
Iiro Valkonen7686b102011-02-02 23:21:58 -08001011 ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001012 if (ret)
1013 goto out;
1014
1015 /* Unlock bootloader */
Iiro Valkonen7686b102011-02-02 23:21:58 -08001016 mxt_unlock_bootloader(client);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001017
1018 while (pos < fw->size) {
Iiro Valkonen7686b102011-02-02 23:21:58 -08001019 ret = mxt_check_bootloader(client,
1020 MXT_WAITING_FRAME_DATA);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001021 if (ret)
1022 goto out;
1023
1024 frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
1025
1026 /* We should add 2 at frame size as the the firmware data is not
1027 * included the CRC bytes.
1028 */
1029 frame_size += 2;
1030
1031 /* Write one frame to device */
Iiro Valkonen7686b102011-02-02 23:21:58 -08001032 mxt_fw_write(client, fw->data + pos, frame_size);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001033
Iiro Valkonen7686b102011-02-02 23:21:58 -08001034 ret = mxt_check_bootloader(client,
1035 MXT_FRAME_CRC_PASS);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001036 if (ret)
1037 goto out;
1038
1039 pos += frame_size;
1040
1041 dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
1042 }
1043
1044out:
1045 release_firmware(fw);
1046
1047 /* Change to slave address of application */
Iiro Valkonen7686b102011-02-02 23:21:58 -08001048 if (client->addr == MXT_BOOT_LOW)
1049 client->addr = MXT_APP_LOW;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001050 else
Iiro Valkonen7686b102011-02-02 23:21:58 -08001051 client->addr = MXT_APP_HIGH;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001052
1053 return ret;
1054}
1055
Iiro Valkonen7686b102011-02-02 23:21:58 -08001056static ssize_t mxt_update_fw_store(struct device *dev,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001057 struct device_attribute *attr,
1058 const char *buf, size_t count)
1059{
Iiro Valkonen7686b102011-02-02 23:21:58 -08001060 struct mxt_data *data = dev_get_drvdata(dev);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001061 int error;
1062
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001063 disable_irq(data->irq);
1064
Iiro Valkonen7686b102011-02-02 23:21:58 -08001065 error = mxt_load_fw(dev, MXT_FW_NAME);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001066 if (error) {
1067 dev_err(dev, "The firmware update failed(%d)\n", error);
1068 count = error;
1069 } else {
1070 dev_dbg(dev, "The firmware update succeeded\n");
1071
1072 /* Wait for reset */
Iiro Valkonen7686b102011-02-02 23:21:58 -08001073 msleep(MXT_FWRESET_TIME);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001074
1075 kfree(data->object_table);
1076 data->object_table = NULL;
1077
Iiro Valkonen7686b102011-02-02 23:21:58 -08001078 mxt_initialize(data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001079 }
1080
1081 enable_irq(data->irq);
1082
Iiro Valkonen08960a02011-04-12 23:16:40 -07001083 error = mxt_make_highchg(data);
1084 if (error)
1085 return error;
1086
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001087 return count;
1088}
1089
Iiro Valkonen7686b102011-02-02 23:21:58 -08001090static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
1091static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001092
Iiro Valkonen7686b102011-02-02 23:21:58 -08001093static struct attribute *mxt_attrs[] = {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001094 &dev_attr_object.attr,
1095 &dev_attr_update_fw.attr,
1096 NULL
1097};
1098
Iiro Valkonen7686b102011-02-02 23:21:58 -08001099static const struct attribute_group mxt_attr_group = {
1100 .attrs = mxt_attrs,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001101};
1102
Amy Maloche52262212011-09-15 16:46:57 -07001103static int mxt_start(struct mxt_data *data)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001104{
Jing Lin36aee812011-10-17 17:17:28 -07001105 int error;
1106
Amy Maloche52262212011-09-15 16:46:57 -07001107 /* restore the old power state values and reenable touch */
Jing Lin36aee812011-10-17 17:17:28 -07001108 error = __mxt_write_reg(data->client, data->t7_start_addr,
1109 T7_DATA_SIZE, data->t7_data);
1110 if (error < 0) {
1111 dev_err(&data->client->dev,
1112 "failed to restore old power state\n");
1113 return error;
Amy Maloche52262212011-09-15 16:46:57 -07001114 }
Jing Lin36aee812011-10-17 17:17:28 -07001115
Amy Maloche52262212011-09-15 16:46:57 -07001116 error = mxt_write_object(data,
1117 MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, data->t9_ctrl);
1118 if (error < 0) {
1119 dev_err(&data->client->dev, "failed to restore touch\n");
1120 return error;
1121 }
1122
1123 return 0;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001124}
1125
Amy Maloche52262212011-09-15 16:46:57 -07001126static int mxt_stop(struct mxt_data *data)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001127{
Jing Lin36aee812011-10-17 17:17:28 -07001128 int error;
1129 u8 t7_data[T7_DATA_SIZE] = {0};
1130
1131 /* disable touch and configure deep sleep mode */
1132 error = mxt_write_object(data, MXT_TOUCH_MULTI, MXT_TOUCH_CTRL, 0);
1133 if (error < 0) {
1134 dev_err(&data->client->dev, "failed to disable touch\n");
1135 return error;
Amy Maloche52262212011-09-15 16:46:57 -07001136 }
1137
Jing Lin36aee812011-10-17 17:17:28 -07001138 error = __mxt_write_reg(data->client, data->t7_start_addr,
1139 T7_DATA_SIZE, t7_data);
Amy Maloche52262212011-09-15 16:46:57 -07001140 if (error < 0) {
1141 dev_err(&data->client->dev,
Jing Lin36aee812011-10-17 17:17:28 -07001142 "failed to configure deep sleep mode\n");
Amy Maloche52262212011-09-15 16:46:57 -07001143 return error;
1144 }
1145
1146 return 0;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001147}
1148
Iiro Valkonen7686b102011-02-02 23:21:58 -08001149static int mxt_input_open(struct input_dev *dev)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001150{
Iiro Valkonen7686b102011-02-02 23:21:58 -08001151 struct mxt_data *data = input_get_drvdata(dev);
Amy Maloche52262212011-09-15 16:46:57 -07001152 int error;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001153
Amy Maloche52262212011-09-15 16:46:57 -07001154 error = mxt_start(data);
1155 if (error < 0) {
1156 dev_err(&data->client->dev, "mxt_start failed in input_open\n");
1157 return error;
1158 }
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001159
1160 return 0;
1161}
1162
Iiro Valkonen7686b102011-02-02 23:21:58 -08001163static void mxt_input_close(struct input_dev *dev)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001164{
Iiro Valkonen7686b102011-02-02 23:21:58 -08001165 struct mxt_data *data = input_get_drvdata(dev);
Amy Maloche52262212011-09-15 16:46:57 -07001166 int error;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001167
Amy Maloche52262212011-09-15 16:46:57 -07001168 error = mxt_stop(data);
1169 if (error < 0)
1170 dev_err(&data->client->dev, "mxt_stop failed in input_close\n");
1171
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001172}
1173
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301174static int mxt_power_on(struct mxt_data *data, bool on)
1175{
1176 int rc;
1177
1178 if (on == false)
1179 goto power_off;
1180
Amy Maloche21115eb2011-11-02 09:04:37 -07001181 rc = regulator_set_optimum_mode(data->vcc_ana, MXT_ACTIVE_LOAD_UA);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301182 if (rc < 0) {
Amy Maloche21115eb2011-11-02 09:04:37 -07001183 dev_err(&data->client->dev,
1184 "Regulator vcc_ana set_opt failed rc=%d\n", rc);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301185 return rc;
1186 }
1187
Amy Maloche21115eb2011-11-02 09:04:37 -07001188 rc = regulator_enable(data->vcc_ana);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301189 if (rc) {
Amy Maloche21115eb2011-11-02 09:04:37 -07001190 dev_err(&data->client->dev,
1191 "Regulator vcc_ana enable failed rc=%d\n", rc);
1192 goto error_reg_en_vcc_ana;
1193 }
1194
1195 if (data->pdata->digital_pwr_regulator) {
1196 rc = regulator_set_optimum_mode(data->vcc_dig,
1197 MXT_ACTIVE_LOAD_DIG_UA);
1198 if (rc < 0) {
1199 dev_err(&data->client->dev,
1200 "Regulator vcc_dig set_opt failed rc=%d\n",
1201 rc);
1202 goto error_reg_opt_vcc_dig;
1203 }
1204
1205 rc = regulator_enable(data->vcc_dig);
1206 if (rc) {
1207 dev_err(&data->client->dev,
1208 "Regulator vcc_dig enable failed rc=%d\n", rc);
1209 goto error_reg_en_vcc_dig;
1210 }
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301211 }
1212
1213 if (data->pdata->i2c_pull_up) {
1214 rc = regulator_set_optimum_mode(data->vcc_i2c, MXT_I2C_LOAD_UA);
1215 if (rc < 0) {
1216 dev_err(&data->client->dev,
Amy Maloche21115eb2011-11-02 09:04:37 -07001217 "Regulator vcc_i2c set_opt failed rc=%d\n", rc);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301218 goto error_reg_opt_i2c;
1219 }
1220
1221 rc = regulator_enable(data->vcc_i2c);
1222 if (rc) {
1223 dev_err(&data->client->dev,
Amy Maloche21115eb2011-11-02 09:04:37 -07001224 "Regulator vcc_i2c enable failed rc=%d\n", rc);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301225 goto error_reg_en_vcc_i2c;
1226 }
1227 }
1228
Amy Malochef0d7b8d2011-10-17 12:10:51 -07001229 msleep(130);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301230
1231 return 0;
1232
1233error_reg_en_vcc_i2c:
1234 if (data->pdata->i2c_pull_up)
1235 regulator_set_optimum_mode(data->vcc_i2c, 0);
1236error_reg_opt_i2c:
Amy Maloche21115eb2011-11-02 09:04:37 -07001237 if (data->pdata->digital_pwr_regulator)
1238 regulator_disable(data->vcc_dig);
1239error_reg_en_vcc_dig:
1240 if (data->pdata->digital_pwr_regulator)
1241 regulator_set_optimum_mode(data->vcc_dig, 0);
1242error_reg_opt_vcc_dig:
1243 regulator_disable(data->vcc_ana);
1244error_reg_en_vcc_ana:
1245 regulator_set_optimum_mode(data->vcc_ana, 0);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301246 return rc;
1247
1248power_off:
Amy Maloche21115eb2011-11-02 09:04:37 -07001249 regulator_set_optimum_mode(data->vcc_ana, 0);
1250 regulator_disable(data->vcc_ana);
1251 if (data->pdata->digital_pwr_regulator) {
1252 regulator_set_optimum_mode(data->vcc_dig, 0);
1253 regulator_disable(data->vcc_dig);
1254 }
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301255 if (data->pdata->i2c_pull_up) {
1256 regulator_set_optimum_mode(data->vcc_i2c, 0);
1257 regulator_disable(data->vcc_i2c);
1258 }
1259 msleep(50);
1260 return 0;
1261}
1262
1263static int mxt_regulator_configure(struct mxt_data *data, bool on)
1264{
1265 int rc;
1266
1267 if (on == false)
1268 goto hw_shutdown;
1269
Amy Maloche21115eb2011-11-02 09:04:37 -07001270 data->vcc_ana = regulator_get(&data->client->dev, "vdd_ana");
1271 if (IS_ERR(data->vcc_ana)) {
1272 rc = PTR_ERR(data->vcc_ana);
1273 dev_err(&data->client->dev,
1274 "Regulator get failed vcc_ana rc=%d\n", rc);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301275 return rc;
1276 }
1277
Amy Maloche21115eb2011-11-02 09:04:37 -07001278 if (regulator_count_voltages(data->vcc_ana) > 0) {
1279 rc = regulator_set_voltage(data->vcc_ana, MXT_VTG_MIN_UV,
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301280 MXT_VTG_MAX_UV);
1281 if (rc) {
1282 dev_err(&data->client->dev,
1283 "regulator set_vtg failed rc=%d\n", rc);
Amy Maloche21115eb2011-11-02 09:04:37 -07001284 goto error_set_vtg_vcc_ana;
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301285 }
1286 }
Amy Maloche21115eb2011-11-02 09:04:37 -07001287 if (data->pdata->digital_pwr_regulator) {
1288 data->vcc_dig = regulator_get(&data->client->dev, "vdd_dig");
1289 if (IS_ERR(data->vcc_dig)) {
1290 rc = PTR_ERR(data->vcc_dig);
1291 dev_err(&data->client->dev,
1292 "Regulator get dig failed rc=%d\n", rc);
1293 goto error_get_vtg_vcc_dig;
1294 }
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301295
Amy Maloche21115eb2011-11-02 09:04:37 -07001296 if (regulator_count_voltages(data->vcc_dig) > 0) {
1297 rc = regulator_set_voltage(data->vcc_dig,
1298 MXT_VTG_DIG_MIN_UV, MXT_VTG_DIG_MAX_UV);
1299 if (rc) {
1300 dev_err(&data->client->dev,
1301 "regulator set_vtg failed rc=%d\n", rc);
1302 goto error_set_vtg_vcc_dig;
1303 }
1304 }
1305 }
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301306 if (data->pdata->i2c_pull_up) {
1307 data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
1308 if (IS_ERR(data->vcc_i2c)) {
1309 rc = PTR_ERR(data->vcc_i2c);
1310 dev_err(&data->client->dev,
1311 "Regulator get failed rc=%d\n", rc);
1312 goto error_get_vtg_i2c;
1313 }
1314 if (regulator_count_voltages(data->vcc_i2c) > 0) {
1315 rc = regulator_set_voltage(data->vcc_i2c,
1316 MXT_I2C_VTG_MIN_UV, MXT_I2C_VTG_MAX_UV);
1317 if (rc) {
1318 dev_err(&data->client->dev,
1319 "regulator set_vtg failed rc=%d\n", rc);
1320 goto error_set_vtg_i2c;
1321 }
1322 }
1323 }
1324
1325 return 0;
1326
1327error_set_vtg_i2c:
1328 regulator_put(data->vcc_i2c);
1329error_get_vtg_i2c:
Amy Maloche21115eb2011-11-02 09:04:37 -07001330 if (data->pdata->digital_pwr_regulator)
1331 if (regulator_count_voltages(data->vcc_dig) > 0)
1332 regulator_set_voltage(data->vcc_dig, 0,
1333 MXT_VTG_DIG_MAX_UV);
1334error_set_vtg_vcc_dig:
1335 if (data->pdata->digital_pwr_regulator)
1336 regulator_put(data->vcc_dig);
1337error_get_vtg_vcc_dig:
1338 if (regulator_count_voltages(data->vcc_ana) > 0)
1339 regulator_set_voltage(data->vcc_ana, 0, MXT_VTG_MAX_UV);
1340error_set_vtg_vcc_ana:
1341 regulator_put(data->vcc_ana);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301342 return rc;
1343
1344hw_shutdown:
Amy Maloche21115eb2011-11-02 09:04:37 -07001345 if (regulator_count_voltages(data->vcc_ana) > 0)
1346 regulator_set_voltage(data->vcc_ana, 0, MXT_VTG_MAX_UV);
1347 regulator_put(data->vcc_ana);
1348 if (data->pdata->digital_pwr_regulator) {
1349 if (regulator_count_voltages(data->vcc_dig) > 0)
1350 regulator_set_voltage(data->vcc_dig, 0,
1351 MXT_VTG_DIG_MAX_UV);
1352 regulator_put(data->vcc_dig);
1353 }
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301354 if (data->pdata->i2c_pull_up) {
1355 if (regulator_count_voltages(data->vcc_i2c) > 0)
1356 regulator_set_voltage(data->vcc_i2c, 0,
1357 MXT_I2C_VTG_MAX_UV);
1358 regulator_put(data->vcc_i2c);
1359 }
1360 return 0;
1361}
1362
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301363#ifdef CONFIG_PM
Jing Linbace50b2011-10-18 22:55:47 -07001364static int mxt_regulator_lpm(struct mxt_data *data, bool on)
1365{
1366
1367 int rc;
1368
1369 if (on == false)
1370 goto regulator_hpm;
1371
Amy Maloche21115eb2011-11-02 09:04:37 -07001372 rc = regulator_set_optimum_mode(data->vcc_ana, MXT_LPM_LOAD_UA);
Jing Linbace50b2011-10-18 22:55:47 -07001373 if (rc < 0) {
1374 dev_err(&data->client->dev,
Amy Maloche21115eb2011-11-02 09:04:37 -07001375 "Regulator vcc_ana set_opt failed rc=%d\n", rc);
Jing Linbace50b2011-10-18 22:55:47 -07001376 goto fail_regulator_lpm;
1377 }
1378
Amy Maloche21115eb2011-11-02 09:04:37 -07001379 if (data->pdata->digital_pwr_regulator) {
1380 rc = regulator_set_optimum_mode(data->vcc_dig,
1381 MXT_LPM_LOAD_DIG_UA);
1382 if (rc < 0) {
1383 dev_err(&data->client->dev,
1384 "Regulator vcc_dig set_opt failed rc=%d\n", rc);
1385 goto fail_regulator_lpm;
1386 }
1387 }
1388
Jing Linbace50b2011-10-18 22:55:47 -07001389 if (data->pdata->i2c_pull_up) {
1390 rc = regulator_set_optimum_mode(data->vcc_i2c,
1391 MXT_I2C_LPM_LOAD_UA);
1392 if (rc < 0) {
1393 dev_err(&data->client->dev,
Amy Maloche21115eb2011-11-02 09:04:37 -07001394 "Regulator vcc_i2c set_opt failed rc=%d\n", rc);
Jing Linbace50b2011-10-18 22:55:47 -07001395 goto fail_regulator_lpm;
1396 }
1397 }
1398
1399 return 0;
1400
1401regulator_hpm:
1402
Amy Maloche21115eb2011-11-02 09:04:37 -07001403 rc = regulator_set_optimum_mode(data->vcc_ana, MXT_ACTIVE_LOAD_UA);
Jing Linbace50b2011-10-18 22:55:47 -07001404 if (rc < 0) {
1405 dev_err(&data->client->dev,
Amy Maloche21115eb2011-11-02 09:04:37 -07001406 "Regulator vcc_ana set_opt failed rc=%d\n", rc);
Jing Linbace50b2011-10-18 22:55:47 -07001407 goto fail_regulator_hpm;
1408 }
1409
Amy Maloche21115eb2011-11-02 09:04:37 -07001410 if (data->pdata->digital_pwr_regulator) {
1411 rc = regulator_set_optimum_mode(data->vcc_dig,
1412 MXT_ACTIVE_LOAD_DIG_UA);
1413 if (rc < 0) {
1414 dev_err(&data->client->dev,
1415 "Regulator vcc_dig set_opt failed rc=%d\n", rc);
1416 goto fail_regulator_hpm;
1417 }
1418 }
1419
Jing Linbace50b2011-10-18 22:55:47 -07001420 if (data->pdata->i2c_pull_up) {
1421 rc = regulator_set_optimum_mode(data->vcc_i2c, MXT_I2C_LOAD_UA);
1422 if (rc < 0) {
1423 dev_err(&data->client->dev,
Amy Maloche21115eb2011-11-02 09:04:37 -07001424 "Regulator vcc_i2c set_opt failed rc=%d\n", rc);
Jing Linbace50b2011-10-18 22:55:47 -07001425 goto fail_regulator_hpm;
1426 }
1427 }
1428
1429 return 0;
1430
1431fail_regulator_lpm:
Amy Maloche21115eb2011-11-02 09:04:37 -07001432 regulator_set_optimum_mode(data->vcc_ana, MXT_ACTIVE_LOAD_UA);
1433 if (data->pdata->digital_pwr_regulator)
1434 regulator_set_optimum_mode(data->vcc_dig,
1435 MXT_ACTIVE_LOAD_DIG_UA);
Jing Linbace50b2011-10-18 22:55:47 -07001436 if (data->pdata->i2c_pull_up)
1437 regulator_set_optimum_mode(data->vcc_i2c, MXT_I2C_LOAD_UA);
1438
1439 return rc;
1440
1441fail_regulator_hpm:
Amy Maloche21115eb2011-11-02 09:04:37 -07001442 regulator_set_optimum_mode(data->vcc_ana, MXT_LPM_LOAD_UA);
1443 if (data->pdata->digital_pwr_regulator)
1444 regulator_set_optimum_mode(data->vcc_dig, MXT_LPM_LOAD_DIG_UA);
Jing Linbace50b2011-10-18 22:55:47 -07001445 if (data->pdata->i2c_pull_up)
1446 regulator_set_optimum_mode(data->vcc_i2c, MXT_I2C_LPM_LOAD_UA);
1447
1448 return rc;
1449}
1450
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301451static int mxt_suspend(struct device *dev)
1452{
1453 struct i2c_client *client = to_i2c_client(dev);
1454 struct mxt_data *data = i2c_get_clientdata(client);
1455 struct input_dev *input_dev = data->input_dev;
Amy Maloche52262212011-09-15 16:46:57 -07001456 int error;
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301457
1458 mutex_lock(&input_dev->mutex);
1459
Amy Maloche52262212011-09-15 16:46:57 -07001460 if (input_dev->users) {
1461 error = mxt_stop(data);
1462 if (error < 0) {
Jing Lin36aee812011-10-17 17:17:28 -07001463 dev_err(dev, "mxt_stop failed in suspend\n");
Amy Maloche52262212011-09-15 16:46:57 -07001464 mutex_unlock(&input_dev->mutex);
1465 return error;
1466 }
1467
1468 }
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301469
1470 mutex_unlock(&input_dev->mutex);
1471
Jing Linbace50b2011-10-18 22:55:47 -07001472 /* put regulators in low power mode */
1473 error = mxt_regulator_lpm(data, true);
1474 if (error < 0) {
1475 dev_err(dev, "failed to enter low power mode\n");
1476 return error;
1477 }
1478
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301479 return 0;
1480}
1481
1482static int mxt_resume(struct device *dev)
1483{
1484 struct i2c_client *client = to_i2c_client(dev);
1485 struct mxt_data *data = i2c_get_clientdata(client);
1486 struct input_dev *input_dev = data->input_dev;
Amy Maloche52262212011-09-15 16:46:57 -07001487 int error;
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301488
Jing Linbace50b2011-10-18 22:55:47 -07001489 /* put regulators in high power mode */
1490 error = mxt_regulator_lpm(data, false);
1491 if (error < 0) {
1492 dev_err(dev, "failed to enter high power mode\n");
1493 return error;
1494 }
1495
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301496 mutex_lock(&input_dev->mutex);
1497
Amy Maloche52262212011-09-15 16:46:57 -07001498 if (input_dev->users) {
1499 error = mxt_start(data);
1500 if (error < 0) {
Jing Lin36aee812011-10-17 17:17:28 -07001501 dev_err(dev, "mxt_start failed in resume\n");
Amy Maloche52262212011-09-15 16:46:57 -07001502 mutex_unlock(&input_dev->mutex);
1503 return error;
1504 }
1505 }
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301506
1507 mutex_unlock(&input_dev->mutex);
1508
1509 return 0;
1510}
1511
1512#if defined(CONFIG_HAS_EARLYSUSPEND)
1513static void mxt_early_suspend(struct early_suspend *h)
1514{
1515 struct mxt_data *data = container_of(h, struct mxt_data, early_suspend);
1516
1517 mxt_suspend(&data->client->dev);
1518}
1519
1520static void mxt_late_resume(struct early_suspend *h)
1521{
1522 struct mxt_data *data = container_of(h, struct mxt_data, early_suspend);
1523
1524 mxt_resume(&data->client->dev);
1525}
1526#endif
1527
1528static const struct dev_pm_ops mxt_pm_ops = {
1529#ifndef CONFIG_HAS_EARLYSUSPEND
1530 .suspend = mxt_suspend,
1531 .resume = mxt_resume,
1532#endif
1533};
1534#endif
1535
Iiro Valkonen7686b102011-02-02 23:21:58 -08001536static int __devinit mxt_probe(struct i2c_client *client,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001537 const struct i2c_device_id *id)
1538{
Iiro Valkonen919ed892011-02-15 13:36:52 -08001539 const struct mxt_platform_data *pdata = client->dev.platform_data;
Iiro Valkonen7686b102011-02-02 23:21:58 -08001540 struct mxt_data *data;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001541 struct input_dev *input_dev;
1542 int error;
1543
Iiro Valkonen919ed892011-02-15 13:36:52 -08001544 if (!pdata)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001545 return -EINVAL;
1546
Iiro Valkonen7686b102011-02-02 23:21:58 -08001547 data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001548 input_dev = input_allocate_device();
1549 if (!data || !input_dev) {
1550 dev_err(&client->dev, "Failed to allocate memory\n");
1551 error = -ENOMEM;
1552 goto err_free_mem;
1553 }
1554
Iiro Valkonen7686b102011-02-02 23:21:58 -08001555 input_dev->name = "Atmel maXTouch Touchscreen";
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001556 input_dev->id.bustype = BUS_I2C;
1557 input_dev->dev.parent = &client->dev;
Iiro Valkonen7686b102011-02-02 23:21:58 -08001558 input_dev->open = mxt_input_open;
1559 input_dev->close = mxt_input_close;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001560
Joonyoung Shim910d8052011-04-12 23:14:38 -07001561 data->client = client;
1562 data->input_dev = input_dev;
1563 data->pdata = pdata;
1564 data->irq = client->irq;
1565
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001566 __set_bit(EV_ABS, input_dev->evbit);
1567 __set_bit(EV_KEY, input_dev->evbit);
1568 __set_bit(BTN_TOUCH, input_dev->keybit);
1569
1570 /* For single touch */
1571 input_set_abs_params(input_dev, ABS_X,
Jing Lin2f863172011-10-17 10:56:58 -07001572 0, data->pdata->x_size, 0, 0);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001573 input_set_abs_params(input_dev, ABS_Y,
Jing Lin2f863172011-10-17 10:56:58 -07001574 0, data->pdata->y_size, 0, 0);
Yufeng Shene6eb36a2011-10-11 12:28:21 -07001575 input_set_abs_params(input_dev, ABS_PRESSURE,
1576 0, 255, 0, 0);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001577
1578 /* For multi touch */
1579 input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
Iiro Valkonen7686b102011-02-02 23:21:58 -08001580 0, MXT_MAX_AREA, 0, 0);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001581 input_set_abs_params(input_dev, ABS_MT_POSITION_X,
Jing Lin2f863172011-10-17 10:56:58 -07001582 0, data->pdata->x_size, 0, 0);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001583 input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
Jing Lin2f863172011-10-17 10:56:58 -07001584 0, data->pdata->y_size, 0, 0);
Yufeng Shene6eb36a2011-10-11 12:28:21 -07001585 input_set_abs_params(input_dev, ABS_MT_PRESSURE,
1586 0, 255, 0, 0);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001587
1588 input_set_drvdata(input_dev, data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001589 i2c_set_clientdata(client, data);
1590
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301591 if (pdata->init_hw)
1592 error = pdata->init_hw(true);
1593 else
1594 error = mxt_regulator_configure(data, true);
1595 if (error) {
1596 dev_err(&client->dev, "Failed to intialize hardware\n");
Jing Lin32c72532011-11-03 12:02:33 -07001597 goto err_free_mem;
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301598 }
1599
1600 if (pdata->power_on)
1601 error = pdata->power_on(true);
1602 else
1603 error = mxt_power_on(data, true);
1604 if (error) {
1605 dev_err(&client->dev, "Failed to power on hardware\n");
1606 goto err_regulator_on;
1607 }
1608
Iiro Valkonen7686b102011-02-02 23:21:58 -08001609 error = mxt_initialize(data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001610 if (error)
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301611 goto err_power_on;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001612
Iiro Valkonen7686b102011-02-02 23:21:58 -08001613 error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
Iiro Valkonen919ed892011-02-15 13:36:52 -08001614 pdata->irqflags, client->dev.driver->name, data);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001615 if (error) {
1616 dev_err(&client->dev, "Failed to register interrupt\n");
Jing Lin32c72532011-11-03 12:02:33 -07001617 goto err_free_object;
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001618 }
1619
Iiro Valkonen08960a02011-04-12 23:16:40 -07001620 error = mxt_make_highchg(data);
1621 if (error)
1622 goto err_free_irq;
1623
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001624 error = input_register_device(input_dev);
1625 if (error)
1626 goto err_free_irq;
1627
Iiro Valkonen7686b102011-02-02 23:21:58 -08001628 error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001629 if (error)
1630 goto err_unregister_device;
1631
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301632#if defined(CONFIG_HAS_EARLYSUSPEND)
1633 data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
1634 MXT_SUSPEND_LEVEL;
1635 data->early_suspend.suspend = mxt_early_suspend;
1636 data->early_suspend.resume = mxt_late_resume;
1637 register_early_suspend(&data->early_suspend);
1638#endif
1639
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001640 return 0;
1641
1642err_unregister_device:
1643 input_unregister_device(input_dev);
1644 input_dev = NULL;
1645err_free_irq:
1646 free_irq(client->irq, data);
Jing Lin32c72532011-11-03 12:02:33 -07001647err_free_object:
1648 kfree(data->object_table);
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301649err_power_on:
1650 if (pdata->power_on)
1651 pdata->power_on(false);
1652 else
1653 mxt_power_on(data, false);
1654err_regulator_on:
1655 if (pdata->init_hw)
1656 pdata->init_hw(false);
1657 else
1658 mxt_regulator_configure(data, false);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001659err_free_mem:
1660 input_free_device(input_dev);
1661 kfree(data);
1662 return error;
1663}
1664
Iiro Valkonen7686b102011-02-02 23:21:58 -08001665static int __devexit mxt_remove(struct i2c_client *client)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001666{
Iiro Valkonen7686b102011-02-02 23:21:58 -08001667 struct mxt_data *data = i2c_get_clientdata(client);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001668
Iiro Valkonen7686b102011-02-02 23:21:58 -08001669 sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001670 free_irq(data->irq, data);
1671 input_unregister_device(data->input_dev);
Anirudh Ghayal253ce122011-08-09 19:32:57 +05301672#if defined(CONFIG_HAS_EARLYSUSPEND)
1673 unregister_early_suspend(&data->early_suspend);
1674#endif
Anirudh Ghayala498e4d2011-08-09 19:10:12 +05301675
1676 if (data->pdata->power_on)
1677 data->pdata->power_on(false);
1678 else
1679 mxt_power_on(data, false);
1680
1681 if (data->pdata->init_hw)
1682 data->pdata->init_hw(false);
1683 else
1684 mxt_regulator_configure(data, false);
1685
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001686 kfree(data->object_table);
1687 kfree(data);
1688
1689 return 0;
1690}
1691
Iiro Valkonen7686b102011-02-02 23:21:58 -08001692static const struct i2c_device_id mxt_id[] = {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001693 { "qt602240_ts", 0 },
Iiro Valkonen7686b102011-02-02 23:21:58 -08001694 { "atmel_mxt_ts", 0 },
Chris Leech46ee2a02011-02-15 13:36:52 -08001695 { "mXT224", 0 },
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001696 { }
1697};
Iiro Valkonen7686b102011-02-02 23:21:58 -08001698MODULE_DEVICE_TABLE(i2c, mxt_id);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001699
Iiro Valkonen7686b102011-02-02 23:21:58 -08001700static struct i2c_driver mxt_driver = {
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001701 .driver = {
Iiro Valkonen7686b102011-02-02 23:21:58 -08001702 .name = "atmel_mxt_ts",
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001703 .owner = THIS_MODULE,
Dmitry Torokhov8b5fce02010-11-18 00:14:03 -08001704#ifdef CONFIG_PM
Iiro Valkonen7686b102011-02-02 23:21:58 -08001705 .pm = &mxt_pm_ops,
Dmitry Torokhov8b5fce02010-11-18 00:14:03 -08001706#endif
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001707 },
Iiro Valkonen7686b102011-02-02 23:21:58 -08001708 .probe = mxt_probe,
1709 .remove = __devexit_p(mxt_remove),
1710 .id_table = mxt_id,
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001711};
1712
Iiro Valkonen7686b102011-02-02 23:21:58 -08001713static int __init mxt_init(void)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001714{
Iiro Valkonen7686b102011-02-02 23:21:58 -08001715 return i2c_add_driver(&mxt_driver);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001716}
1717
Iiro Valkonen7686b102011-02-02 23:21:58 -08001718static void __exit mxt_exit(void)
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001719{
Iiro Valkonen7686b102011-02-02 23:21:58 -08001720 i2c_del_driver(&mxt_driver);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001721}
1722
Iiro Valkonen7686b102011-02-02 23:21:58 -08001723module_init(mxt_init);
1724module_exit(mxt_exit);
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001725
1726/* Module information */
1727MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
Iiro Valkonen7686b102011-02-02 23:21:58 -08001728MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
Joonyoung Shim4cf51c32010-07-14 21:55:30 -07001729MODULE_LICENSE("GPL");