/* 2009-08-18: File added and changed by Sony Corporation */
/*
 *  linux/drivers/input/touchscreen/mp200-tsc2007.c
 *
 *  Copyright (C) 2008 Wind River Systems, Inc.
 *  Copyright (C) NEC Electronics Corporation 2008
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Driver for the Micro Touch Screen Controller with I2C interface(TSC2007)
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/device.h>
#include <linux/input.h>
#include <linux/wait.h>
#include <linux/i2c.h>
#include <linux/freezer.h>

#include <asm/irq.h>
#include <asm/gpio.h>

static int event_flag;

#define USE_ANDROID

#define MP200_TS_SCAN_TIME	20

#ifndef wait_event_freezable
#ifdef CONFIG_PM
#define wait_event_freezable(wq, condition)		\
({									            \
	int __retval;							    \
	do {								        \
		__retval     = wait_event_interruptible(wq, \
				(condition) || freezing(current)); \
		if (__retval && !freezing(current))		\
			break;						        \
		else if (!(condition))					\
			__retval = -ERESTARTSYS;			\
	} while (try_to_freeze());					\
	__retval;							        \
})
#else
#define wait_event_freezable(wq, condition)		\
		wait_event_interruptible(wq, condition)
#endif
#endif

typedef enum
{
  TSC_DEBUG_LEVEL_NONE=0,
  TSC_DEBUG_LEVEL_BASIC,		// Entry, exits, no irq handler messages
  TSC_DEBUG_LEVEL_VERBOSE		// Basic + IRQ handler messages
} TSC_DEBUG_LEVEL;

static u32 debug = TSC_DEBUG_LEVEL_NONE;

module_param(debug, int, 0644);

#define TSC_NAME	"mp200-tsc2007"
#define TSC_VERSION	"1.0"

#define X_POSITION	0
#define Y_POSITION	1
#define Z1_POSITION	2
#define Z2_POSITION	3

#define MP200_TS_PIN	GPIO_TP_INT
#if defined(CONFIG_MACH_PGPF)
#define MP200_TS_IRQ	gpio_to_irq(GPIO_TP_INT)
#else
#define MP200_TS_IRQ	INT_GPIO_10
#endif

static struct i2c_driver mp200_tsc2007_driver;
static struct i2c_client *save_client = NULL;
static unsigned short normal_i2c[] = { 0x48, I2C_CLIENT_END };
I2C_CLIENT_INSMOD_1(mp200_tsc2007);

static struct input_dev *inpdev=NULL;

static int tsc_irq = 0;

static wait_queue_head_t tsc_queue;

static u8 mp200_ts_time_flag = 0;

static void mp200_ts_timer(unsigned long data);
static struct timer_list mp200_ts_scan_timer = {
	.function = mp200_ts_timer,
	.data = 0,
};

static u8 mp200_timer_thread_flag = 1;

#if defined(CONFIG_MACH_PGPF)
extern void pgpf_tsc2007_control_reset_signal(int signal);
#endif

static int mp200_tsc2007_io_write(struct i2c_client *client, u8 command)
{
	struct i2c_msg msg;

	msg.addr = client->addr;
	msg.flags = 0;			  // Write command
	msg.len = 1;
	msg.buf = &command;

	i2c_transfer(client->adapter, &msg, 1);

	return 0;
}


static int mp200_tsc2007_io_read(struct i2c_client *client, u8 *buf, int bufLen)
{
	struct i2c_msg msg;

	msg.addr = client->addr;
	msg.flags = I2C_M_RD;	   // Read command
	msg.len = bufLen;
	msg.buf = buf;

	i2c_transfer(client->adapter, &msg, 1);

	return 0;
}


static void mp200_tsc2007_get_data(int dataType, u8 *buf, int bufLen)
{
	char command[] = {0xc0, 0xd0, 0xe0, 0xf0};

	mp200_tsc2007_io_write(save_client, command[dataType]);
	mp200_tsc2007_io_read(save_client, buf, bufLen);
}


static int mp200_tsc2007_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
	return 0;
}

static void mp200_ts_timer(unsigned long data)
{
	event_flag = 1;
	wake_up(&tsc_queue);
}

static inline int is_pen_down(void)
{
	if (gpio_get_value(MP200_TS_PIN)) {
		return 0;
	} else {
		return 1;
	}
}

void mp200_tsc2007_get_tscdata(void)
{
	u16 x = 0;
	u16 y = 0;
	u8 x2[2];
	u8 y2[2];
	u16 z1 = 0;
	u16 z2 = 0;
	u8 z1_2[2];
	u8 z2_2[2];
	int pressure = 0;
	u8 pen_down = 0;
	static u8 prev_pen_down = 0;

	if (is_pen_down()) {
		/* Read in the values to quiesce the level interrupt line. */
		mp200_tsc2007_get_data(X_POSITION, x2, 2);
		mp200_tsc2007_get_data(Y_POSITION, y2, 2);
		mp200_tsc2007_get_data(Z1_POSITION, z1_2, 2);
		mp200_tsc2007_get_data(Z2_POSITION, z2_2, 2);
		x = (x2[0] << 4) | (x2[1] >> 4);
		y = (y2[0] << 4) | (x2[1] >> 4);
		z1 = (z1_2[0] << 4) | (z1_2[1] >> 4);
		z2 = (z2_2[0] << 4) | (z2_2[1] >> 4);

		/* Calculate the pressure. */
		if ( z1 != 0 ) {
			pressure = 100 * x;
			pressure /= 4096;
			pressure *= z2 - z1;
			pressure /= z1;
			if ( pressure < 0 ) {
				pressure = 0;
			}
		} else {
			pressure = 0;
		}

		/* Re-check */
		if (is_pen_down()) {
			pen_down = 1;
		}
	}

	if (pen_down) {
#ifdef USE_ANDROID
		{
			int tmp = 0xf40 - y;
			if (tmp < 0) {
				input_report_abs(inpdev, ABS_Y, 0);
			} else {
				input_report_abs(inpdev, ABS_Y, 0xf40 - y);
			}
		}
#else
		input_report_abs(inpdev, ABS_Y, y);
#endif
		input_report_abs(inpdev, ABS_X, x);
		input_report_abs(inpdev, ABS_PRESSURE, pressure);
		if (prev_pen_down == 0) {
			input_report_key(inpdev, BTN_TOUCH, 1);
		}
		mp200_ts_time_flag = 1;
		input_sync(inpdev);
	} else {
		if (prev_pen_down) {
			input_report_abs(inpdev, ABS_PRESSURE, 0);
			input_report_key(inpdev, BTN_TOUCH, 0);
			input_sync(inpdev);
		}
		mp200_ts_time_flag = 2;
	}

	if (debug) {
		printk("TS: %s (%d:%d:%d)\n", (pen_down == 0) ? "UP" : "DOWN", x,y,pressure);
	}

	prev_pen_down = pen_down;
}

