/* 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");
