/* linux/drivers/input/touchscreen/cy8ctma300_touch.c
*
* Copyright (C) 2009 Sony Ericsson Mobile Communications, INC
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* <Driver name>
* cy8ctma300 touchscreen driver
*
* <Supported firmware version>
* TBD
*
* <Supported FPGA version>
* TBD
*/
#undef DEBUG
#define DUMP_BUF_SHORT
#undef MEASURE_PERFORMANCE
#define MODULE_VER "0.9b-mt"
#include <linux/init.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/cypress_touch.h>
#include <linux/syscalls.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <mach/gpio.h>
#include <asm/irq.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#ifdef MEASURE_PERFORMANCE
#include <linux/jiffies.h>
#endif
#ifdef CONFIG_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#ifdef CONFIG_ARM
#include <asm/mach-types.h>
#endif
/* FIXME- Use firmware-loader facility */
#include "cypress-firmware-0087.h"
/*--------------------------------------------------------------------------*/
/* Operating Mode Register */
#define TP_REG_HST_MODE 0x00
#define TP_REG_TT_MODE 0x01
#define TP_REG_TT_STAT 0x02
#define TP_REG_PNT1XH 0x03
#define TP_REG_PNT1XL 0x04
#define TP_REG_PNT1YH 0x05
#define TP_REG_PNT1YL 0x06
#define TP_REG_PNT1Z 0x07
#define TP_REG_PNT12_ID 0x08
#define TP_REG_PNT2XH 0x09
#define TP_REG_PNT2XL 0x0A
#define TP_REG_PNT2YH 0x0B
#define TP_REG_PNT2YL 0x0C
#define TP_REG_PNT2Z 0x0D
#define TP_REG_PNT3XH 0x10
#define TP_REG_PNT3XL 0x11
#define TP_REG_PNT3YH 0x12
#define TP_REG_PNT3YL 0x13
#define TP_REG_PNT3Z 0x14
#define TP_REG_PNT34_ID 0x15
#define TP_REG_PNT4XH 0x16
#define TP_REG_PNT4XL 0x17
#define TP_REG_PNT4YH 0x18
#define TP_REG_PNT4YL 0x19
#define TP_REG_PNT4Z 0x1A
#define TP_REG_GEST_CNT 0x0E
#define TP_REG_GEST_ID 0x0F
#define TP_REG_GEST_SET 0x1E
/* Gesture active distance. BEWARE: this is more than just for gestures */
#define TP_GEST_ACTD 0x02
/* BIST Mode Register Set */
#define TP_BISTR_BIST_STAT 0x01
#define TP_BISTR_BIST_NUM 0x02
/* ... */
#define TP_BISTR_UID_0 0x07
#define TP_BISTR_UID_1 0x08
#define TP_BISTR_UID_2 0x09
/* you get the idea ... */
#define TP_BISTR_BL_VERH 0x0F
#define TP_BISTR_BL_VERL 0x10
#define TP_BISTR_TTS_VERH 0x11
#define TP_BISTR_TTS_VERL 0x12
#define TP_BISTR_AP_IDH 0x13
#define TP_BISTR_AP_IDL 0x14
#define TP_BISTR_AP_VERH 0x15
#define TP_BISTR_AP_VERL 0x16
#define TP_REG_FW 0xFF
/* more of these later if/as I need 'em */
#define TP_TM_BIT_BIST (1 << 4)
#define TP_TM_BIT_SRESET (1 << 0)
#define TP_TM_BIT_DEEPSLEEP (1 << 1)
#define TP_TT_BIT_DINVAL (1 << 5)
#define TP_TT_BIT_FACE (1 << 4)
#define TP_TT_BIT_TOUCH_MASK (0xF)
#define TP_BL_BIT_BLRDY (1 << 0)
#define TP_BL_BIT_BM (1 << 1)
#define TP_BL_BIT_ERR_MASK (~(TP_BL_BIT_BLRDY | TP_BL_BIT_BM))
/* if set alone in TP_REG_FW we're in application mode OK
in later (> 0.65) firmwares. Sanity check */
#define TP_AP_BIT_APPMODE (1 << 2)
#define FIRMWARE_BLOCK_SIZE (128)
#define FIRMWARE_USER_BLOCKS (237)
#define FIRMWARE_TOTAL_BLOCKS (256)
#define TOUCH_DATA_BYTES (TP_REG_PNT4Z)
#define BL_CMD_BL_ENTER (0x38)
#define BL_CMD_WR_BLOCK (0x39)
#define BL_CMD_BL_EXIT (0x3B)
/* Big-Endian representation */
#define BL_CMD_EXECUTE (0x53CA)
/* buffer valid bits, firmware 87+ 107+ */
#define TP_SPI_BUFFER_VALID_MASK 0xC0 /* mask bits 6, 7 in reg 2 */
#define TP_SPI_BUFFER_VALID 0x40 /* bit 6 set = accept data */
#define TP_SPI_BUFFER_VALID_VER 87 /* FW version 87 and above */
#ifdef DEBUG
#define DEBUG_PRINTK(format, args...) \
{ if (dump_noisy) \
printk(format , ## args); }
#else
#define DEBUG_PRINTK(format, args...)
#endif
#define ENABLE_IRQ(this) { if (!this->irq_suspend_enabled++) \
enable_irq(this->pdata->irq); }
#define DISABLE_IRQ(this) { if (this->irq_suspend_enabled) {\
disable_irq_nosync(this->pdata->irq); \
this->irq_suspend_enabled = 0; } }
static int force_update;
static int shit_firmware;
module_param(force_update, int, 0);
MODULE_PARM_DESC(force_update, "Force flashing of driver firmware");
/*--------------------------------------------------------------------------*/
struct cy8ctma300_touch {
struct input_dev *input;
spinlock_t lock;
struct mutex mutex;
struct spi_device *spi;
struct work_struct fwupd_work;
struct spi_message async_spi_message;
struct spi_transfer async_spi_transfer;
u8 async_read_buf[TOUCH_DATA_BYTES];
struct cdev device_cdev;
int device_major;
struct class *device_class;
int first_irq;
#ifdef CONFIG_EARLYSUSPEND
struct early_suspend early_suspend;
#endif
int irq_suspend_enabled;
int init_complete;
int has_been_initialized;
struct cypress_touch_platform_data *pdata;
#ifdef MEASURE_PERFORMANCE
unsigned long measure_start;
int measure_count;
int measure_dupes;
u16 dupe_x;
u16 dupe_y;
u8 dupe_touch;
#endif
u8 suspend;
int use_spi_buffer_valid;
};
struct fw_packet {
u8 bl_keys[8];
u8 bl_cmd;
u16 bl_blockno;
u8 bl_data[FIRMWARE_BLOCK_SIZE];
u8 bl_data_csum;
u8 bl_cmd_csum;
u16 bl_execute;
} __attribute__((packed));
/* send packet to get touch data from device */
static u8 const cy8ctma300_send_msg[TOUCH_DATA_BYTES + 2] = { 0, 0, };
static u8 const bootloader_keys[] = { 0, 1, 2, 3, 4, 5, 6, 7, };
static int dump_noisy = 1;
static int cy8ctma300_deferred_init(struct cy8ctma300_touch *this);
static int reset_device(struct cy8ctma300_touch *this);
/*--------------------------------------------------------------------------*/
static void dump_buf(u8 *buf, unsigned len)
{
#ifdef DEBUG
int i;
if (!len || !buf || !dump_noisy)
return;
#ifdef DUMP_BUF_SHORT
if (len > 12)
len = 12;
#endif
printk(KERN_INFO "CY8CTMA300_TOUCH: dump_buf (%03d): ", len);
for (i = 0; i < len; i++)
printk("0x%02X ", buf[i]);
printk("\n");
#endif
};
static int reg_write(struct spi_device *spi, u8 addr, u8* buf, size_t len)
{
int err = 0;
u8 *write_buf;
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: reg_write(0x%02X, %d)\n", addr, len);
if (!buf || !len)
return -EINVAL;
write_buf = kzalloc(sizeof(u8) * (len + 2), GFP_KERNEL);
if (!write_buf)
return -ENOMEM;
dump_buf(buf, len);
write_buf[0] = 0x01;
write_buf[1] = addr;
memcpy(&write_buf[2], buf, len);
err = spi_write(spi, write_buf, len + 2);
msleep(10);
kfree(write_buf);
if (err)
printk(KERN_ERR "CY8CTMA300_TOUCH: reg_write error %d\n", err);
return err;
};
static inline int reg_write_byte(struct spi_device *spi, u8 addr, u8 data)
{
return reg_write(spi, addr, &data, 1);
};
static int reg_read(struct spi_device *spi, u8 addr, u8* buf, size_t len)
{
int err = 0;
u8 read_req[2];
u8 *read_buf;
size_t newlen;
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: reg_read (0x%02X, %d)\n", addr, len);
if (!buf || !len)
return -EINVAL;
/* re-buffer reads to ensure even-byte transfers */
read_buf = kzalloc(sizeof(u8) * (newlen = len & 1 ? len + 1 : len),
GFP_KERNEL);
if (!read_buf)
return -ENOMEM;
read_req[0] = 0x00;
read_req[1] = addr;
err = spi_write(spi, read_req, 2);
if (err) {
printk(KERN_ERR
"CY8CTMA300_TOUCH: reg_read() write err %d\n", err);
goto out;
};
msleep(10);
err = spi_read(spi, read_buf, newlen);
if (err) {
printk(KERN_ERR
"CY8CTMA300_TOUCH: reg_read() read err %d\n", err);
} else {
memcpy(buf, read_buf, len);
dump_buf(buf, len);
};
out:
kfree(read_buf);
return err;
};
static int reg_read_intcause_async(struct spi_device *spi, void* complete)
{
struct cy8ctma300_touch *this = dev_get_drvdata(&spi->dev);
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: reg_read_intcause_async()\n");
this->async_spi_transfer.tx_buf = cy8ctma300_send_msg;
this->async_spi_transfer.rx_buf = this->async_read_buf;
this->async_spi_transfer.len = TOUCH_DATA_BYTES;
spi_message_init(&this->async_spi_message);
spi_message_add_tail(&this->async_spi_transfer,
&this->async_spi_message);
this->async_spi_message.complete = complete;
this->async_spi_message.context = (void *)this;
return spi_async(spi, &this->async_spi_message);
};
static int bl_flash_mode(struct spi_device *spi)
{
int err;
u8 status;
mdelay(100);
err = reg_read(spi, TP_REG_FW, &status, 1);
if (err)
return err;
if (status & TP_BL_BIT_ERR_MASK)
return status;
return ((status == (TP_BL_BIT_BM | TP_BL_BIT_BLRDY)) ? 0 : 1);
};
static void do_checksum(struct fw_packet *pk)
{
u8 *fw_hdr;
pk->bl_data_csum = 0;
pk->bl_cmd_csum = TP_REG_FW; /* calc includes the FW addr byte */
for (fw_hdr = &pk->bl_keys[0]; fw_hdr < &pk->bl_data[0]; fw_hdr++)
pk->bl_cmd_csum += *fw_hdr;
for (; fw_hdr < &pk->bl_data_csum; fw_hdr++)
pk->bl_data_csum += *fw_hdr;
/* checksum data val, then the csum byte itself */
pk->bl_cmd_csum += (pk->bl_data_csum * 2);
};
static int write_firmware_block(struct spi_device *spi, u8 *block, u16 blockno)
{
struct fw_packet pk;
int err = 0;
/* once bitten, twice shy */
if (!blockno)
return -EROFS;
if (blockno >= FIRMWARE_TOTAL_BLOCKS)
return -ERANGE;
memcpy(&pk.bl_keys, bootloader_keys, sizeof(pk.bl_keys));
pk.bl_cmd = BL_CMD_WR_BLOCK;
pk.bl_blockno = (blockno << 8) | (blockno >> 8);
memcpy(&pk.bl_data, block, FIRMWARE_BLOCK_SIZE);
do_checksum(&pk);
pk.bl_execute = BL_CMD_EXECUTE;
err = reg_write(spi, TP_REG_FW, (u8 *)&pk, sizeof(struct fw_packet));
if (err)
return err;
err = bl_flash_mode(spi);
if (err < 0)
return err;
if (err)
return -EPROTO;
return err;
};
/* sanity check on stored firmware file. Not necessary now but will be for
when the file is loaded the right way */
static int verify_fw_checksum(void)
{
int i;
u16 mastercsum = 0;
u8 *p;
/* calculate CSUM from 2nd block thru # of user blocks */
p = &cypress_firmware[FIRMWARE_BLOCK_SIZE];
i = FIRMWARE_USER_BLOCKS * FIRMWARE_BLOCK_SIZE;
while (i--)
mastercsum += *p++;
p = &cypress_firmware[sizeof(cypress_firmware) - 2];
if (mastercsum != (u16)((*p << 8) | *(p + 1))) {
printk(KERN_ERR "CY8CTMA300_TOUCH: firmware file checksum error\n");
return 1;
};
return 0;
};
static int send_bootloader_cmd(struct spi_device *spi, u8 cmd)
{
struct fw_packet packet;
int err;
memcpy(&packet.bl_keys, bootloader_keys, sizeof(bootloader_keys));
packet.bl_cmd = cmd;
err = reg_write(spi, TP_REG_FW, (u8 *) &packet,
sizeof(bootloader_keys) + 1);
if (err)
printk(KERN_ERR
"CY8CTMA300_TOUCH: send_bootloader_cmd write: %d\n",
err);
return err;
};
static int perform_reset(struct spi_device *spi)
{
int i = 5000000;
int loops = 0, cur_val, new_val = 0xFF;
struct cypress_touch_platform_data *pdata = spi->dev.platform_data;
gpio_set_value(pdata->gpio_reset_pin, 0);
udelay(100);
gpio_set_value(pdata->gpio_reset_pin, 1);
cur_val = gpio_get_value(pdata->gpio_irq_pin);
do {
while (i-- && (cur_val ==
(new_val = gpio_get_value(pdata->gpio_irq_pin))))
;
DEBUG_PRINTK(KERN_INFO
"CY8CTMA300_TOUCH: loop %d val %d remain %d\n",
loops, cur_val, i);
cur_val = new_val;
/* successful reset is 2 successful pulses before timeout */
if (++loops == 4) {
mdelay(100);
/* some old firmware ACKs with IRQ before
really ready */
if (shit_firmware)
mdelay(2000);
return 0;
};
} while (i > 0);
/* loops == 2 means bootloader failed (usually checksum) and we're in
BL mode (ret 1), application won't start.
Anything else is a bad TP (ret -1) */
return (loops == 2) ? 1 : -1;
};
static int update_firmware(struct spi_device *spi)
{
u16 i;
int err = 0;
int tries = 0;
if (verify_fw_checksum())
return -EBADF;
retry:
/* try doing application mode first */
reg_write_byte(spi, TP_REG_HST_MODE, 0);
mdelay(100);
/* try entering bootloader mode */
err = send_bootloader_cmd(spi, BL_CMD_BL_ENTER);
if (err)
return err;
/* check status */
err = bl_flash_mode(spi);
if (err) {
if (err < 0) {
printk(KERN_ERR
"CY8CTMA300_TOUCH: "
"bootloader enter read: %d\n",
err);
return err;
} else {
printk(KERN_ERR
"CY8CTMA300_TOUCH: "
"bootloader enter status: %d\n",
err);
if (tries++ < 2) {
printk(KERN_ERR "CY8CTMA300_TOUCH: Retrying\n");
perform_reset(spi);
goto retry;
}
return -EPROTO;
};
};
printk(KERN_INFO "CY8CTMA300_TOUCH: loading firmware: please wait\n");
dump_noisy = 0;
/* try programming the device */
for (i = 1; i < FIRMWARE_TOTAL_BLOCKS; i++) {
/* if we've programmed all the Application blocks,
program the last block */
if (i > FIRMWARE_USER_BLOCKS)
i = FIRMWARE_TOTAL_BLOCKS - 1;
err = write_firmware_block(spi,
&cypress_firmware[i * FIRMWARE_BLOCK_SIZE], i);
if (err) {
printk(KERN_ERR
"CY8CTMA300_TOUCH: "
"firmware write error %d\n", err);
return err;
};
};
dump_noisy = 1;
/* Exiting BL mode no longer needed per Cypress;
it performs the same as the reset below
err = send_bootloader_cmd(spi, BL_CMD_BL_EXIT);
*/
if (!err) {
printk(KERN_INFO "CY8CTMA300_TOUCH: loading firmware: done\n");
err = perform_reset(spi);
}
return err;
};
static int query_chip(struct spi_device *spi)
{
u8 j[16];
u16 blver, appver;
struct cypress_touch_platform_data *pdata = spi->dev.platform_data;
struct cy8ctma300_touch *this = dev_get_drvdata(&spi->dev);
reg_write_byte(spi, TP_REG_HST_MODE, TP_TM_BIT_BIST);
mdelay(100);
reg_read(spi, TP_REG_HST_MODE, j, 1);
if (j[0] != TP_TM_BIT_BIST)
return -EIO;
reg_read(spi, TP_BISTR_BL_VERH, j, 2);
printk(KERN_INFO
"CY8CTMA300_TOUCH: Bootloader FW Ver %d.%d\n", j[0], j[1]);
blver = j[0] << 8 | j[1];
reg_read(spi, TP_BISTR_AP_VERH, j, 2);
printk(KERN_INFO
"CY8CTMA300_TOUCH: Application FW Ver %d.%d\n", j[0], j[1]);
appver = j[0] << 8 | j[1];
reg_read(spi, TP_BISTR_UID_0, j, 2);
printk(KERN_INFO
"CY8CTMA300_TOUCH: Silicon Revision %c\n", ((j[0] == 0x05)
&& (j[1] == 0x80)) ? 'D' : 'E');
/* when firmware is 87 or above, use SPI buffer valid check */
this->use_spi_buffer_valid = (TP_SPI_BUFFER_VALID_VER <= appver);
if (pdata->no_fw_update == 0) {
if (appver < 58) {
printk(KERN_INFO "CY8CTMA300_TOUCH: old FW version, applying reset workarounds\n");
shit_firmware++;
};
DEBUG_PRINTK(KERN_INFO
"CY8CTMA300_TOUCH: about to compare FW versions\n");
if ((blver == 1) && (appver != FW_VERSION_MINOR)) {
force_update = 1;
return 1;
} else if (appver == FW_VERSION_MINOR) {
printk(KERN_ERR "CY8CTMA300_TOUCH: firmware versions match.\n");
};
} else {
printk(KERN_INFO "CY8CTMA300_TOUCH: no FW update allowed.\n");
};
DEBUG_PRINTK(KERN_INFO
"CY8CTMA300_TOUCH: about to re-enter normal mode\n");
/* now put chip back into Normal mode */
reg_write_byte(spi, TP_REG_HST_MODE, 0);
mdelay(100);
reg_read(spi, TP_REG_HST_MODE, j, 1);
if (j[0] != TP_REG_HST_MODE)
return -EIO;
return 0;
}
static void cy8ctma300_fwupd_work(struct work_struct *work)
{
struct cy8ctma300_touch *this =
container_of(work, struct cy8ctma300_touch, fwupd_work);
struct spi_device *spi = this->spi;
int err;
u8 j[16];
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: cy8ctma300_fwupd_work()\n");
mdelay(100);
err = update_firmware(spi);
if (err) {
printk(KERN_ERR
"CY8CTMA300_TOUCH: "
"firmware update failed %d, not registering device\n",
err);
return;
};
/* try to put device into BIST mode */
/* have to reset FW status byte first,
as current firmware locks otherwise */
reg_read(spi, TP_REG_FW, j, 1);
printk(KERN_INFO "CY8CTMA300_TOUCH: TP_REG_FW 0x%02X\n", j[0]);
err = query_chip(spi);
if (!err) {
if (j[0] == TP_AP_BIT_APPMODE) {
DEBUG_PRINTK(KERN_INFO
"CY8CTMA300_TOUCH: have sane firmware rev now");
if (shit_firmware) {
DEBUG_PRINTK(", clearing bad-FW flag");
shit_firmware = 0;
};
DEBUG_PRINTK("\n");
};
cy8ctma300_deferred_init(this);
} else {
printk(KERN_ERR
"CY8CTMA300_TOUCH: cy8ctma300_fwupd_work():"
" chip query failed %d\n", err);
this->init_complete++;
};
};
static int reset_device(struct cy8ctma300_touch *this)
{
struct spi_device *spi = this->spi;
int reset_retries = 2;
int err = 0;
u8 j[16];
reset_chip:
if ((perform_reset(spi) < 0) || !reset_retries--)
return -ENOMEDIUM;
/* check FW status and attempt to flash chip if necessary */
reg_read(spi, TP_REG_FW, j, 1);
printk(KERN_INFO "CY8CTMA300_TOUCH: TP_REG_FW 0x%02X\n", j[0]);
/* check for the case of older firmware that's not quite ready yet */
if (j[0] == 0xFF) {
shit_firmware++;
goto reset_chip;
};
if ((force_update-- > 0) || (j[0] & (TP_BL_BIT_BLRDY | TP_BL_BIT_BM)))
return 1;
err = query_chip(spi);
if (err < 0) {
shit_firmware++;
goto reset_chip;
};
return err;
};
static void cy8ctma300_send_input(struct cy8ctma300_touch *this, u8 down,
u16 x, u16 y, u16 id)
{
DEBUG_PRINTK(KERN_INFO
"CY8CTMA300_TOUCH: down %u x %u y %u id %u\n", down, x, y, id);
if (down != 1)
input_mt_sync(this->input);
input_report_abs(this->input, ABS_MT_POSITION_X, x);
input_report_abs(this->input, ABS_MT_POSITION_Y, y);
input_report_abs(this->input, ABS_MT_TOUCH_MAJOR, 1);
input_report_abs(this->input, ABS_MT_TOUCH_MINOR, 1);
input_report_abs(this->input, ABS_MT_TRACKING_ID, id - 1);
};
static void cy8ctma300_touch_isr(void *context)
{
struct cy8ctma300_touch *this = (struct cy8ctma300_touch *)context;
struct cypress_touch_platform_data
*pdata = this->spi->dev.platform_data;
u8 read_buf[TOUCH_DATA_BYTES];
u16 xp = 0, yp = 0, id = 0;
u8 down;
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: cy8ctma300_touch_isr()\n");
mutex_lock(&this->mutex);
if (this->suspend) {
mutex_unlock(&this->mutex);
return;
}
memcpy(read_buf, this->async_read_buf, TOUCH_DATA_BYTES);
dump_buf(read_buf, TOUCH_DATA_BYTES);
if (read_buf[TP_REG_TT_MODE] & TP_TT_BIT_DINVAL) {
printk(KERN_INFO "CY8CTMA300_TOUCH: invalid data?\n");
goto out;
};
/*
* For SSD FW-87+ and DSD-FW 107+, check bitamsk in 3rd byte
* for 01 in bits [7:6] to accept the data
* i.e. if (BYTE[2] & 0xC0 == 0x40) accept data, else reject
*/
if (this->use_spi_buffer_valid) {
if ((read_buf[TP_REG_TT_STAT] & TP_SPI_BUFFER_VALID_MASK) !=
TP_SPI_BUFFER_VALID) {
dev_info(&this->spi->dev,
"CY8CTMA300_TOUCH: rejected data.\n");
goto out;
}
}
down = read_buf[TP_REG_TT_STAT] & TP_TT_BIT_TOUCH_MASK;
xp = read_buf[TP_REG_PNT1XL] | (read_buf[TP_REG_PNT1XH] << 8);
yp = read_buf[TP_REG_PNT1YL] | (read_buf[TP_REG_PNT1YH] << 8);
yp = (pdata->y_max) - yp;
id = read_buf[TP_REG_PNT12_ID] >> 4 & 0xF;
input_report_key(this->input, BTN_TOUCH, down ? 1 : 0);
if (down == 1) {
input_report_abs(this->input, ABS_X, xp);
input_report_abs(this->input, ABS_Y, yp);
};
if (down) {
cy8ctma300_send_input(this, down, xp, yp, id);
if (down > 1) {
xp = read_buf[TP_REG_PNT2XL] |
(read_buf[TP_REG_PNT2XH] << 8);
yp = read_buf[TP_REG_PNT2YL] |
(read_buf[TP_REG_PNT2YH] << 8);
yp = (pdata->y_max) - yp;
id = read_buf[TP_REG_PNT12_ID] & 0xF;
cy8ctma300_send_input(this, down, xp, yp, id);
};
if (down > 2) {
xp = read_buf[TP_REG_PNT3XL] |
(read_buf[TP_REG_PNT3XH] << 8);
yp = read_buf[TP_REG_PNT3YL] |
(read_buf[TP_REG_PNT3YH] << 8);
yp = (pdata->y_max) - yp;
id = read_buf[TP_REG_PNT34_ID] >> 4 & 0xF;
cy8ctma300_send_input(this, down, xp, yp, id);
};
if (down > 3) {
xp = read_buf[TP_REG_PNT4XL] |
(read_buf[TP_REG_PNT4XH] << 8);
yp = read_buf[TP_REG_PNT4YL] |
(read_buf[TP_REG_PNT4YH] << 8);
yp = (pdata->y_max) - yp;
id = read_buf[TP_REG_PNT34_ID] & 0xF;
cy8ctma300_send_input(this, down, xp, yp, id);
};
};
input_mt_sync(this->input);
input_sync(this->input);
#ifdef MEASURE_PERFORMANCE
if (this->dupe_x == xp && this->dupe_y == yp &&
this->dupe_touch == (down ? 1 : 0))
++this->measure_dupes;
this->dupe_x = xp;
this->dupe_y = yp;
this->dupe_touch = (down ? 1 : 0);
#endif
DEBUG_PRINTK(KERN_INFO
"CY8CTMA300_TOUCH: touch %s at (%3d, %3d)\n",
down ? "down" : "up ", xp, yp);
#ifdef MEASURE_PERFORMANCE
if (this->measure_start < jiffies) {
printk(KERN_INFO
"CY8CTMA300_TOUCH: "
"%d interrupts/events per second and %d dupes\n",
this->measure_count, this->measure_dupes);
this->measure_start = jiffies + msecs_to_jiffies(1000);
this->measure_count = 0;
this->measure_dupes = 0;
}
++this->measure_count;
#endif
out:
mutex_unlock(&this->mutex);
ENABLE_IRQ(this);
};
static irqreturn_t cy8ctma300_touch_irq(int irq, void *handle)
{
struct cy8ctma300_touch *this = handle;
unsigned long flags;
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: cy8ctma300_touch_irq()\n");
/* first IRQ is side-effect of reset operation */
if (!this->first_irq++)
return IRQ_HANDLED;
DISABLE_IRQ(this);
spin_lock_irqsave(&this->lock, flags);
reg_read_intcause_async(this->spi, cy8ctma300_touch_isr);
spin_unlock_irqrestore(&this->lock, flags);
return IRQ_HANDLED;
};
/*--------------------------------------------------------------------------*/
#ifdef CONFIG_PM
static int cy8ctma300_touch_suspend(struct spi_device *spi,
pm_message_t message)
{
struct cy8ctma300_touch *this = dev_get_drvdata(&spi->dev);
DEBUG_PRINTK(KERN_INFO "CY8CTMA300_TOUCH: %s()\n", __func__);
DISABLE_IRQ(this);
mutex_lock(&this->mutex);
this->suspend = 1;
mutex_unlock(&this->mutex);
return reg_write_byte(spi, TP_REG_HST_MODE, TP_TM_BIT_DEEPSLEEP);
}
static int cy8ctma300_touch_resume(struct spi_device *spi)
{
int rc;
u8 data;
struct cy8ctma300_touch *this = dev_get_drvdata(&spi->dev);
DEBUG_PRINTK(KERN_INFO "CY8CTMA300_TOUCH: %s()\n", __func__);
this->suspend = 0;
ENABLE_IRQ(this);
rc = reg_write_byte(spi, TP_REG_HST_MODE, 0);
mdelay(100);
reg_read(spi, TP_REG_HST_MODE, &data, 1);
if (data != TP_REG_HST_MODE)
return -ENODEV;
return rc;
};
#endif
#if defined(CONFIG_EARLYSUSPEND) && defined(CONFIG_PM)
static void cy8ctma300_touch_early_suspend(struct early_suspend *es)
{
struct cy8ctma300_touch *this;
this = container_of(es, struct cy8ctma300_touch, early_suspend);
DEBUG_PRINTK(KERN_INFO "CY8CTMA300_TOUCH: %s()\n", __func__);
dev_dbg(&this->spi->dev, "CY8CTMA300_TOUCH: early suspend\n");
cy8ctma300_touch_suspend(this->spi, PMSG_SUSPEND);
};
static void cy8ctma300_touch_late_resume(struct early_suspend *es)
{
struct cy8ctma300_touch *this;
this = container_of(es, struct cy8ctma300_touch, early_suspend);
DEBUG_PRINTK(KERN_INFO "CY8CTMA300_TOUCH: %s()\n", __func__);
dev_dbg(&this->spi->dev, "CY8CTMA300_TOUCH: late resume\n");
cy8ctma300_touch_resume(this->spi);
};
#endif /* #ifdef CONFIG_EARLYSUSPEND */
static int cy8ctma300_touch_open(struct inode *inode, struct file *file)
{
struct cy8ctma300_touch *this =
container_of(inode->i_cdev, struct cy8ctma300_touch,
device_cdev);
file->private_data = this;
return 0;
};
static int cy8ctma300_touch_release(struct inode *inode, struct file *file)
{
return 0;
};
static ssize_t cy8ctma300_touch_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
switch (cmd) {
default:
printk(KERN_ERR "CY8CTMA300_TOUCH: ioctl, cmd error\n");
return -EINVAL;
break;
}
return err;
};
static const struct file_operations cy8ctma300_touch_fops = {
.owner = THIS_MODULE,
.open = cy8ctma300_touch_open,
.ioctl = cy8ctma300_touch_ioctl,
.release = cy8ctma300_touch_release,
};
static int cy8ctma300_deferred_init(struct cy8ctma300_touch *this)
{
struct input_dev *input_dev;
struct spi_device *spi = this->spi;
struct cypress_touch_platform_data *pdata = spi->dev.platform_data;
dev_t device_t = MKDEV(0, 0);
struct device *class_dev_t = NULL;
int err = 0;
if (this->has_been_initialized)
return 0;
input_dev = input_allocate_device();
if (!input_dev) {
err = -ENOMEM;
goto err_cleanup_mem;
}
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Allocated input device\n");
this->input = input_dev;
input_dev->name = "cy8ctma300_touch";
input_dev->phys = "cy8ctma300_touch/input0";
input_dev->dev.parent = &spi->dev;
set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_ABS, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(BTN_2, input_dev->keybit);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
pdata->x_min, pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
pdata->y_min, pdata->y_max, 0, 0);
input_set_abs_params(this->input, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0);
input_set_abs_params(this->input, ABS_MT_TOUCH_MINOR, 0, 1, 0, 0);
set_bit(ABS_MT_TRACKING_ID, this->input->absbit);
/* Single-Touch stuff */
input_set_abs_params(input_dev, ABS_X, pdata->x_min,
pdata->x_max, 0, 0);
input_set_abs_params(input_dev, ABS_Y, pdata->y_min,
pdata->y_max, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0, 0, 0);
err = input_register_device(input_dev);
if (err)
goto err_cleanup_mem;
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Registered input device\n");
err = alloc_chrdev_region(&device_t, 0, 1, "cy8ctma300_touch");
if (err)
goto err_cleanup_input;
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: Allocated character device\n");
this->device_major = MAJOR(device_t);
cdev_init(&(this->device_cdev), &cy8ctma300_touch_fops);
this->device_cdev.owner = THIS_MODULE;
this->device_cdev.ops = &cy8ctma300_touch_fops;
err = cdev_add(&(this->device_cdev), MKDEV(this->device_major, 0), 1);
if (err)
goto err_cleanup_chrdev;
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Character device added\n");
this->device_class = class_create(THIS_MODULE, "cy8ctma300_touch");
if (IS_ERR(this->device_class)) {
err = -1;
goto err_cleanup_cdev;
};
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Class created\n");
class_dev_t = device_create(this->device_class, NULL,
MKDEV(this->device_major, 0), NULL, "cy8ctma300_touch");
if (IS_ERR(class_dev_t)) {
err = -1;
goto err_cleanup_class;
}
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Device created\n");
#ifdef CONFIG_EARLYSUSPEND
/* register early suspend */
this->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
this->early_suspend.suspend = cy8ctma300_touch_early_suspend;
this->early_suspend.resume = cy8ctma300_touch_late_resume;
register_early_suspend(&this->early_suspend);
#endif /* #ifdef CONFIG_EARLYSUSPEND */
err = request_irq(pdata->irq, cy8ctma300_touch_irq, pdata->irq_polarity,
this->spi->dev.driver->name, this);
if (err) {
dev_err(&this->spi->dev, "irq %d busy?\n", pdata->irq);
goto err_cleanup_device;
};
/* IRQs are enabled as a side-effect of requesting */
this->irq_suspend_enabled++;
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Registered IRQ\n");
/* Set the gesture active distance */
reg_write_byte(this->spi, TP_REG_GEST_SET, TP_GEST_ACTD);
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: cy8ctma300_deferred_init() ok\n");
this->init_complete++;
this->has_been_initialized = 1;
return 0;
err_cleanup_device:
#ifdef CONFIG_EARLYSUSPEND
unregister_early_suspend(&this->early_suspend);
#endif
device_destroy(this->device_class, MKDEV(this->device_major, 0));
err_cleanup_class:
class_destroy(this->device_class);
err_cleanup_cdev:
cdev_del(&(this->device_cdev));
err_cleanup_chrdev:
unregister_chrdev_region(device_t, 1);
err_cleanup_input:
input_unregister_device(input_dev);
err_cleanup_mem:
if (input_dev)
input_free_device(input_dev);
return err;
};
static int cy8ctma300_touch_probe(struct spi_device *spi)
{
struct cypress_touch_platform_data *pdata = spi->dev.platform_data;
struct cy8ctma300_touch *this;
int err;
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: cy8ctma300_touch_probe()\n");
if (!pdata) {
dev_dbg(&spi->dev, "no platform data?\n");
return -ENODEV;
};
if (!pdata->irq) {
dev_dbg(&spi->dev, "no IRQ?\n");
return -ENODEV;
}
/* Set up SPI */
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
err = spi_setup(spi);
if (err < 0)
return err;
printk(KERN_DEBUG
"CY8CTMA300_TOUCH: SPI setup (requesting %uHz) OK\n",
spi->max_speed_hz);
/* GPIO: set up */
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: Requesting GPIO Reset ownership\n");
err = gpio_request(pdata->gpio_reset_pin,
"cy8ctma300_touch_gpio_reset");
if (err)
goto err_gpio_setup;
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: Requesting GPIO IRQ ownership\n");
err = gpio_request(pdata->gpio_irq_pin, "cy8ctma300_touch_gpio_irq");
if (err)
goto err_gpio_setup;
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: Configuring GPIO Reset direction\n");
err = gpio_direction_output(pdata->gpio_reset_pin, 0);
if (err)
goto err_gpio_setup;
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: Configuring GPIO IRQ direction\n");
err = gpio_direction_input(pdata->gpio_irq_pin);
if (err)
goto err_gpio_setup;
this = kzalloc(sizeof(struct cy8ctma300_touch), GFP_KERNEL);
if (!this) {
err = -ENOMEM;
goto err_gpio_setup;
};
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Allocated private data\n");
this->pdata = pdata;
this->spi = spi;
spin_lock_init(&this->lock);
mutex_init(&this->mutex);
dev_set_drvdata(&spi->dev, this);
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Driver data set\n");
/* register firmware validation and initialization to workqueue */
INIT_WORK(&this->fwupd_work, cy8ctma300_fwupd_work);
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: Firmware-update work queue init OK\n");
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: About to reset device\n");
err = reset_device(this);
if (err < 0)
goto err_mem_free;
if (!err) {
DEBUG_PRINTK(KERN_DEBUG "CY8CTMA300_TOUCH: Chip reset OK\n");
err = cy8ctma300_deferred_init(this);
if (err) {
goto err_mem_free;
} else {
printk(KERN_INFO "CY8CTMA300_TOUCH: Device registered OK\n");
return 0;
}
} else {
printk(KERN_INFO "CY8CTMA300_TOUCH: Deferring device init post-FW Update\n");
schedule_work(&this->fwupd_work);
return 0;
};
err_mem_free:
kfree(this);
err_gpio_setup:
gpio_free(pdata->gpio_reset_pin);
gpio_free(pdata->gpio_irq_pin);
printk(KERN_ERR "CY8CTMA300_TOUCH: probe() fail: %d\n", err);
return err;
};
static int __devexit cy8ctma300_touch_remove(struct spi_device *spi)
{
struct cy8ctma300_touch *this = dev_get_drvdata(&spi->dev);
struct cypress_touch_platform_data *pdata = spi->dev.platform_data;
dev_t device_t = MKDEV(this->device_major, 0);
DEBUG_PRINTK(KERN_INFO "CY8CTMA300_TOUCH: unregistering touchscreen\n");
if (!this)
return -ENODEV;
gpio_free(pdata->gpio_reset_pin);
gpio_free(pdata->gpio_irq_pin);
#ifdef CONFIG_PM
cy8ctma300_touch_suspend(spi, PMSG_SUSPEND);
#endif
if (!this->init_complete) {
printk(KERN_ERR "CY8CTMA300_TOUCH: unregistering partially-loaded driver\n");
return -EBUSY;
};
#ifdef CONFIG_EARLYSUSPEND
unregister_early_suspend(&this->early_suspend);
#endif
if (this->input)
input_unregister_device(this->input);
if (this->device_class) {
device_destroy(this->device_class,
MKDEV(this->device_major, 0));
class_destroy(this->device_class);
};
if (&this->device_cdev) {
cdev_del(&(this->device_cdev));
unregister_chrdev_region(device_t, 1);
};
free_irq(pdata->irq, this);
kfree(this);
dev_dbg(&spi->dev, "CY8CTMA300_TOUCH: unregistered touchscreen\n");
return 0;
};
static struct spi_driver cy8ctma300_touch_driver = {
.driver = {
.name = "cypress_touchscreen",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = cy8ctma300_touch_probe,
.remove = __devexit_p(cy8ctma300_touch_remove),
#ifndef CONFIG_PM
.suspend = cy8ctma300_touch_suspend,
.resume = cy8ctma300_touch_resume,
#endif
};
static int __init cy8ctma300_touch_init(void)
{
int err;
printk(KERN_DEBUG
"CY8CTMA300_TOUCH: V%s built %s %s\n",
MODULE_VER, __DATE__, __TIME__);
err = spi_register_driver(&cy8ctma300_touch_driver);
DEBUG_PRINTK(KERN_DEBUG
"CY8CTMA300_TOUCH: module init, result=%d\n", err);
return err;
};
static void __exit cy8ctma300_touch_exit(void)
{
spi_unregister_driver(&cy8ctma300_touch_driver);
printk(KERN_DEBUG "CY8CTMA300_TOUCH: module exit\n");
};
module_init(cy8ctma300_touch_init);
module_exit(cy8ctma300_touch_exit);
MODULE_DESCRIPTION("Touchscreen driver for Cypress CY8CTMA300 hardware");
MODULE_LICENSE("GPL");