Hi, On Fri, Nov 30, 2012 at 09:18:54AM -0500, Li Wu wrote: > This is a initial driver for STMicroelectronics multi touch > capacitive touchscren FingertipK. It supports maximum 10 fingers, > based on I2C interface. > > Tested on Beagleboard, Android ICS. > > Signed-off-by: Li Wu <willy.woo@xxxxxxxxx> > --- > MAINTAINERS | 8 + > drivers/input/touchscreen/Kconfig | 11 + > drivers/input/touchscreen/Makefile | 1 + > drivers/input/touchscreen/ftk.c | 792 ++++++++++++++++++++++++++++++++++++ > 4 files changed, 812 insertions(+), 0 deletions(-) > create mode 100644 drivers/input/touchscreen/ftk.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 9386a63..3f5398f 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -314,6 +314,14 @@ W: http://wiki.analog.com/AD7879 > S: Supported > F: drivers/input/touchscreen/ad7879.c > > +STM FTK TOUCHSCREEN DRIVER > +M: Li Wu <li.wu@xxxxxx> > +L: device-drivers-devel@xxxxxxxxxxxxxxxxxxxx > +W: http://www.st.com > +S: Supported > +F: drivers/input/touchscreen/ftk.c > + > + > ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR) > M: Jiri Kosina <jkosina@xxxxxxx> > S: Maintained > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig > index f7668b2..c81e2e7 100644 > --- a/drivers/input/touchscreen/Kconfig > +++ b/drivers/input/touchscreen/Kconfig > @@ -900,4 +900,15 @@ config TOUCHSCREEN_TPS6507X > To compile this driver as a module, choose M here: the > module will be called tps6507x_ts. > > +config TOUCHSCREEN_FTK > + tristate "STMicroelectronics i2c multitouch touchscreen with FingerTipK" > + depends on I2C > + help > + Say Y here to enable STMicroelectronics touchscreen support. > + > + If unsure, say N. > + > + To compile this driver as a module, choose M here: the > + module will be called STM_ts. > + > endif > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile > index 178eb12..6feba9b 100644 > --- a/drivers/input/touchscreen/Makefile > +++ b/drivers/input/touchscreen/Makefile > @@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o > obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o > obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o > obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o > +obj-$(CONFIG_TOUCHSCREEN_FTK) += ftk.o > diff --git a/drivers/input/touchscreen/ftk.c b/drivers/input/touchscreen/ftk.c > new file mode 100644 > index 0000000..7c995f6 > --- /dev/null > +++ b/drivers/input/touchscreen/ftk.c > @@ -0,0 +1,792 @@ > +/* > + * drivers/input/touchscreen/ftk.c > + * > + * Driver for STMicroelectronics FTK capacity touchscreen > + * > + * Author: JH Jang <jh.jang@xxxxxx> > + * Victor Phay <victor.phay@xxxxxx> > + * Li Wu <li.wu@xxxxxx>, <willy.woo@xxxxxxxxx> > + * > + * Copyright (c) 2012 STMicroelectronics Limited > + * > + * 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/ctype.h> > +#include <linux/delay.h> > +#include <linux/errno.h> > +#include <linux/firmware.h> > +#include <linux/fs.h> > +#include <linux/gpio.h> > +#include <linux/hrtimer.h> > +#include <linux/i2c.h> > +#include <linux/i2c-dev.h> > +#include <linux/init.h> > +#include <linux/input.h> > +#include <linux/interrupt.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/serio.h> > +#include <linux/slab.h> > +#include <linux/uaccess.h> > + > +/* > + * Definitions & global arrays. > + */ > +#define DRIVER_DESC "ftk i2c touchscreen driver" > +#define ftk_TS_DRV_NAME "ftk" > +#define X_AXIS_MAX 800 > +#define X_AXIS_MIN 0 > +#define Y_AXIS_MAX 480 > +#define Y_AXIS_MIN 0 > +#define PRESSURE_MIN 0 > +#define PRESSURE_MAX 256 > +#define P70_PATCH_ADDR_START 0x00420000 > +#define LEAVE_EVENT 0x04 > +#define ENTER_EVENT 0x03 > +#define MOTION_EVENT 0x05 > +#define RESET_EVENT 0x10 > +#define MAX_SUPPORTED_FINGERS 10 > +#define MAX_TRANSACTION_LENGTH 8 > + > +static struct i2c_driver stm_ts_driver; > +static struct workqueue_struct *stmtouch_wq; > +static int cor_xyz[10][3]; > +static unsigned char ID_Indx[10] = { no CaMeLcAsE please. > + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > +}; no need to initialize this since it's static. > + > +struct B0_write { no CaMeLcAsE please. > + u8 addr; > + u8 val; > +}; > + > +struct ftk_i2c_platform_data { > + int (*power)(int on); > +}; > + > +struct ftk_ts { > + struct device *dev; > + struct i2c_client *client; > + struct input_dev *input_dev; > + struct hrtimer timer; > + struct work_struct work; > + spinlock_t lock; > + int x; > + int y; > + int z; > + int irq; > + int (*power)(int on); > +#ifdef CONFIG_HAS_EARLYSUSPEND > + struct early_suspend early_suspend; > +#endif I can't find the definition for struct early_suspend anywhere, not even on linux-next. Can you point me to the patch adding it ? > +}; > + > +#ifdef CONFIG_HAS_EARLYSUSPEND > +static void stm_ts_early_suspend(struct early_suspend *h); > +static void stm_ts_late_resume(struct early_suspend *h); > +#endif > + > +static int ftk_write_reg(struct ftk_ts *ftk_ts, u8 *reg, u16 num_com); > +static int ftk_read_reg(struct ftk_ts *ftk_ts, u8 *reg, int cnum, u8 *buf, > + int num); > +static void touch_ON(struct ftk_ts *ftkts, int x, int y, int z, int id); > +static void touch_OFF(struct ftk_ts *ftkts); > +static u8 load_config(struct ftk_ts *ftk_ts, const struct firmware *firmware); > +static u8 load_patch(struct ftk_ts *ftk_ts, const struct firmware *firmware); > +static int verify_firmware(const struct firmware *firmware); > +static int init_ftk(struct ftk_ts *ftk_ts); > +static enum hrtimer_restart st_ts_timer_func(struct hrtimer *timer); > +static irqreturn_t ts_interrupt(int irq, void *handle); > +static u8 decode_data_packet(struct ftk_ts *ftkts, unsigned char data[], > + unsigned char LeftEvent); > +static void ts_tasklet_proc(struct work_struct *work); > +static int stm_ts_probe(struct i2c_client *client, > + const struct i2c_device_id *idp); > +static int stm_ts_remove(struct i2c_client *client); > +static int stm_ts_suspend(struct i2c_client *client, pm_message_t mesg); > +static int stm_ts_resume(struct i2c_client *client); > +static int __init stm_ts_init(void); > +static void __exit stm_ts_exit(void); make sure to organize your source code correctly so you don't need to declare a prototype for every single function here. > +static int ftk_write_reg(struct ftk_ts *ftk_ts, u8 *reg, u16 num_com) > +{ > + struct i2c_msg xfer_msg[2]; > + > + xfer_msg[0].addr = ftk_ts->client->addr; > + xfer_msg[0].len = num_com; > + xfer_msg[0].flags = 0; > + xfer_msg[0].buf = reg; > + > + return i2c_transfer(ftk_ts->client->adapter, xfer_msg, 1); Doesn't this controller support smbus ? > +} > + > + one blank line only. > +static int ftk_read_reg(struct ftk_ts *ftk_ts, u8 *reg, int cnum, u8 *buf, > + int num) > +{ > + u16 left = num; > + u16 offset = 0; > + > + struct i2c_msg xfer_msg[2]; > + > + xfer_msg[0].addr = ftk_ts->client->addr; > + xfer_msg[0].len = cnum; > + xfer_msg[0].flags = 0; > + xfer_msg[0].buf = reg; > + > + xfer_msg[1].addr = ftk_ts->client->addr; > + xfer_msg[1].flags = I2C_M_RD; > + > + /* MTK platform */ > + /* Can only transfer 8 bytes per transaction */ > + while (left > 0) { > + xfer_msg[1].buf = &buf[offset]; > + > + if (left > MAX_TRANSACTION_LENGTH) { > + xfer_msg[1].len = MAX_TRANSACTION_LENGTH; > + left -= MAX_TRANSACTION_LENGTH; > + offset += MAX_TRANSACTION_LENGTH; > + } else { > + xfer_msg[1].len = left; > + left = 0; > + } > + > + if (i2c_transfer(ftk_ts->client->adapter, xfer_msg, 2) != 2) { > + dev_err(ftk_ts->dev, "FTK - i2c Transfer error!\n"); > + return 1; > + } > + } > + > + return 0; > +} > + > + > +static void touch_ON(struct ftk_ts *ftkts, int x, int y, int z, int id) > +{ > + input_report_key(ftkts->input_dev, BTN_TOUCH, 1); > + input_report_abs(ftkts->input_dev, ABS_MT_POSITION_X, x); > + input_report_abs(ftkts->input_dev, ABS_MT_POSITION_Y, y); > + input_report_abs(ftkts->input_dev, ABS_MT_TOUCH_MAJOR, z); > + input_report_abs(ftkts->input_dev, ABS_MT_TRACKING_ID, id); > + input_mt_sync(ftkts->input_dev); > +} > + > + > +static void touch_OFF(struct ftk_ts *ftkts) all lower cases. > +{ > + input_report_key(ftkts->input_dev, BTN_TOUCH, 0); > + input_report_abs(ftkts->input_dev, ABS_MT_TRACKING_ID, 0); > + input_mt_sync(ftkts->input_dev); > +} > + > + > +static u8 load_config(struct ftk_ts *ftk_ts, const struct firmware *firmware) > +{ > + u16 one_group_length = 0; > + u16 patch_length = 0; > + u16 config_length = 0; > + u16 i = 0; > + u8 *pdata; > + u8 regAdd[8]; > + u8 val[8]; > + int ret; > + > + patch_length = firmware->data[0] * 256 + firmware->data[1]; > + config_length = > + firmware->data[patch_length + 2] * 256 + > + firmware->data[patch_length + 2 + 1]; > + pdata = (u8 *)&firmware->data[patch_length + 4]; > + > + while (i < config_length) { > + one_group_length = pdata[i] * 256 + pdata[i + 1]; > + > + if ((pdata[i + 2] == 0xFF) && (pdata[i + 3] == 0xFF)) > + mdelay(pdata[i + 4]); > + else{ > + ftk_write_reg(ftk_ts, &(pdata[i + 2]), > + one_group_length); > + mdelay(100); > + } > + > + i = i + 2; > + i = i + one_group_length; > + } > + > + regAdd[0] = 0xB0; > + regAdd[1] = 0x05; /* Set Interrupt Polarity */ > + regAdd[2] = 0x00; /* '00' - level interrupt */ > + /* '02' - edge interrupt */ > + ftk_write_reg(ftk_ts, ®Add[0], 3); > + mdelay(5); > + > + regAdd[0] = 0xB0; > + regAdd[1] = 0x06; /* Enable Touch Detect Interrupt */ > + regAdd[2] = 0x40; /* 0xC0 */ > + ftk_write_reg(ftk_ts, ®Add[0], 3); > + mdelay(5); > + > + regAdd[0] = 0xB0; > + regAdd[1] = 0x07; /* Read 0x07 to clear ISR */ > + ret = ftk_read_reg(ftk_ts, ®Add[0], 2, &val[0], 1); > + mdelay(5); > + > + regAdd[0] = 0x85; > + ret = ftk_read_reg(ftk_ts, ®Add[0], 1, &val[0], 8); > + mdelay(20); > + > + regAdd[0] = 0x85; > + ret = ftk_read_reg(ftk_ts, ®Add[0], 1, &val[0], 8); > + mdelay(20); > + > + regAdd[0] = 0xB0; > + regAdd[1] = 0x03; > + ret = ftk_read_reg(ftk_ts, regAdd, 2, val, 1); > + mdelay(5); > + dev_info(ftk_ts->dev, "Patch loaded, Version =%X\n", val[0]); > + > + regAdd[0] = 0x83; /* TS Sense on */ > + regAdd[1] = 0x00; no magic constants, ever. Please define symbolic constants for everything. > + ret = ftk_write_reg(ftk_ts, ®Add[0], 1); > + mdelay(5); > + return ret; > +} > + > + > +static u8 load_patch(struct ftk_ts *ftk_ts, const struct firmware *firmware) > +{ > + u32 writeAddr, j = 0, i = 0; > + u16 patch_length = 0; > + u8 byteWork1[256 + 3] = { 0 }; > + u8 regAdd[3] = { 0 }; > + > + patch_length = firmware->data[0] * 256 + firmware->data[1]; > + > + while (j < patch_length) { > + writeAddr = P70_PATCH_ADDR_START + j; > + > + regAdd[0] = 0xB3; > + regAdd[1] = (writeAddr >> 24) & 0xFF; > + regAdd[2] = (writeAddr >> 16) & 0xFF; > + ftk_write_reg(ftk_ts, ®Add[0], 3); > + > + byteWork1[0] = 0xB1; > + byteWork1[1] = (writeAddr >> 8) & 0xFF; > + byteWork1[2] = writeAddr & 0xFF; > + > + i = 0; > + while ((j < firmware->size) && (i < 256)) { > + byteWork1[i + 3] = firmware->data[j + 2]; > + i++; > + j++; > + } > + ftk_write_reg(ftk_ts, &byteWork1[0], 256 + 3); > + } > + > + return 0; > +} > + > + > +static int verify_firmware(const struct firmware *firmware) > +{ > + u16 firmware_length; > + u16 patch_length; > + u16 config_length; > + > + firmware_length = firmware->size; > + patch_length = firmware->data[0] * 256 + firmware->data[1]; > + config_length = > + firmware->data[patch_length + 2] * 256 + > + firmware->data[patch_length + 2 + 1]; > + > + if (firmware_length == patch_length + config_length + 4) > + return 0; > + else > + return -1; > +} > + > + > +static int init_ftk(struct ftk_ts *ftk_ts) > +{ > + const struct firmware *firmware; > + u8 regAdd[7]; > + u8 val[8]; > + int ret; > + > + regAdd[0] = 0xB0; > + regAdd[1] = 0x00; > + ret = ftk_read_reg(ftk_ts, regAdd, 2, val, 3); > + > + if (ret < 0) > + dev_err(ftk_ts->dev, " i2c_transfer failed\n"); why the space before the actual message ? > + > + mdelay(1); > + dev_info(ftk_ts->dev, "Chip ID = %x %x %x\n", val[0], val[1], val[2]); how about dev_dbg() ? > + > + regAdd[0] = 0x9E; /* TS Soft Reset */ > + ret = ftk_write_reg(ftk_ts, ®Add[0], 1); > + mdelay(1); > + > + ret = request_firmware(&firmware, "ftk/ftk.bin", ftk_ts->dev); > + if (ret < 0) > + dev_err(ftk_ts->dev, " request fw fail ,err = %d\n", ret); > + else{ > + ret = verify_firmware(firmware); > + if (ret == 0) { > + load_patch(ftk_ts, firmware); > + load_config(ftk_ts, firmware); > + } > + } istead of if...else you can exit early if request_firmware() fails. That will decrease the indentation a bit. > + > + release_firmware(firmware); > + > + regAdd[0] = 0xB3; > + regAdd[1] = 0xFF; > + regAdd[2] = 0xFF; > + ftk_write_reg(ftk_ts, ®Add[0], 3); > + mdelay(5); you need to add a comment to this mdelay(). > + > + regAdd[0] = 0xA0; > + ftk_write_reg(ftk_ts, ®Add[0], 1); > + mdelay(5); > + > + if (ret < 0) > + dev_err(ftk_ts->dev, "ftk Not Initialised\n"); > + else > + dev_info(ftk_ts->dev, "ftk Initialised\n"); dev_dbg() ? > + > + return 0; > +} > + > + one blank line only (ditto to all below) > +static enum hrtimer_restart st_ts_timer_func(struct hrtimer *timer) > +{ > + struct ftk_ts *ftkts = container_of(timer, struct ftk_ts, timer); > + > + queue_work(stmtouch_wq, &ftkts->work); > + return HRTIMER_NORESTART; > +} > + > + > +static irqreturn_t ts_interrupt(int irq, void *handle) > +{ > + struct ftk_ts *ftk_ts = handle; > + > + disable_irq_nosync(ftk_ts->client->irq); > + queue_work(stmtouch_wq, &ftk_ts->work); really ??? you need to use threaded IRQ instead. > + return IRQ_HANDLED; > +} > + > + > +static u8 decode_data_packet(struct ftk_ts *ftkts, unsigned char data[], > + unsigned char LeftEvent) > +{ > + u8 EventNum; > + u8 TouchID, EventID; > + u8 LastLeftEvent = 0; > + u8 i, num_released_finger; > + u8 valid_id = 0; > + > + for (EventNum = 0; EventNum < LeftEvent; EventNum++) { no CaMeLcAsE (ditto to all other instances of this). > + LastLeftEvent = data[7 + EventNum * 8] & 0x0F; > + TouchID = data[1 + EventNum * 8] & 0x0F; > + EventID = data[EventNum * 8] & 0xFF; > + > + if ((EventID == LEAVE_EVENT) || (EventID == ENTER_EVENT) || > + (EventID == MOTION_EVENT)) { > + /* Enter, Leave or Motion Event */ > + > + if (TouchID < MAX_SUPPORTED_FINGERS) { > + valid_id = 1; > + > + ID_Indx[TouchID] = EventID; > + cor_xyz[TouchID][0] = > + ((data[4 + EventNum * > + 8] & > + 0xF0) >> > + 4) | ((data[2 + EventNum * 8]) << 4); > + cor_xyz[TouchID][1] = > + ((data[4 + EventNum * > + 8] & > + 0x0F) | > + ((data[3 + EventNum * 8]) << 4)); > + cor_xyz[TouchID][2] = data[5 + EventNum * 8]; > + } > + } else if (EventID == RESET_EVENT) { > + /* Reset happened */ > + for (i = 0; i < MAX_SUPPORTED_FINGERS; i++) > + ID_Indx[i] = 0; > + > + touch_OFF(ftkts); > + > + input_sync(ftkts->input_dev); > + init_ftk(ftkts); > + return 0; > + } > + } > + > + if (valid_id) { > + /* Report all fingers on panel */ > + /* ---------------------------------- */ > + num_released_finger = 0; > + for (i = 0; i < MAX_SUPPORTED_FINGERS; i++) { > + if (ID_Indx[i]) { > + if (ID_Indx[i] == LEAVE_EVENT) { > + ID_Indx[i] = 0; > + num_released_finger++; > + } > + > + touch_ON(ftkts, cor_xyz[i][0], cor_xyz[i][1], > + cor_xyz[i][2], > + i); > + } else > + num_released_finger++; > + } > + > + input_sync(ftkts->input_dev); > + /* ---------------------------------- */ > + > + /* Check if all fingers are released */ > + /* ---------------------------------- */ > + if (num_released_finger == MAX_SUPPORTED_FINGERS) { > + /* All fingers are released */ > + touch_OFF(ftkts); > + input_sync(ftkts->input_dev); > + } > + /* ---------------------------------- */ > + } > + > + return LastLeftEvent; > +} > + > + > +static void ts_tasklet_proc(struct work_struct *work) > +{ > + struct ftk_ts *ftkts = container_of(work, struct ftk_ts, work); > + > + unsigned char data[256]; > + int ret; > + u8 status; > + u8 regAdd; > + u8 i; > + u8 FirstLeftEvent = 0; > + > + data[0] = 0xB0; > + data[1] = 0x07; > + ret = ftk_read_reg(ftkts, &data[0], 2, &status, 1); > + > + if (status & 0x40) { > + regAdd = 0x85; > + if (ftk_read_reg(ftkts, ®Add, 1, data, 8) == 0) { > + FirstLeftEvent = decode_data_packet(ftkts, data, 1); > + > + /* Read and process 1 event (8bytes) at a time */ > + for (i = 0; i < FirstLeftEvent; i++) { > + regAdd = 0x85; > + if (ftk_read_reg(ftkts, ®Add, 1, data, > + 8) == 0) > + FirstLeftEvent = decode_data_packet( > + ftkts, data, 1); > + } > + } > + } > + > + if (!ftkts->irq) > + hrtimer_start(&ftkts->timer, ktime_set(0, 10000000), > + HRTIMER_MODE_REL); > + else > + enable_irq(ftkts->client->irq); > +} > + > + > +static int stm_ts_probe(struct i2c_client *client, > + const struct i2c_device_id *idp) > +{ > + struct ftk_ts *ftk_ts = NULL; > + struct ftk_i2c_platform_data *pdata; > + int ret = 0; > + int err = 0; > + > + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { > + dev_err(ftk_ts->dev, "err = EIO!\n"); > + err = EIO; > + goto fail; > + } > + > + ftk_ts = kzalloc(sizeof(struct ftk_ts), GFP_KERNEL); devm_kzalloc(). > + if (!ftk_ts) { > + dev_err(ftk_ts->dev, "err = ENOMEM!\n"); > + err = ENOMEM; > + goto fail; > + } > + > + INIT_WORK(&ftk_ts->work, ts_tasklet_proc); > + > + ftk_ts->client = client; > + i2c_set_clientdata(client, ftk_ts); > + > + pdata = client->dev.platform_data; > + > + if (pdata) > + ftk_ts->power = pdata->power; > + > + if (ftk_ts->power) { > + ret = ftk_ts->power(1); > + > + pdata->power(1); > + > + if (ret < 0) { > + pr_err("ftk_probe power on failed\n"); > + goto fail; > + } > + } > + > + ftk_ts->dev = &ftk_ts->client->dev; > + ftk_ts->input_dev = input_allocate_device(); > + ftk_ts->input_dev->dev.parent = &client->dev; > + if (!ftk_ts->input_dev) { > + dev_err(ftk_ts->dev, "err = ENOMEM!\n"); > + err = ENOMEM; > + goto fail; > + } > + ftk_ts->input_dev->name = "ftk"; > + ftk_ts->input_dev->phys = "ftk/input0"; > + ftk_ts->input_dev->id.bustype = BUS_I2C; > + ftk_ts->input_dev->id.vendor = 0x0001; > + ftk_ts->input_dev->id.product = 0x0002; > + ftk_ts->input_dev->id.version = 0x0100; > + > + ftk_ts->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); > + ftk_ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); > + set_bit(EV_SYN, ftk_ts->input_dev->evbit); > + set_bit(EV_KEY, ftk_ts->input_dev->evbit); > + set_bit(BTN_TOUCH, ftk_ts->input_dev->keybit); > + set_bit(BTN_2, ftk_ts->input_dev->keybit); > + set_bit(EV_ABS, ftk_ts->input_dev->evbit); > + > + input_set_abs_params(ftk_ts->input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, > + 0, 0); > + input_set_abs_params(ftk_ts->input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, > + 0, 0); > + input_set_abs_params(ftk_ts->input_dev, ABS_PRESSURE, PRESSURE_MIN, > + PRESSURE_MAX, 0, 0); > + input_set_abs_params(ftk_ts->input_dev, ABS_MT_TRACKING_ID, 0, 10, 0, > + 0); > + input_set_abs_params(ftk_ts->input_dev, ABS_MT_TOUCH_MAJOR, > + PRESSURE_MIN, PRESSURE_MAX, 0, 0); > + input_set_abs_params(ftk_ts->input_dev, ABS_MT_WIDTH_MAJOR, > + PRESSURE_MIN, PRESSURE_MAX, 0, 0); > + input_set_abs_params(ftk_ts->input_dev, ABS_MT_POSITION_X, X_AXIS_MIN, > + X_AXIS_MAX, 0, 0); > + input_set_abs_params(ftk_ts->input_dev, ABS_MT_POSITION_Y, Y_AXIS_MIN, > + Y_AXIS_MAX, 0, 0); > + > + err = input_register_device(ftk_ts->input_dev); > + if (err) { > + dev_err(ftk_ts->dev, "input_register_device fail!\n"); > + goto fail; > + } > + > + err = init_ftk(ftk_ts); > + if (err) { > + dev_err(ftk_ts->dev, "init_ftk fail!\n"); > + goto fail; > + } > + > + ftk_ts->irq = client->irq; > + > + if (!ftk_ts->irq) { > + hrtimer_init(&ftk_ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); > + ftk_ts->timer.function = st_ts_timer_func; > + hrtimer_start(&ftk_ts->timer, ktime_set(1, 0), > + HRTIMER_MODE_REL); > + } else { > + if (request_irq > + (ftk_ts->irq, ts_interrupt, IRQF_TRIGGER_LOW, > + client->name, ftk_ts)) { make this: ret = devm_request_threaded_irq(ftk_ts->irq, NULL, ts_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ftks_ts); if (ret) { ... > + dev_err(ftk_ts->dev, "request_irq fail!\n"); > + err = -EBUSY; > + goto fail; > + } else > + dev_info(ftk_ts->dev, "request_irq success!\n"); drop this message, it's pretty much useless. > + } > + > +#ifdef CONFIG_HAS_EARLYSUSPEND > + ftk_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; > + ftk_ts->early_suspend.suspend = stm_ts_early_suspend; > + ftk_ts->early_suspend.resume = stm_ts_late_resume; > + register_early_suspend(&ftk_ts->early_suspend); > +#endif > + > + return 0; > +fail: > + if (ftk_ts) { > + if (ftk_ts->input_dev) > + input_free_device(ftk_ts->input_dev); > + kfree(ftk_ts); > + } > + dev_info(ftk_ts->dev, "--ftkts_probe ret=%d\n", err); > + return err; > +} > + > + > +static int stm_ts_remove(struct i2c_client *client) > +{ > + struct ftk_ts *ts = i2c_get_clientdata(client); > + > +#ifdef CONFIG_HAS_EARLYSUSPEND > + unregister_early_suspend(&ts->early_suspend); > +#endif > + > + if (ts->irq) > + free_irq(client->irq, ts); > + else > + hrtimer_cancel(&ts->timer); > + > + input_unregister_device(ts->input_dev); > + kfree(ts); > + return 0; > +} > + > + > +static int stm_ts_suspend(struct i2c_client *client, pm_message_t mesg) > +{ > + int ret, i; > + u8 regAdd[3]; > + > + struct ftk_ts *ts = i2c_get_clientdata(client); > + > + if (ts->irq) > + disable_irq(client->irq); > + else > + hrtimer_cancel(&ts->timer); > + > + ret = cancel_work_sync(&ts->work); > + > + regAdd[0] = 0x80; > + regAdd[1] = 0x00; > + ret = ftk_write_reg(ts, ®Add[0], 1); > + mdelay(5); > + > + regAdd[0] = 0x88; > + regAdd[1] = 0x00; > + ret = ftk_write_reg(ts, ®Add[0], 1); > + mdelay(5); > + > + regAdd[0] = 0xB0; > + regAdd[1] = 0x06; > + regAdd[2] = 0x00; > + ret = ftk_write_reg(ts, ®Add[0], 3); > + mdelay(5); > + > + for (i = 0; i < 10; i++) > + ID_Indx[i] = 0; > + > + input_mt_sync(ts->input_dev); > + input_sync(ts->input_dev); > + > + if (ret < 0) > + dev_err(ts->dev, "stm_ts_suspend: i2c_smbus_write_byte_data failed\n"); > + > + return 0; > +} > + > + > +static int stm_ts_resume(struct i2c_client *client) > +{ > + int ret; > + u8 regAdd[3]; > + struct ftk_ts *ts = i2c_get_clientdata(client); > + > + if (ts->irq) > + enable_irq(client->irq); > + else > + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); > + > + regAdd[0] = 0x81; > + regAdd[1] = 0x00; > + ret = ftk_write_reg(ts, ®Add[0], 1); > + mdelay(5); > + > + regAdd[0] = 0xB0; > + regAdd[1] = 0x06; > + regAdd[2] = 0x40; > + ret = ftk_write_reg(ts, ®Add[0], 3); > + mdelay(5); > + > + if (ret < 0) > + dev_err(ts->dev, > + "stm_ts_suspend: i2c_smbus_write_byte_data failed\n"); > + > + return 0; > +} > + > + > +#ifdef CONFIG_HAS_EARLYSUSPEND > +static void stm_ts_early_suspend(struct early_suspend *h) > +{ > + struct ftk_ts *ts; > + > + ts = container_of(h, struct ftk_ts, early_suspend); > + stm_ts_suspend(ts->client, PMSG_SUSPEND); > +} > + > + > +static void stm_ts_late_resume(struct early_suspend *h) > +{ > + struct ftk_ts *ts; > + > + ts = container_of(h, struct ftk_ts, early_suspend); > + stm_ts_resume(ts->client); > +} > + > + > +#endif > + > +static const struct i2c_device_id stm_ts_id[] = { > + { "ftk", 0 }, > + {} > +}; > + > +static struct i2c_driver stm_ts_driver = { > + .driver = { > + .name = "ftk", > + }, > + .probe = stm_ts_probe, > + .remove = stm_ts_remove, > +#ifndef CONFIG_HAS_EARLYSUSPEND > + .suspend = stm_ts_suspend, > + .resume = stm_ts_resume, > +#endif > + .id_table = stm_ts_id, > +}; > + > +static int __init stm_ts_init(void) > +{ > + stmtouch_wq = create_singlethread_workqueue("stmtouch_wq"); NAK. you don need this at all. > + if (!stmtouch_wq) > + return -ENOMEM; > + > + return i2c_add_driver(&stm_ts_driver); > +} > + > + > +static void __exit stm_ts_exit(void) > +{ > + i2c_del_driver(&stm_ts_driver); > + if (stmtouch_wq) > + destroy_workqueue(stmtouch_wq); > +} > + > + > +MODULE_DESCRIPTION("STM MultiTouch IC Driver"); > +MODULE_AUTHOR("Li Wu <li.wu@xxxxxx>"); > +MODULE_LICENSE("GPL"); > + > +module_init(stm_ts_init); > +module_exit(stm_ts_exit); BTW, you could be used module_i2c_driver() instead. -- balbi
Attachment:
signature.asc
Description: Digital signature