int mp200_timer_thread(void *donotuse)
{
	int tmp_irq;

	while (1) {
		event_flag = 0;
		if (mp200_ts_time_flag == 1) {
			mod_timer(&mp200_ts_scan_timer, jiffies + (MP200_TS_SCAN_TIME*HZ / 1000));
		}
		if (mp200_ts_time_flag == 2) {
			mp200_ts_time_flag = 0;
			tmp_irq = tsc_irq;
			tsc_irq = 0;
			enable_irq(tmp_irq);
		}
		wait_event_freezable(tsc_queue, event_flag);
		if (mp200_timer_thread_flag == 0) break;
		mp200_tsc2007_get_tscdata();
	}

	return 0;
}

static irqreturn_t mp200_tsc2007_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs)
{
	if (debug)
		printk("%s: irq handler called\n",__FUNCTION__);

	tsc_irq = this_irq;

	disable_irq(tsc_irq);

	event_flag = 1;
	wake_up(&tsc_queue);

	return IRQ_HANDLED;
}

static void mp200_init_gpio(void)
{
	gpio_direction_input(MP200_TS_PIN);
	set_irq_type(MP200_TS_IRQ, IRQ_TYPE_EDGE_FALLING);
}

static int mp200_tsc2007_probe(struct i2c_adapter *adapter, int addr, int kind)
{
	struct i2c_client *client;
	int rc;

	if (debug)
		printk("%s: begin\n",__FUNCTION__);

	client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
	if (!client)
		return -ENOMEM;

	strncpy(client->name, "mp200_tsc2007", I2C_NAME_SIZE);
	client->addr = addr;
	client->adapter = adapter;
	client->driver = &mp200_tsc2007_driver;

	if ((rc = i2c_attach_client(client)) != 0) {
		kfree(client);
		return rc;
	}

	save_client = client;

	init_timer(&mp200_ts_scan_timer);

	init_waitqueue_head(&tsc_queue);
	kernel_thread(mp200_timer_thread, NULL, CLONE_FS|CLONE_FILES);

	mp200_init_gpio();

	/* register GPIO IRQ */
	if (request_irq(MP200_TS_IRQ, (void*)mp200_tsc2007_irq_handler, IRQF_DISABLED, "mp200-tsc2007", 0) < 0) {
		printk("%s: failed to register the irq\n", __FUNCTION__);
	}

	return 0;
}

