mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
626af86112
Sysfs attribute show methods are always passed a buffer of length PAGE_SIZE. To keep from overwriting this buffer and causing havoc, use snprintf() to guarantee we never write more than the buffer can hold. In addition, at least for my touchscreen, the number and size of objects was far too big to fit in a single 4K page. Therefore, this patch also trims some redundant framing text to leave more room for actual data. Signed-off-by: Daniel Kurtz <djkurtz@chromium.org> Acked-by: Nick Dyer <nick.dyer@itdev.co.uk> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
1287 lines
29 KiB
C
1287 lines
29 KiB
C
/*
|
|
* Atmel maXTouch Touchscreen driver
|
|
*
|
|
* Copyright (C) 2010 Samsung Electronics Co.Ltd
|
|
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/i2c/atmel_mxt_ts.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/slab.h>
|
|
|
|
/* Version */
|
|
#define MXT_VER_20 20
|
|
#define MXT_VER_21 21
|
|
#define MXT_VER_22 22
|
|
|
|
/* Slave addresses */
|
|
#define MXT_APP_LOW 0x4a
|
|
#define MXT_APP_HIGH 0x4b
|
|
#define MXT_BOOT_LOW 0x24
|
|
#define MXT_BOOT_HIGH 0x25
|
|
|
|
/* Firmware */
|
|
#define MXT_FW_NAME "maxtouch.fw"
|
|
|
|
/* Registers */
|
|
#define MXT_FAMILY_ID 0x00
|
|
#define MXT_VARIANT_ID 0x01
|
|
#define MXT_VERSION 0x02
|
|
#define MXT_BUILD 0x03
|
|
#define MXT_MATRIX_X_SIZE 0x04
|
|
#define MXT_MATRIX_Y_SIZE 0x05
|
|
#define MXT_OBJECT_NUM 0x06
|
|
#define MXT_OBJECT_START 0x07
|
|
|
|
#define MXT_OBJECT_SIZE 6
|
|
|
|
/* Object types */
|
|
#define MXT_DEBUG_DIAGNOSTIC_T37 37
|
|
#define MXT_GEN_MESSAGE_T5 5
|
|
#define MXT_GEN_COMMAND_T6 6
|
|
#define MXT_GEN_POWER_T7 7
|
|
#define MXT_GEN_ACQUIRE_T8 8
|
|
#define MXT_GEN_DATASOURCE_T53 53
|
|
#define MXT_TOUCH_MULTI_T9 9
|
|
#define MXT_TOUCH_KEYARRAY_T15 15
|
|
#define MXT_TOUCH_PROXIMITY_T23 23
|
|
#define MXT_TOUCH_PROXKEY_T52 52
|
|
#define MXT_PROCI_GRIPFACE_T20 20
|
|
#define MXT_PROCG_NOISE_T22 22
|
|
#define MXT_PROCI_ONETOUCH_T24 24
|
|
#define MXT_PROCI_TWOTOUCH_T27 27
|
|
#define MXT_PROCI_GRIP_T40 40
|
|
#define MXT_PROCI_PALM_T41 41
|
|
#define MXT_PROCI_TOUCHSUPPRESSION_T42 42
|
|
#define MXT_PROCI_STYLUS_T47 47
|
|
#define MXT_PROCG_NOISESUPPRESSION_T48 48
|
|
#define MXT_SPT_COMMSCONFIG_T18 18
|
|
#define MXT_SPT_GPIOPWM_T19 19
|
|
#define MXT_SPT_SELFTEST_T25 25
|
|
#define MXT_SPT_CTECONFIG_T28 28
|
|
#define MXT_SPT_USERDATA_T38 38
|
|
#define MXT_SPT_DIGITIZER_T43 43
|
|
#define MXT_SPT_MESSAGECOUNT_T44 44
|
|
#define MXT_SPT_CTECONFIG_T46 46
|
|
|
|
/* MXT_GEN_COMMAND_T6 field */
|
|
#define MXT_COMMAND_RESET 0
|
|
#define MXT_COMMAND_BACKUPNV 1
|
|
#define MXT_COMMAND_CALIBRATE 2
|
|
#define MXT_COMMAND_REPORTALL 3
|
|
#define MXT_COMMAND_DIAGNOSTIC 5
|
|
|
|
/* MXT_GEN_POWER_T7 field */
|
|
#define MXT_POWER_IDLEACQINT 0
|
|
#define MXT_POWER_ACTVACQINT 1
|
|
#define MXT_POWER_ACTV2IDLETO 2
|
|
|
|
/* MXT_GEN_ACQUIRE_T8 field */
|
|
#define MXT_ACQUIRE_CHRGTIME 0
|
|
#define MXT_ACQUIRE_TCHDRIFT 2
|
|
#define MXT_ACQUIRE_DRIFTST 3
|
|
#define MXT_ACQUIRE_TCHAUTOCAL 4
|
|
#define MXT_ACQUIRE_SYNC 5
|
|
#define MXT_ACQUIRE_ATCHCALST 6
|
|
#define MXT_ACQUIRE_ATCHCALSTHR 7
|
|
|
|
/* MXT_TOUCH_MULTI_T9 field */
|
|
#define MXT_TOUCH_CTRL 0
|
|
#define MXT_TOUCH_XORIGIN 1
|
|
#define MXT_TOUCH_YORIGIN 2
|
|
#define MXT_TOUCH_XSIZE 3
|
|
#define MXT_TOUCH_YSIZE 4
|
|
#define MXT_TOUCH_BLEN 6
|
|
#define MXT_TOUCH_TCHTHR 7
|
|
#define MXT_TOUCH_TCHDI 8
|
|
#define MXT_TOUCH_ORIENT 9
|
|
#define MXT_TOUCH_MOVHYSTI 11
|
|
#define MXT_TOUCH_MOVHYSTN 12
|
|
#define MXT_TOUCH_NUMTOUCH 14
|
|
#define MXT_TOUCH_MRGHYST 15
|
|
#define MXT_TOUCH_MRGTHR 16
|
|
#define MXT_TOUCH_AMPHYST 17
|
|
#define MXT_TOUCH_XRANGE_LSB 18
|
|
#define MXT_TOUCH_XRANGE_MSB 19
|
|
#define MXT_TOUCH_YRANGE_LSB 20
|
|
#define MXT_TOUCH_YRANGE_MSB 21
|
|
#define MXT_TOUCH_XLOCLIP 22
|
|
#define MXT_TOUCH_XHICLIP 23
|
|
#define MXT_TOUCH_YLOCLIP 24
|
|
#define MXT_TOUCH_YHICLIP 25
|
|
#define MXT_TOUCH_XEDGECTRL 26
|
|
#define MXT_TOUCH_XEDGEDIST 27
|
|
#define MXT_TOUCH_YEDGECTRL 28
|
|
#define MXT_TOUCH_YEDGEDIST 29
|
|
#define MXT_TOUCH_JUMPLIMIT 30
|
|
|
|
/* MXT_PROCI_GRIPFACE_T20 field */
|
|
#define MXT_GRIPFACE_CTRL 0
|
|
#define MXT_GRIPFACE_XLOGRIP 1
|
|
#define MXT_GRIPFACE_XHIGRIP 2
|
|
#define MXT_GRIPFACE_YLOGRIP 3
|
|
#define MXT_GRIPFACE_YHIGRIP 4
|
|
#define MXT_GRIPFACE_MAXTCHS 5
|
|
#define MXT_GRIPFACE_SZTHR1 7
|
|
#define MXT_GRIPFACE_SZTHR2 8
|
|
#define MXT_GRIPFACE_SHPTHR1 9
|
|
#define MXT_GRIPFACE_SHPTHR2 10
|
|
#define MXT_GRIPFACE_SUPEXTTO 11
|
|
|
|
/* MXT_PROCI_NOISE field */
|
|
#define MXT_NOISE_CTRL 0
|
|
#define MXT_NOISE_OUTFLEN 1
|
|
#define MXT_NOISE_GCAFUL_LSB 3
|
|
#define MXT_NOISE_GCAFUL_MSB 4
|
|
#define MXT_NOISE_GCAFLL_LSB 5
|
|
#define MXT_NOISE_GCAFLL_MSB 6
|
|
#define MXT_NOISE_ACTVGCAFVALID 7
|
|
#define MXT_NOISE_NOISETHR 8
|
|
#define MXT_NOISE_FREQHOPSCALE 10
|
|
#define MXT_NOISE_FREQ0 11
|
|
#define MXT_NOISE_FREQ1 12
|
|
#define MXT_NOISE_FREQ2 13
|
|
#define MXT_NOISE_FREQ3 14
|
|
#define MXT_NOISE_FREQ4 15
|
|
#define MXT_NOISE_IDLEGCAFVALID 16
|
|
|
|
/* MXT_SPT_COMMSCONFIG_T18 */
|
|
#define MXT_COMMS_CTRL 0
|
|
#define MXT_COMMS_CMD 1
|
|
|
|
/* MXT_SPT_CTECONFIG_T28 field */
|
|
#define MXT_CTE_CTRL 0
|
|
#define MXT_CTE_CMD 1
|
|
#define MXT_CTE_MODE 2
|
|
#define MXT_CTE_IDLEGCAFDEPTH 3
|
|
#define MXT_CTE_ACTVGCAFDEPTH 4
|
|
#define MXT_CTE_VOLTAGE 5
|
|
|
|
#define MXT_VOLTAGE_DEFAULT 2700000
|
|
#define MXT_VOLTAGE_STEP 10000
|
|
|
|
/* Define for MXT_GEN_COMMAND_T6 */
|
|
#define MXT_BOOT_VALUE 0xa5
|
|
#define MXT_BACKUP_VALUE 0x55
|
|
#define MXT_BACKUP_TIME 25 /* msec */
|
|
#define MXT_RESET_TIME 65 /* msec */
|
|
|
|
#define MXT_FWRESET_TIME 175 /* msec */
|
|
|
|
/* Command to unlock bootloader */
|
|
#define MXT_UNLOCK_CMD_MSB 0xaa
|
|
#define MXT_UNLOCK_CMD_LSB 0xdc
|
|
|
|
/* Bootloader mode status */
|
|
#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
|
|
#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
|
|
#define MXT_FRAME_CRC_CHECK 0x02
|
|
#define MXT_FRAME_CRC_FAIL 0x03
|
|
#define MXT_FRAME_CRC_PASS 0x04
|
|
#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
|
|
#define MXT_BOOT_STATUS_MASK 0x3f
|
|
|
|
/* Touch status */
|
|
#define MXT_SUPPRESS (1 << 1)
|
|
#define MXT_AMP (1 << 2)
|
|
#define MXT_VECTOR (1 << 3)
|
|
#define MXT_MOVE (1 << 4)
|
|
#define MXT_RELEASE (1 << 5)
|
|
#define MXT_PRESS (1 << 6)
|
|
#define MXT_DETECT (1 << 7)
|
|
|
|
/* Touch orient bits */
|
|
#define MXT_XY_SWITCH (1 << 0)
|
|
#define MXT_X_INVERT (1 << 1)
|
|
#define MXT_Y_INVERT (1 << 2)
|
|
|
|
/* Touchscreen absolute values */
|
|
#define MXT_MAX_AREA 0xff
|
|
|
|
#define MXT_MAX_FINGER 10
|
|
|
|
struct mxt_info {
|
|
u8 family_id;
|
|
u8 variant_id;
|
|
u8 version;
|
|
u8 build;
|
|
u8 matrix_xsize;
|
|
u8 matrix_ysize;
|
|
u8 object_num;
|
|
};
|
|
|
|
struct mxt_object {
|
|
u8 type;
|
|
u16 start_address;
|
|
u8 size;
|
|
u8 instances;
|
|
u8 num_report_ids;
|
|
|
|
/* to map object and message */
|
|
u8 max_reportid;
|
|
};
|
|
|
|
struct mxt_message {
|
|
u8 reportid;
|
|
u8 message[7];
|
|
u8 checksum;
|
|
};
|
|
|
|
struct mxt_finger {
|
|
int status;
|
|
int x;
|
|
int y;
|
|
int area;
|
|
int pressure;
|
|
};
|
|
|
|
/* Each client has this additional data */
|
|
struct mxt_data {
|
|
struct i2c_client *client;
|
|
struct input_dev *input_dev;
|
|
const struct mxt_platform_data *pdata;
|
|
struct mxt_object *object_table;
|
|
struct mxt_info info;
|
|
struct mxt_finger finger[MXT_MAX_FINGER];
|
|
unsigned int irq;
|
|
unsigned int max_x;
|
|
unsigned int max_y;
|
|
};
|
|
|
|
static bool mxt_object_readable(unsigned int type)
|
|
{
|
|
switch (type) {
|
|
case MXT_GEN_MESSAGE_T5:
|
|
case MXT_GEN_COMMAND_T6:
|
|
case MXT_GEN_POWER_T7:
|
|
case MXT_GEN_ACQUIRE_T8:
|
|
case MXT_GEN_DATASOURCE_T53:
|
|
case MXT_TOUCH_MULTI_T9:
|
|
case MXT_TOUCH_KEYARRAY_T15:
|
|
case MXT_TOUCH_PROXIMITY_T23:
|
|
case MXT_TOUCH_PROXKEY_T52:
|
|
case MXT_PROCI_GRIPFACE_T20:
|
|
case MXT_PROCG_NOISE_T22:
|
|
case MXT_PROCI_ONETOUCH_T24:
|
|
case MXT_PROCI_TWOTOUCH_T27:
|
|
case MXT_PROCI_GRIP_T40:
|
|
case MXT_PROCI_PALM_T41:
|
|
case MXT_PROCI_TOUCHSUPPRESSION_T42:
|
|
case MXT_PROCI_STYLUS_T47:
|
|
case MXT_PROCG_NOISESUPPRESSION_T48:
|
|
case MXT_SPT_COMMSCONFIG_T18:
|
|
case MXT_SPT_GPIOPWM_T19:
|
|
case MXT_SPT_SELFTEST_T25:
|
|
case MXT_SPT_CTECONFIG_T28:
|
|
case MXT_SPT_USERDATA_T38:
|
|
case MXT_SPT_DIGITIZER_T43:
|
|
case MXT_SPT_CTECONFIG_T46:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool mxt_object_writable(unsigned int type)
|
|
{
|
|
switch (type) {
|
|
case MXT_GEN_COMMAND_T6:
|
|
case MXT_GEN_POWER_T7:
|
|
case MXT_GEN_ACQUIRE_T8:
|
|
case MXT_TOUCH_MULTI_T9:
|
|
case MXT_TOUCH_KEYARRAY_T15:
|
|
case MXT_TOUCH_PROXIMITY_T23:
|
|
case MXT_TOUCH_PROXKEY_T52:
|
|
case MXT_PROCI_GRIPFACE_T20:
|
|
case MXT_PROCG_NOISE_T22:
|
|
case MXT_PROCI_ONETOUCH_T24:
|
|
case MXT_PROCI_TWOTOUCH_T27:
|
|
case MXT_PROCI_GRIP_T40:
|
|
case MXT_PROCI_PALM_T41:
|
|
case MXT_PROCI_TOUCHSUPPRESSION_T42:
|
|
case MXT_PROCI_STYLUS_T47:
|
|
case MXT_PROCG_NOISESUPPRESSION_T48:
|
|
case MXT_SPT_COMMSCONFIG_T18:
|
|
case MXT_SPT_GPIOPWM_T19:
|
|
case MXT_SPT_SELFTEST_T25:
|
|
case MXT_SPT_CTECONFIG_T28:
|
|
case MXT_SPT_DIGITIZER_T43:
|
|
case MXT_SPT_CTECONFIG_T46:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void mxt_dump_message(struct device *dev,
|
|
struct mxt_message *message)
|
|
{
|
|
dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
|
|
dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
|
|
dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
|
|
dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
|
|
dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
|
|
dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
|
|
dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
|
|
dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
|
|
dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
|
|
}
|
|
|
|
static int mxt_check_bootloader(struct i2c_client *client,
|
|
unsigned int state)
|
|
{
|
|
u8 val;
|
|
|
|
recheck:
|
|
if (i2c_master_recv(client, &val, 1) != 1) {
|
|
dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
switch (state) {
|
|
case MXT_WAITING_BOOTLOAD_CMD:
|
|
case MXT_WAITING_FRAME_DATA:
|
|
val &= ~MXT_BOOT_STATUS_MASK;
|
|
break;
|
|
case MXT_FRAME_CRC_PASS:
|
|
if (val == MXT_FRAME_CRC_CHECK)
|
|
goto recheck;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (val != state) {
|
|
dev_err(&client->dev, "Unvalid bootloader mode state\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_unlock_bootloader(struct i2c_client *client)
|
|
{
|
|
u8 buf[2];
|
|
|
|
buf[0] = MXT_UNLOCK_CMD_LSB;
|
|
buf[1] = MXT_UNLOCK_CMD_MSB;
|
|
|
|
if (i2c_master_send(client, buf, 2) != 2) {
|
|
dev_err(&client->dev, "%s: i2c send failed\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_fw_write(struct i2c_client *client,
|
|
const u8 *data, unsigned int frame_size)
|
|
{
|
|
if (i2c_master_send(client, data, frame_size) != frame_size) {
|
|
dev_err(&client->dev, "%s: i2c send failed\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __mxt_read_reg(struct i2c_client *client,
|
|
u16 reg, u16 len, void *val)
|
|
{
|
|
struct i2c_msg xfer[2];
|
|
u8 buf[2];
|
|
|
|
buf[0] = reg & 0xff;
|
|
buf[1] = (reg >> 8) & 0xff;
|
|
|
|
/* Write register */
|
|
xfer[0].addr = client->addr;
|
|
xfer[0].flags = 0;
|
|
xfer[0].len = 2;
|
|
xfer[0].buf = buf;
|
|
|
|
/* Read data */
|
|
xfer[1].addr = client->addr;
|
|
xfer[1].flags = I2C_M_RD;
|
|
xfer[1].len = len;
|
|
xfer[1].buf = val;
|
|
|
|
if (i2c_transfer(client->adapter, xfer, 2) != 2) {
|
|
dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
|
|
{
|
|
return __mxt_read_reg(client, reg, 1, val);
|
|
}
|
|
|
|
static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
|
|
{
|
|
u8 buf[3];
|
|
|
|
buf[0] = reg & 0xff;
|
|
buf[1] = (reg >> 8) & 0xff;
|
|
buf[2] = val;
|
|
|
|
if (i2c_master_send(client, buf, 3) != 3) {
|
|
dev_err(&client->dev, "%s: i2c send failed\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_read_object_table(struct i2c_client *client,
|
|
u16 reg, u8 *object_buf)
|
|
{
|
|
return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE,
|
|
object_buf);
|
|
}
|
|
|
|
static struct mxt_object *
|
|
mxt_get_object(struct mxt_data *data, u8 type)
|
|
{
|
|
struct mxt_object *object;
|
|
int i;
|
|
|
|
for (i = 0; i < data->info.object_num; i++) {
|
|
object = data->object_table + i;
|
|
if (object->type == type)
|
|
return object;
|
|
}
|
|
|
|
dev_err(&data->client->dev, "Invalid object type\n");
|
|
return NULL;
|
|
}
|
|
|
|
static int mxt_read_message(struct mxt_data *data,
|
|
struct mxt_message *message)
|
|
{
|
|
struct mxt_object *object;
|
|
u16 reg;
|
|
|
|
object = mxt_get_object(data, MXT_GEN_MESSAGE_T5);
|
|
if (!object)
|
|
return -EINVAL;
|
|
|
|
reg = object->start_address;
|
|
return __mxt_read_reg(data->client, reg,
|
|
sizeof(struct mxt_message), message);
|
|
}
|
|
|
|
static int mxt_read_object(struct mxt_data *data,
|
|
u8 type, u8 offset, u8 *val)
|
|
{
|
|
struct mxt_object *object;
|
|
u16 reg;
|
|
|
|
object = mxt_get_object(data, type);
|
|
if (!object)
|
|
return -EINVAL;
|
|
|
|
reg = object->start_address;
|
|
return __mxt_read_reg(data->client, reg + offset, 1, val);
|
|
}
|
|
|
|
static int mxt_write_object(struct mxt_data *data,
|
|
u8 type, u8 offset, u8 val)
|
|
{
|
|
struct mxt_object *object;
|
|
u16 reg;
|
|
|
|
object = mxt_get_object(data, type);
|
|
if (!object)
|
|
return -EINVAL;
|
|
|
|
reg = object->start_address;
|
|
return mxt_write_reg(data->client, reg + offset, val);
|
|
}
|
|
|
|
static void mxt_input_report(struct mxt_data *data, int single_id)
|
|
{
|
|
struct mxt_finger *finger = data->finger;
|
|
struct input_dev *input_dev = data->input_dev;
|
|
int status = finger[single_id].status;
|
|
int finger_num = 0;
|
|
int id;
|
|
|
|
for (id = 0; id < MXT_MAX_FINGER; id++) {
|
|
if (!finger[id].status)
|
|
continue;
|
|
|
|
input_mt_slot(input_dev, id);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
|
|
finger[id].status != MXT_RELEASE);
|
|
|
|
if (finger[id].status != MXT_RELEASE) {
|
|
finger_num++;
|
|
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
|
|
finger[id].area);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_X,
|
|
finger[id].x);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_Y,
|
|
finger[id].y);
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE,
|
|
finger[id].pressure);
|
|
} else {
|
|
finger[id].status = 0;
|
|
}
|
|
}
|
|
|
|
input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
|
|
|
|
if (status != MXT_RELEASE) {
|
|
input_report_abs(input_dev, ABS_X, finger[single_id].x);
|
|
input_report_abs(input_dev, ABS_Y, finger[single_id].y);
|
|
input_report_abs(input_dev,
|
|
ABS_PRESSURE, finger[single_id].pressure);
|
|
}
|
|
|
|
input_sync(input_dev);
|
|
}
|
|
|
|
static void mxt_input_touchevent(struct mxt_data *data,
|
|
struct mxt_message *message, int id)
|
|
{
|
|
struct mxt_finger *finger = data->finger;
|
|
struct device *dev = &data->client->dev;
|
|
u8 status = message->message[0];
|
|
int x;
|
|
int y;
|
|
int area;
|
|
int pressure;
|
|
|
|
/* Check the touch is present on the screen */
|
|
if (!(status & MXT_DETECT)) {
|
|
if (status & MXT_RELEASE) {
|
|
dev_dbg(dev, "[%d] released\n", id);
|
|
|
|
finger[id].status = MXT_RELEASE;
|
|
mxt_input_report(data, id);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Check only AMP detection */
|
|
if (!(status & (MXT_PRESS | MXT_MOVE)))
|
|
return;
|
|
|
|
x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
|
|
y = (message->message[2] << 4) | ((message->message[3] & 0xf));
|
|
if (data->max_x < 1024)
|
|
x = x >> 2;
|
|
if (data->max_y < 1024)
|
|
y = y >> 2;
|
|
|
|
area = message->message[4];
|
|
pressure = message->message[5];
|
|
|
|
dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
|
|
status & MXT_MOVE ? "moved" : "pressed",
|
|
x, y, area);
|
|
|
|
finger[id].status = status & MXT_MOVE ?
|
|
MXT_MOVE : MXT_PRESS;
|
|
finger[id].x = x;
|
|
finger[id].y = y;
|
|
finger[id].area = area;
|
|
finger[id].pressure = pressure;
|
|
|
|
mxt_input_report(data, id);
|
|
}
|
|
|
|
static irqreturn_t mxt_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct mxt_data *data = dev_id;
|
|
struct mxt_message message;
|
|
struct mxt_object *object;
|
|
struct device *dev = &data->client->dev;
|
|
int id;
|
|
u8 reportid;
|
|
u8 max_reportid;
|
|
u8 min_reportid;
|
|
|
|
do {
|
|
if (mxt_read_message(data, &message)) {
|
|
dev_err(dev, "Failed to read message\n");
|
|
goto end;
|
|
}
|
|
|
|
reportid = message.reportid;
|
|
|
|
/* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
|
|
object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
|
|
if (!object)
|
|
goto end;
|
|
|
|
max_reportid = object->max_reportid;
|
|
min_reportid = max_reportid - object->num_report_ids + 1;
|
|
id = reportid - min_reportid;
|
|
|
|
if (reportid >= min_reportid && reportid <= max_reportid)
|
|
mxt_input_touchevent(data, &message, id);
|
|
else
|
|
mxt_dump_message(dev, &message);
|
|
} while (reportid != 0xff);
|
|
|
|
end:
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int mxt_check_reg_init(struct mxt_data *data)
|
|
{
|
|
const struct mxt_platform_data *pdata = data->pdata;
|
|
struct mxt_object *object;
|
|
struct device *dev = &data->client->dev;
|
|
int index = 0;
|
|
int i, j, config_offset;
|
|
|
|
if (!pdata->config) {
|
|
dev_dbg(dev, "No cfg data defined, skipping reg init\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < data->info.object_num; i++) {
|
|
object = data->object_table + i;
|
|
|
|
if (!mxt_object_writable(object->type))
|
|
continue;
|
|
|
|
for (j = 0;
|
|
j < (object->size + 1) * (object->instances + 1);
|
|
j++) {
|
|
config_offset = index + j;
|
|
if (config_offset > pdata->config_length) {
|
|
dev_err(dev, "Not enough config data!\n");
|
|
return -EINVAL;
|
|
}
|
|
mxt_write_object(data, object->type, j,
|
|
pdata->config[config_offset]);
|
|
}
|
|
index += (object->size + 1) * (object->instances + 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_make_highchg(struct mxt_data *data)
|
|
{
|
|
struct device *dev = &data->client->dev;
|
|
struct mxt_message message;
|
|
int count = 10;
|
|
int error;
|
|
|
|
/* Read dummy message to make high CHG pin */
|
|
do {
|
|
error = mxt_read_message(data, &message);
|
|
if (error)
|
|
return error;
|
|
} while (message.reportid != 0xff && --count);
|
|
|
|
if (!count) {
|
|
dev_err(dev, "CHG pin isn't cleared\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mxt_handle_pdata(struct mxt_data *data)
|
|
{
|
|
const struct mxt_platform_data *pdata = data->pdata;
|
|
u8 voltage;
|
|
|
|
/* Set touchscreen lines */
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE,
|
|
pdata->x_line);
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE,
|
|
pdata->y_line);
|
|
|
|
/* Set touchscreen orient */
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT,
|
|
pdata->orient);
|
|
|
|
/* Set touchscreen burst length */
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9,
|
|
MXT_TOUCH_BLEN, pdata->blen);
|
|
|
|
/* Set touchscreen threshold */
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9,
|
|
MXT_TOUCH_TCHTHR, pdata->threshold);
|
|
|
|
/* Set touchscreen resolution */
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9,
|
|
MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff);
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9,
|
|
MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8);
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9,
|
|
MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff);
|
|
mxt_write_object(data, MXT_TOUCH_MULTI_T9,
|
|
MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8);
|
|
|
|
/* Set touchscreen voltage */
|
|
if (pdata->voltage) {
|
|
if (pdata->voltage < MXT_VOLTAGE_DEFAULT) {
|
|
voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) /
|
|
MXT_VOLTAGE_STEP;
|
|
voltage = 0xff - voltage + 1;
|
|
} else
|
|
voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) /
|
|
MXT_VOLTAGE_STEP;
|
|
|
|
mxt_write_object(data, MXT_SPT_CTECONFIG_T28,
|
|
MXT_CTE_VOLTAGE, voltage);
|
|
}
|
|
}
|
|
|
|
static int mxt_get_info(struct mxt_data *data)
|
|
{
|
|
struct i2c_client *client = data->client;
|
|
struct mxt_info *info = &data->info;
|
|
int error;
|
|
u8 val;
|
|
|
|
error = mxt_read_reg(client, MXT_FAMILY_ID, &val);
|
|
if (error)
|
|
return error;
|
|
info->family_id = val;
|
|
|
|
error = mxt_read_reg(client, MXT_VARIANT_ID, &val);
|
|
if (error)
|
|
return error;
|
|
info->variant_id = val;
|
|
|
|
error = mxt_read_reg(client, MXT_VERSION, &val);
|
|
if (error)
|
|
return error;
|
|
info->version = val;
|
|
|
|
error = mxt_read_reg(client, MXT_BUILD, &val);
|
|
if (error)
|
|
return error;
|
|
info->build = val;
|
|
|
|
error = mxt_read_reg(client, MXT_OBJECT_NUM, &val);
|
|
if (error)
|
|
return error;
|
|
info->object_num = val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_get_object_table(struct mxt_data *data)
|
|
{
|
|
int error;
|
|
int i;
|
|
u16 reg;
|
|
u8 reportid = 0;
|
|
u8 buf[MXT_OBJECT_SIZE];
|
|
|
|
for (i = 0; i < data->info.object_num; i++) {
|
|
struct mxt_object *object = data->object_table + i;
|
|
|
|
reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i;
|
|
error = mxt_read_object_table(data->client, reg, buf);
|
|
if (error)
|
|
return error;
|
|
|
|
object->type = buf[0];
|
|
object->start_address = (buf[2] << 8) | buf[1];
|
|
object->size = buf[3];
|
|
object->instances = buf[4];
|
|
object->num_report_ids = buf[5];
|
|
|
|
if (object->num_report_ids) {
|
|
reportid += object->num_report_ids *
|
|
(object->instances + 1);
|
|
object->max_reportid = reportid;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_initialize(struct mxt_data *data)
|
|
{
|
|
struct i2c_client *client = data->client;
|
|
struct mxt_info *info = &data->info;
|
|
int error;
|
|
u8 val;
|
|
|
|
error = mxt_get_info(data);
|
|
if (error)
|
|
return error;
|
|
|
|
data->object_table = kcalloc(info->object_num,
|
|
sizeof(struct mxt_object),
|
|
GFP_KERNEL);
|
|
if (!data->object_table) {
|
|
dev_err(&client->dev, "Failed to allocate memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Get object table information */
|
|
error = mxt_get_object_table(data);
|
|
if (error)
|
|
return error;
|
|
|
|
/* Check register init values */
|
|
error = mxt_check_reg_init(data);
|
|
if (error)
|
|
return error;
|
|
|
|
mxt_handle_pdata(data);
|
|
|
|
/* Backup to memory */
|
|
mxt_write_object(data, MXT_GEN_COMMAND_T6,
|
|
MXT_COMMAND_BACKUPNV,
|
|
MXT_BACKUP_VALUE);
|
|
msleep(MXT_BACKUP_TIME);
|
|
|
|
/* Soft reset */
|
|
mxt_write_object(data, MXT_GEN_COMMAND_T6,
|
|
MXT_COMMAND_RESET, 1);
|
|
msleep(MXT_RESET_TIME);
|
|
|
|
/* Update matrix size at info struct */
|
|
error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
|
|
if (error)
|
|
return error;
|
|
info->matrix_xsize = val;
|
|
|
|
error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
|
|
if (error)
|
|
return error;
|
|
info->matrix_ysize = val;
|
|
|
|
dev_info(&client->dev,
|
|
"Family ID: %d Variant ID: %d Version: %d Build: %d\n",
|
|
info->family_id, info->variant_id, info->version,
|
|
info->build);
|
|
|
|
dev_info(&client->dev,
|
|
"Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
|
|
info->matrix_xsize, info->matrix_ysize,
|
|
info->object_num);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mxt_calc_resolution(struct mxt_data *data)
|
|
{
|
|
unsigned int max_x = data->pdata->x_size - 1;
|
|
unsigned int max_y = data->pdata->y_size - 1;
|
|
|
|
if (data->pdata->orient & MXT_XY_SWITCH) {
|
|
data->max_x = max_y;
|
|
data->max_y = max_x;
|
|
} else {
|
|
data->max_x = max_x;
|
|
data->max_y = max_y;
|
|
}
|
|
}
|
|
|
|
static ssize_t mxt_object_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mxt_data *data = dev_get_drvdata(dev);
|
|
struct mxt_object *object;
|
|
int count = 0;
|
|
int i, j;
|
|
int error;
|
|
u8 val;
|
|
|
|
for (i = 0; i < data->info.object_num; i++) {
|
|
object = data->object_table + i;
|
|
|
|
count += snprintf(buf + count, PAGE_SIZE - count,
|
|
"Object[%d] (Type %d)\n",
|
|
i + 1, object->type);
|
|
if (count >= PAGE_SIZE)
|
|
return PAGE_SIZE - 1;
|
|
|
|
if (!mxt_object_readable(object->type)) {
|
|
count += snprintf(buf + count, PAGE_SIZE - count,
|
|
"\n");
|
|
if (count >= PAGE_SIZE)
|
|
return PAGE_SIZE - 1;
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < object->size + 1; j++) {
|
|
error = mxt_read_object(data,
|
|
object->type, j, &val);
|
|
if (error)
|
|
return error;
|
|
|
|
count += snprintf(buf + count, PAGE_SIZE - count,
|
|
"\t[%2d]: %02x (%d)\n", j, val, val);
|
|
if (count >= PAGE_SIZE)
|
|
return PAGE_SIZE - 1;
|
|
}
|
|
|
|
count += snprintf(buf + count, PAGE_SIZE - count, "\n");
|
|
if (count >= PAGE_SIZE)
|
|
return PAGE_SIZE - 1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static int mxt_load_fw(struct device *dev, const char *fn)
|
|
{
|
|
struct mxt_data *data = dev_get_drvdata(dev);
|
|
struct i2c_client *client = data->client;
|
|
const struct firmware *fw = NULL;
|
|
unsigned int frame_size;
|
|
unsigned int pos = 0;
|
|
int ret;
|
|
|
|
ret = request_firmware(&fw, fn, dev);
|
|
if (ret) {
|
|
dev_err(dev, "Unable to open firmware %s\n", fn);
|
|
return ret;
|
|
}
|
|
|
|
/* Change to the bootloader mode */
|
|
mxt_write_object(data, MXT_GEN_COMMAND_T6,
|
|
MXT_COMMAND_RESET, MXT_BOOT_VALUE);
|
|
msleep(MXT_RESET_TIME);
|
|
|
|
/* Change to slave address of bootloader */
|
|
if (client->addr == MXT_APP_LOW)
|
|
client->addr = MXT_BOOT_LOW;
|
|
else
|
|
client->addr = MXT_BOOT_HIGH;
|
|
|
|
ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* Unlock bootloader */
|
|
mxt_unlock_bootloader(client);
|
|
|
|
while (pos < fw->size) {
|
|
ret = mxt_check_bootloader(client,
|
|
MXT_WAITING_FRAME_DATA);
|
|
if (ret)
|
|
goto out;
|
|
|
|
frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
|
|
|
|
/* We should add 2 at frame size as the the firmware data is not
|
|
* included the CRC bytes.
|
|
*/
|
|
frame_size += 2;
|
|
|
|
/* Write one frame to device */
|
|
mxt_fw_write(client, fw->data + pos, frame_size);
|
|
|
|
ret = mxt_check_bootloader(client,
|
|
MXT_FRAME_CRC_PASS);
|
|
if (ret)
|
|
goto out;
|
|
|
|
pos += frame_size;
|
|
|
|
dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size);
|
|
}
|
|
|
|
out:
|
|
release_firmware(fw);
|
|
|
|
/* Change to slave address of application */
|
|
if (client->addr == MXT_BOOT_LOW)
|
|
client->addr = MXT_APP_LOW;
|
|
else
|
|
client->addr = MXT_APP_HIGH;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t mxt_update_fw_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct mxt_data *data = dev_get_drvdata(dev);
|
|
int error;
|
|
|
|
disable_irq(data->irq);
|
|
|
|
error = mxt_load_fw(dev, MXT_FW_NAME);
|
|
if (error) {
|
|
dev_err(dev, "The firmware update failed(%d)\n", error);
|
|
count = error;
|
|
} else {
|
|
dev_dbg(dev, "The firmware update succeeded\n");
|
|
|
|
/* Wait for reset */
|
|
msleep(MXT_FWRESET_TIME);
|
|
|
|
kfree(data->object_table);
|
|
data->object_table = NULL;
|
|
|
|
mxt_initialize(data);
|
|
}
|
|
|
|
enable_irq(data->irq);
|
|
|
|
error = mxt_make_highchg(data);
|
|
if (error)
|
|
return error;
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
|
|
static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
|
|
|
|
static struct attribute *mxt_attrs[] = {
|
|
&dev_attr_object.attr,
|
|
&dev_attr_update_fw.attr,
|
|
NULL
|
|
};
|
|
|
|
static const struct attribute_group mxt_attr_group = {
|
|
.attrs = mxt_attrs,
|
|
};
|
|
|
|
static void mxt_start(struct mxt_data *data)
|
|
{
|
|
/* Touch enable */
|
|
mxt_write_object(data,
|
|
MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
|
|
}
|
|
|
|
static void mxt_stop(struct mxt_data *data)
|
|
{
|
|
/* Touch disable */
|
|
mxt_write_object(data,
|
|
MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
|
|
}
|
|
|
|
static int mxt_input_open(struct input_dev *dev)
|
|
{
|
|
struct mxt_data *data = input_get_drvdata(dev);
|
|
|
|
mxt_start(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mxt_input_close(struct input_dev *dev)
|
|
{
|
|
struct mxt_data *data = input_get_drvdata(dev);
|
|
|
|
mxt_stop(data);
|
|
}
|
|
|
|
static int __devinit mxt_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
const struct mxt_platform_data *pdata = client->dev.platform_data;
|
|
struct mxt_data *data;
|
|
struct input_dev *input_dev;
|
|
int error;
|
|
|
|
if (!pdata)
|
|
return -EINVAL;
|
|
|
|
data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
|
|
input_dev = input_allocate_device();
|
|
if (!data || !input_dev) {
|
|
dev_err(&client->dev, "Failed to allocate memory\n");
|
|
error = -ENOMEM;
|
|
goto err_free_mem;
|
|
}
|
|
|
|
input_dev->name = "Atmel maXTouch Touchscreen";
|
|
input_dev->id.bustype = BUS_I2C;
|
|
input_dev->dev.parent = &client->dev;
|
|
input_dev->open = mxt_input_open;
|
|
input_dev->close = mxt_input_close;
|
|
|
|
data->client = client;
|
|
data->input_dev = input_dev;
|
|
data->pdata = pdata;
|
|
data->irq = client->irq;
|
|
|
|
mxt_calc_resolution(data);
|
|
|
|
__set_bit(EV_ABS, input_dev->evbit);
|
|
__set_bit(EV_KEY, input_dev->evbit);
|
|
__set_bit(BTN_TOUCH, input_dev->keybit);
|
|
|
|
/* For single touch */
|
|
input_set_abs_params(input_dev, ABS_X,
|
|
0, data->max_x, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_Y,
|
|
0, data->max_y, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_PRESSURE,
|
|
0, 255, 0, 0);
|
|
|
|
/* For multi touch */
|
|
input_mt_init_slots(input_dev, MXT_MAX_FINGER);
|
|
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
|
0, MXT_MAX_AREA, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
|
0, data->max_x, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
|
0, data->max_y, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
|
|
0, 255, 0, 0);
|
|
|
|
input_set_drvdata(input_dev, data);
|
|
i2c_set_clientdata(client, data);
|
|
|
|
error = mxt_initialize(data);
|
|
if (error)
|
|
goto err_free_object;
|
|
|
|
error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
|
|
pdata->irqflags, client->dev.driver->name, data);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to register interrupt\n");
|
|
goto err_free_object;
|
|
}
|
|
|
|
error = mxt_make_highchg(data);
|
|
if (error)
|
|
goto err_free_irq;
|
|
|
|
error = input_register_device(input_dev);
|
|
if (error)
|
|
goto err_free_irq;
|
|
|
|
error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
|
|
if (error)
|
|
goto err_unregister_device;
|
|
|
|
return 0;
|
|
|
|
err_unregister_device:
|
|
input_unregister_device(input_dev);
|
|
input_dev = NULL;
|
|
err_free_irq:
|
|
free_irq(client->irq, data);
|
|
err_free_object:
|
|
kfree(data->object_table);
|
|
err_free_mem:
|
|
input_free_device(input_dev);
|
|
kfree(data);
|
|
return error;
|
|
}
|
|
|
|
static int __devexit mxt_remove(struct i2c_client *client)
|
|
{
|
|
struct mxt_data *data = i2c_get_clientdata(client);
|
|
|
|
sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
|
|
free_irq(data->irq, data);
|
|
input_unregister_device(data->input_dev);
|
|
kfree(data->object_table);
|
|
kfree(data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int mxt_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct mxt_data *data = i2c_get_clientdata(client);
|
|
struct input_dev *input_dev = data->input_dev;
|
|
|
|
mutex_lock(&input_dev->mutex);
|
|
|
|
if (input_dev->users)
|
|
mxt_stop(data);
|
|
|
|
mutex_unlock(&input_dev->mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct mxt_data *data = i2c_get_clientdata(client);
|
|
struct input_dev *input_dev = data->input_dev;
|
|
|
|
/* Soft reset */
|
|
mxt_write_object(data, MXT_GEN_COMMAND_T6,
|
|
MXT_COMMAND_RESET, 1);
|
|
|
|
msleep(MXT_RESET_TIME);
|
|
|
|
mutex_lock(&input_dev->mutex);
|
|
|
|
if (input_dev->users)
|
|
mxt_start(data);
|
|
|
|
mutex_unlock(&input_dev->mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops mxt_pm_ops = {
|
|
.suspend = mxt_suspend,
|
|
.resume = mxt_resume,
|
|
};
|
|
#endif
|
|
|
|
static const struct i2c_device_id mxt_id[] = {
|
|
{ "qt602240_ts", 0 },
|
|
{ "atmel_mxt_ts", 0 },
|
|
{ "mXT224", 0 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, mxt_id);
|
|
|
|
static struct i2c_driver mxt_driver = {
|
|
.driver = {
|
|
.name = "atmel_mxt_ts",
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_PM
|
|
.pm = &mxt_pm_ops,
|
|
#endif
|
|
},
|
|
.probe = mxt_probe,
|
|
.remove = __devexit_p(mxt_remove),
|
|
.id_table = mxt_id,
|
|
};
|
|
|
|
static int __init mxt_init(void)
|
|
{
|
|
return i2c_add_driver(&mxt_driver);
|
|
}
|
|
|
|
static void __exit mxt_exit(void)
|
|
{
|
|
i2c_del_driver(&mxt_driver);
|
|
}
|
|
|
|
module_init(mxt_init);
|
|
module_exit(mxt_exit);
|
|
|
|
/* Module information */
|
|
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
|
|
MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
|
|
MODULE_LICENSE("GPL");
|