forked from Minki/linux
8343bce195
There is a more complete atmel patch-series out by Nick Dyer that fixes this and other things, but in the meantime this is the minimal thing to get the touchscreen going on (at least my) Pixel Chromebook. Not that I want my dirty fingers near that beautiful screen, but it seems that a non-initialized touchscreen will also end up being a constant wakeup source, so you have to disable it to go to sleep. And it's easier to just fix the initialization sequence. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1343 lines
31 KiB
C
1343 lines
31 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_INFO 0x00
|
|
#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 50 /* msec */
|
|
#define MXT_RESET_TIME 200 /* msec */
|
|
|
|
#define MXT_FWRESET_TIME 175 /* msec */
|
|
|
|
/* MXT_SPT_GPIOPWM_T19 field */
|
|
#define MXT_GPIO0_MASK 0x04
|
|
#define MXT_GPIO1_MASK 0x08
|
|
#define MXT_GPIO2_MASK 0x10
|
|
#define MXT_GPIO3_MASK 0x20
|
|
|
|
/* 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_UNGRIP (1 << 0)
|
|
#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_PIXELS_PER_MM 20
|
|
|
|
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; /* Size of each instance - 1 */
|
|
u8 instances; /* Number of instances - 1 */
|
|
u8 num_report_ids;
|
|
} __packed;
|
|
|
|
struct mxt_message {
|
|
u8 reportid;
|
|
u8 message[7];
|
|
};
|
|
|
|
/* Each client has this additional data */
|
|
struct mxt_data {
|
|
struct i2c_client *client;
|
|
struct input_dev *input_dev;
|
|
char phys[64]; /* device physical location */
|
|
const struct mxt_platform_data *pdata;
|
|
struct mxt_object *object_table;
|
|
struct mxt_info info;
|
|
bool is_tp;
|
|
|
|
unsigned int irq;
|
|
unsigned int max_x;
|
|
unsigned int max_y;
|
|
|
|
/* Cached parameters from object table */
|
|
u8 T6_reportid;
|
|
u8 T9_reportid_min;
|
|
u8 T9_reportid_max;
|
|
u8 T19_reportid;
|
|
};
|
|
|
|
static bool mxt_object_readable(unsigned int type)
|
|
{
|
|
switch (type) {
|
|
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: %u\tmessage: %*ph\n",
|
|
message->reportid, 7, message->message);
|
|
}
|
|
|
|
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];
|
|
int ret;
|
|
|
|
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;
|
|
|
|
ret = i2c_transfer(client->adapter, xfer, 2);
|
|
if (ret == 2) {
|
|
ret = 0;
|
|
} else {
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
|
|
__func__, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
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, u16 len,
|
|
const void *val)
|
|
{
|
|
u8 *buf;
|
|
size_t count;
|
|
int ret;
|
|
|
|
count = len + 2;
|
|
buf = kmalloc(count, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
buf[0] = reg & 0xff;
|
|
buf[1] = (reg >> 8) & 0xff;
|
|
memcpy(&buf[2], val, len);
|
|
|
|
ret = i2c_master_send(client, buf, count);
|
|
if (ret == count) {
|
|
ret = 0;
|
|
} else {
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
dev_err(&client->dev, "%s: i2c send failed (%d)\n",
|
|
__func__, ret);
|
|
}
|
|
|
|
kfree(buf);
|
|
return ret;
|
|
}
|
|
|
|
static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
|
|
{
|
|
return __mxt_write_reg(client, reg, 1, &val);
|
|
}
|
|
|
|
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_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 || offset >= object->size + 1)
|
|
return -EINVAL;
|
|
|
|
reg = object->start_address;
|
|
return mxt_write_reg(data->client, reg + offset, val);
|
|
}
|
|
|
|
static void mxt_input_button(struct mxt_data *data, struct mxt_message *message)
|
|
{
|
|
struct input_dev *input = data->input_dev;
|
|
bool button;
|
|
int i;
|
|
|
|
/* Active-low switch */
|
|
for (i = 0; i < MXT_NUM_GPIO; i++) {
|
|
if (data->pdata->key_map[i] == KEY_RESERVED)
|
|
continue;
|
|
button = !(message->message[0] & MXT_GPIO0_MASK << i);
|
|
input_report_key(input, data->pdata->key_map[i], button);
|
|
}
|
|
}
|
|
|
|
static void mxt_input_touchevent(struct mxt_data *data,
|
|
struct mxt_message *message, int id)
|
|
{
|
|
struct device *dev = &data->client->dev;
|
|
u8 status = message->message[0];
|
|
struct input_dev *input_dev = data->input_dev;
|
|
int x;
|
|
int y;
|
|
int area;
|
|
int pressure;
|
|
|
|
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,
|
|
"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
|
|
id,
|
|
(status & MXT_DETECT) ? 'D' : '.',
|
|
(status & MXT_PRESS) ? 'P' : '.',
|
|
(status & MXT_RELEASE) ? 'R' : '.',
|
|
(status & MXT_MOVE) ? 'M' : '.',
|
|
(status & MXT_VECTOR) ? 'V' : '.',
|
|
(status & MXT_AMP) ? 'A' : '.',
|
|
(status & MXT_SUPPRESS) ? 'S' : '.',
|
|
(status & MXT_UNGRIP) ? 'U' : '.',
|
|
x, y, area, pressure);
|
|
|
|
input_mt_slot(input_dev, id);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
|
|
status & MXT_DETECT);
|
|
|
|
if (status & MXT_DETECT) {
|
|
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
|
|
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
|
|
}
|
|
}
|
|
|
|
static unsigned mxt_extract_T6_csum(const u8 *csum)
|
|
{
|
|
return csum[0] | (csum[1] << 8) | (csum[2] << 16);
|
|
}
|
|
|
|
static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg)
|
|
{
|
|
u8 id = msg->reportid;
|
|
return (id >= data->T9_reportid_min && id <= data->T9_reportid_max);
|
|
}
|
|
|
|
static irqreturn_t mxt_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct mxt_data *data = dev_id;
|
|
struct mxt_message message;
|
|
const u8 *payload = &message.message[0];
|
|
struct device *dev = &data->client->dev;
|
|
u8 reportid;
|
|
bool update_input = false;
|
|
|
|
do {
|
|
if (mxt_read_message(data, &message)) {
|
|
dev_err(dev, "Failed to read message\n");
|
|
goto end;
|
|
}
|
|
|
|
reportid = message.reportid;
|
|
|
|
if (reportid == data->T6_reportid) {
|
|
u8 status = payload[0];
|
|
unsigned csum = mxt_extract_T6_csum(&payload[1]);
|
|
dev_dbg(dev, "Status: %02x Config Checksum: %06x\n",
|
|
status, csum);
|
|
} else if (mxt_is_T9_message(data, &message)) {
|
|
int id = reportid - data->T9_reportid_min;
|
|
mxt_input_touchevent(data, &message, id);
|
|
update_input = true;
|
|
} else if (message.reportid == data->T19_reportid) {
|
|
mxt_input_button(data, &message);
|
|
update_input = true;
|
|
} else {
|
|
mxt_dump_message(dev, &message);
|
|
}
|
|
} while (reportid != 0xff);
|
|
|
|
if (update_input) {
|
|
input_mt_report_pointer_emulation(data->input_dev, false);
|
|
input_sync(data->input_dev);
|
|
}
|
|
|
|
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, size;
|
|
int ret;
|
|
|
|
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;
|
|
|
|
size = (object->size + 1) * (object->instances + 1);
|
|
if (index + size > pdata->config_length) {
|
|
dev_err(dev, "Not enough config data!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = __mxt_write_reg(data->client, object->start_address,
|
|
size, &pdata->config[index]);
|
|
if (ret)
|
|
return ret;
|
|
index += size;
|
|
}
|
|
|
|
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;
|
|
|
|
/* Read 7-byte info block starting at address 0 */
|
|
error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info);
|
|
if (error)
|
|
return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mxt_get_object_table(struct mxt_data *data)
|
|
{
|
|
struct i2c_client *client = data->client;
|
|
size_t table_size;
|
|
int error;
|
|
int i;
|
|
u8 reportid;
|
|
|
|
table_size = data->info.object_num * sizeof(struct mxt_object);
|
|
error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
|
|
data->object_table);
|
|
if (error)
|
|
return error;
|
|
|
|
/* Valid Report IDs start counting from 1 */
|
|
reportid = 1;
|
|
for (i = 0; i < data->info.object_num; i++) {
|
|
struct mxt_object *object = data->object_table + i;
|
|
u8 min_id, max_id;
|
|
|
|
le16_to_cpus(&object->start_address);
|
|
|
|
if (object->num_report_ids) {
|
|
min_id = reportid;
|
|
reportid += object->num_report_ids *
|
|
(object->instances + 1);
|
|
max_id = reportid - 1;
|
|
} else {
|
|
min_id = 0;
|
|
max_id = 0;
|
|
}
|
|
|
|
dev_dbg(&data->client->dev,
|
|
"Type %2d Start %3d Size %3d Instances %2d ReportIDs %3u : %3u\n",
|
|
object->type, object->start_address, object->size + 1,
|
|
object->instances + 1, min_id, max_id);
|
|
|
|
switch (object->type) {
|
|
case MXT_GEN_COMMAND_T6:
|
|
data->T6_reportid = min_id;
|
|
break;
|
|
case MXT_TOUCH_MULTI_T9:
|
|
data->T9_reportid_min = min_id;
|
|
data->T9_reportid_max = max_id;
|
|
break;
|
|
case MXT_SPT_GPIOPWM_T19:
|
|
data->T19_reportid = min_id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mxt_free_object_table(struct mxt_data *data)
|
|
{
|
|
kfree(data->object_table);
|
|
data->object_table = NULL;
|
|
data->T6_reportid = 0;
|
|
data->T9_reportid_min = 0;
|
|
data->T9_reportid_max = 0;
|
|
data->T19_reportid = 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)
|
|
goto err_free_object_table;
|
|
|
|
/* Check register init values */
|
|
error = mxt_check_reg_init(data);
|
|
if (error)
|
|
goto err_free_object_table;
|
|
|
|
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)
|
|
goto err_free_object_table;
|
|
info->matrix_xsize = val;
|
|
|
|
error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
|
|
if (error)
|
|
goto err_free_object_table;
|
|
info->matrix_ysize = val;
|
|
|
|
dev_info(&client->dev,
|
|
"Family ID: %u Variant ID: %u Major.Minor.Build: %u.%u.%02X\n",
|
|
info->family_id, info->variant_id, info->version >> 4,
|
|
info->version & 0xf, info->build);
|
|
|
|
dev_info(&client->dev,
|
|
"Matrix X Size: %u Matrix Y Size: %u Object Num: %u\n",
|
|
info->matrix_xsize, info->matrix_ysize,
|
|
info->object_num);
|
|
|
|
return 0;
|
|
|
|
err_free_object_table:
|
|
mxt_free_object_table(data);
|
|
return error;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* Firmware Version is returned as Major.Minor.Build */
|
|
static ssize_t mxt_fw_version_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mxt_data *data = dev_get_drvdata(dev);
|
|
struct mxt_info *info = &data->info;
|
|
return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
|
|
info->version >> 4, info->version & 0xf, info->build);
|
|
}
|
|
|
|
/* Hardware Version is returned as FamilyID.VariantID */
|
|
static ssize_t mxt_hw_version_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct mxt_data *data = dev_get_drvdata(dev);
|
|
struct mxt_info *info = &data->info;
|
|
return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
|
|
info->family_id, info->variant_id);
|
|
}
|
|
|
|
static ssize_t mxt_show_instance(char *buf, int count,
|
|
struct mxt_object *object, int instance,
|
|
const u8 *val)
|
|
{
|
|
int i;
|
|
|
|
if (object->instances > 0)
|
|
count += scnprintf(buf + count, PAGE_SIZE - count,
|
|
"Instance %u\n", instance);
|
|
|
|
for (i = 0; i < object->size + 1; i++)
|
|
count += scnprintf(buf + count, PAGE_SIZE - count,
|
|
"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
|
|
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
|
|
|
|
return count;
|
|
}
|
|
|
|
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 *obuf;
|
|
|
|
/* Pre-allocate buffer large enough to hold max sized object. */
|
|
obuf = kmalloc(256, GFP_KERNEL);
|
|
if (!obuf)
|
|
return -ENOMEM;
|
|
|
|
error = 0;
|
|
for (i = 0; i < data->info.object_num; i++) {
|
|
object = data->object_table + i;
|
|
|
|
if (!mxt_object_readable(object->type))
|
|
continue;
|
|
|
|
count += scnprintf(buf + count, PAGE_SIZE - count,
|
|
"T%u:\n", object->type);
|
|
|
|
for (j = 0; j < object->instances + 1; j++) {
|
|
u16 size = object->size + 1;
|
|
u16 addr = object->start_address + j * size;
|
|
|
|
error = __mxt_read_reg(data->client, addr, size, obuf);
|
|
if (error)
|
|
goto done;
|
|
|
|
count = mxt_show_instance(buf, count, object, j, obuf);
|
|
}
|
|
}
|
|
|
|
done:
|
|
kfree(obuf);
|
|
return error ?: 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);
|
|
|
|
mxt_free_object_table(data);
|
|
|
|
mxt_initialize(data);
|
|
}
|
|
|
|
enable_irq(data->irq);
|
|
|
|
error = mxt_make_highchg(data);
|
|
if (error)
|
|
return error;
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
|
|
static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
|
|
static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
|
|
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
|
|
|
|
static struct attribute *mxt_attrs[] = {
|
|
&dev_attr_fw_version.attr,
|
|
&dev_attr_hw_version.attr,
|
|
&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 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;
|
|
unsigned int num_mt_slots;
|
|
|
|
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;
|
|
}
|
|
|
|
data->is_tp = pdata && pdata->is_tp;
|
|
|
|
input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" :
|
|
"Atmel maXTouch Touchscreen";
|
|
snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
|
|
client->adapter->nr, client->addr);
|
|
|
|
input_dev->phys = data->phys;
|
|
|
|
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);
|
|
|
|
error = mxt_initialize(data);
|
|
if (error)
|
|
goto err_free_mem;
|
|
|
|
__set_bit(EV_ABS, input_dev->evbit);
|
|
__set_bit(EV_KEY, input_dev->evbit);
|
|
__set_bit(BTN_TOUCH, input_dev->keybit);
|
|
|
|
if (data->is_tp) {
|
|
int i;
|
|
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
|
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
|
|
|
|
for (i = 0; i < MXT_NUM_GPIO; i++)
|
|
if (pdata->key_map[i] != KEY_RESERVED)
|
|
__set_bit(pdata->key_map[i], input_dev->keybit);
|
|
|
|
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
|
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
|
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
|
|
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
|
|
__set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit);
|
|
|
|
input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
|
|
input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
|
|
input_abs_set_res(input_dev, ABS_MT_POSITION_X,
|
|
MXT_PIXELS_PER_MM);
|
|
input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
|
|
MXT_PIXELS_PER_MM);
|
|
}
|
|
|
|
/* 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 */
|
|
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
|
|
error = input_mt_init_slots(input_dev, num_mt_slots, 0);
|
|
if (error)
|
|
goto err_free_object;
|
|
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 = request_threaded_irq(client->irq, NULL, mxt_interrupt,
|
|
pdata->irqflags | IRQF_ONESHOT,
|
|
client->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 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_SLEEP
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
|
|
|
|
static const struct i2c_device_id mxt_id[] = {
|
|
{ "qt602240_ts", 0 },
|
|
{ "atmel_mxt_ts", 0 },
|
|
{ "atmel_mxt_tp", 0 },
|
|
{ "mXT224", 0 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, mxt_id);
|
|
|
|
static struct i2c_driver mxt_driver = {
|
|
.driver = {
|
|
.name = "atmel_mxt_ts",
|
|
.owner = THIS_MODULE,
|
|
.pm = &mxt_pm_ops,
|
|
},
|
|
.probe = mxt_probe,
|
|
.remove = mxt_remove,
|
|
.id_table = mxt_id,
|
|
};
|
|
|
|
module_i2c_driver(mxt_driver);
|
|
|
|
/* Module information */
|
|
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
|
|
MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
|
|
MODULE_LICENSE("GPL");
|