static int mp200_tsc2007_attach_adapter(struct i2c_adapter *adapter)
{
	if (debug) {
		printk("%s: begin\n", __FUNCTION__);
	}

	return i2c_probe(adapter, &addr_data, mp200_tsc2007_probe);
}

static int mp200_tsc2007_detach_client(struct i2c_client *client)
{
	if (debug)
		printk("%s: begin\n", __FUNCTION__);

	return 0;
}


static struct i2c_driver mp200_tsc2007_driver = {
	.driver = {
		.name	= "mp200_tsc2007",
	},
	.attach_adapter	= mp200_tsc2007_attach_adapter,
	.detach_client	= mp200_tsc2007_detach_client,
	.command	= mp200_tsc2007_command,
};

static int __init mp200_tsc2007_init(void)
{
	int rc = 0;

#if defined(CONFIG_MACH_PGPF)
    pgpf_tsc2007_control_reset_signal(1);
#endif    
        
	if (debug) {
		printk("%s: %s\n", TSC_NAME, TSC_VERSION);
	}

	inpdev = input_allocate_device();
	if (!inpdev) {
		printk("%s: error allocating memory for input structure\n",__FUNCTION__);
		return -EBUSY;
	}

	inpdev->name = TSC_NAME;
	inpdev->id.bustype = BUS_I2C;

	inpdev->id.vendor = 0x10b7;
	inpdev->id.product = 0x0001;
	inpdev->id.version = 0x0100;

	inpdev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
	inpdev->keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
//	inpdev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
	inpdev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);

	/* value based on board measurement */
#ifdef USE_ANDROID
	input_set_abs_params(inpdev, ABS_X,       60, 4000, 0, 0);
	input_set_abs_params(inpdev, ABS_Y,      120, 3750, 0, 0);
	input_set_abs_params(inpdev, ABS_PRESSURE, 0,  200, 0, 0);
#else
	input_set_abs_params(inpdev, ABS_X,        0, 4095, 0, 0);
	input_set_abs_params(inpdev, ABS_Y,        0, 4095, 0, 0);
	input_set_abs_params(inpdev, ABS_PRESSURE, 0,  200, 0, 0);
#endif

	rc = input_register_device(inpdev);
	if (rc != 0) {
		printk("%s: error registering with input system\n",__FUNCTION__);
	}

	rc = i2c_add_driver(&mp200_tsc2007_driver);
	if (rc != 0) {
		printk("%s: error initializing i2c...\n",__FUNCTION__);
	  	printk("%s: unregistering with input system\n",__FUNCTION__);
		input_unregister_device(inpdev);
		return rc;
	}

	return rc;
}

static void __exit mp200_tsc2007_exit(void)
{
	/* Exit kernel_thread */
	event_flag = 1;
	wake_up(&tsc_queue);
	mp200_timer_thread_flag = 0;

	input_unregister_device(inpdev);

	if (save_client) {
	   if (i2c_detach_client(save_client) != 0) {
		   kfree(save_client);
	   }
	}

	i2c_del_driver(&mp200_tsc2007_driver);

	free_irq(MP200_TS_IRQ,NULL);

#if defined(CONFIG_MACH_PGPF)
    pgpf_tsc2007_control_reset_signal(0);
#endif
}

MODULE_AUTHOR("NECEL");
MODULE_DESCRIPTION("Driver for the Micro Touch Screen Controller with I2C interface(TSC2007)");
MODULE_LICENSE("GPL");
MODULE_VERSION(TSC_VERSION);

module_init(mp200_tsc2007_init);
module_exit(mp200_tsc2007_exit);
