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 <[email protected]>
> ---
>  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 <[email protected]>
> +L:      [email protected]
> +W:      http://www.st.com
> +S:      Supported
> +F:      drivers/input/touchscreen/ftk.c
> +
> +
>  ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR)
>  M:   Jiri Kosina <[email protected]>
>  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 <[email protected]>
> + *         Victor Phay <[email protected]>
> + *      Li Wu <[email protected]>, <[email protected]>
> + *
> + * 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, &regAdd[0], 3);
> +     mdelay(5);
> +
> +     regAdd[0] = 0xB0;
> +     regAdd[1] = 0x06;       /* Enable Touch Detect Interrupt */
> +     regAdd[2] = 0x40;       /* 0xC0 */
> +     ftk_write_reg(ftk_ts, &regAdd[0], 3);
> +     mdelay(5);
> +
> +     regAdd[0] = 0xB0;
> +     regAdd[1] = 0x07;       /* Read 0x07 to clear ISR */
> +     ret = ftk_read_reg(ftk_ts, &regAdd[0], 2, &val[0], 1);
> +     mdelay(5);
> +
> +     regAdd[0] = 0x85;
> +     ret = ftk_read_reg(ftk_ts, &regAdd[0], 1, &val[0], 8);
> +     mdelay(20);
> +
> +     regAdd[0] = 0x85;
> +     ret = ftk_read_reg(ftk_ts, &regAdd[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, &regAdd[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, &regAdd[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, &regAdd[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, &regAdd[0], 3);
> +     mdelay(5);

you need to add a comment to this mdelay().

> +
> +     regAdd[0] = 0xA0;
> +     ftk_write_reg(ftk_ts, &regAdd[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, &regAdd, 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, &regAdd, 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, &regAdd[0], 1);
> +     mdelay(5);
> +
> +     regAdd[0] = 0x88;
> +     regAdd[1] = 0x00;
> +     ret = ftk_write_reg(ts, &regAdd[0], 1);
> +     mdelay(5);
> +
> +     regAdd[0] = 0xB0;
> +     regAdd[1] = 0x06;
> +     regAdd[2] = 0x00;
> +     ret = ftk_write_reg(ts, &regAdd[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, &regAdd[0], 1);
> +     mdelay(5);
> +
> +     regAdd[0] = 0xB0;
> +     regAdd[1] = 0x06;
> +     regAdd[2] = 0x40;
> +     ret = ftk_write_reg(ts, &regAdd[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 <[email protected]>");
> +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

Reply via email to