/*
 * TSC2046 Touchscreen driver
 *
 * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
 *
 * Communication details from original TI driver
 * Copyright (C) 2004-2005 Texas Instruments, Inc.
 *
 * Structure based heavily on TSC2301 driver
 * Copyright (C) 2005-2006 Nokia Corporation
 *
 * 2007 (c) MontaVista Software, Inc. This file is licensed under
 * the terms of the GNU General Public License version 2. This program
 * is licensed "as is" without any warranty of any kind, whether express
 * or implied.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>

#ifdef CONFIG_ARCH_OMAP
#include <asm/arch/gpio.h>
#endif

#include <linux/spi/tsc2046.h>

/* TSC2046 commands */
#define START_BYTE		0x8000
#define	X_CMDWD			0xd300
#define Y_CMDWD			0x9300
#define Z1_CMDWD		0xb300
#define Z2_CMDWD		0xc300

#define TSC2046_TS_SCAN_TIME	1
#define MAX_12BIT		((1 << 12) - 1)

#define MIN_ABS_X    528
#define MAX_ABS_X    3760
#define MIN_ABS_Y    624
#define MAX_ABS_Y    3760

static u16 old_x;
static u16 old_y;
static u16 old_pressure;

struct tsc2046_ts {
	struct input_dev	*idev;
	char			phys[32];
	struct timer_list	timer;
	spinlock_t		lock;

	struct spi_transfer	read_xfer[3];
	struct spi_message	read_msg;
	struct spi_message	enable_msg;
	u16                     data[5];

	u16			x;
	u16			y;
	u16			p;
	int			sample_cnt;

	int			ignore_last : 1;
	u16			x_plate_ohm;
	int			max_pressure;
	int			touch_pressure;
	int			pressure_limit;

	int			pointercal[7];
	u16			x_res;
	u16			y_res;

	u16			irq_enabled:1;
	u16			pen_down:1;
	u16			disabled:1;
	u16			pending:1;
	u16			use_pointercal:1;

	s16			dav_gpio;
	int			irq;
};

#define PA ts->pointercal[0]
#define PB ts->pointercal[1]
#define PC ts->pointercal[2]
#define PD ts->pointercal[3]
#define PE ts->pointercal[4]
#define PF ts->pointercal[5]
#define PS ts->pointercal[6]


static const u16 tsc2046_ts_cmd_data[] = {
	START_BYTE, X_CMDWD, Y_CMDWD, Z1_CMDWD, Z2_CMDWD,
};

static int device_suspended(struct device *dev)
{
	struct tsc2046 *tsc = dev_get_drvdata(dev);
	return dev->power.power_state.event != PM_EVENT_ON || tsc->ts->disabled;
}

static void update_pen_state(struct tsc2046 *tsc, int x, int y, int pressure)
{
	struct tsc2046_ts *ts = tsc->ts;

   if (x <= old_x - 4 || x >= old_x + 4 ||
       y <= old_y - 4 || y >= old_y + 4 ||
       pressure <= old_pressure - 4 || pressure >= old_pressure + 4)
   {
      if (pressure) {
         if (!ts->use_pointercal || (ts->use_pointercal &&
                      x >= 0 &&
                      x <= ts->x_res - 1 &&
                      y >= 0 &&
                      y <= ts->y_res - 1))
         {
            input_report_abs(ts->idev, ABS_X, x);
            input_report_abs(ts->idev, ABS_Y, y);
            input_report_abs(ts->idev, ABS_PRESSURE, 1);
            input_report_key(ts->idev, BTN_TOUCH, 1);
            ts->pen_down = 1;
         } else {
            input_report_abs(ts->idev, ABS_PRESSURE, 0);
            input_report_key(ts->idev, BTN_TOUCH, 0);
            ts->pen_down = 0;
         }
      } else {
         input_report_abs(ts->idev, ABS_PRESSURE, 0);
         input_report_key(ts->idev, BTN_TOUCH, 0);
         ts->pen_down = 0;
      }

      old_x = x;
      old_y = y;
      old_pressure = pressure;

      input_sync(ts->idev);

#ifdef VERBOSE
      dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure);
#endif
   }
}

#define CONV_DATA(d1, d2) \
  (((d1 & 0x7f) << 5) | ((d2 >> 11) & 0x1f))

static ssize_t tsc2046_ts_use_pointercal_show(struct device *dev,
				       struct device_attribute *attr, char *buf)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;

   return sprintf(buf, "%u\n", ts->use_pointercal);
}

static ssize_t tsc2046_ts_use_pointercal_store(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;
	char *endp;
	int i;

	i = simple_strtoul(buf, &endp, 10);
	spin_lock_irq(&ts->lock);

   if (i)
      ts->use_pointercal = 1;
	else
      ts->use_pointercal = 0;

	spin_unlock_irq(&ts->lock);

	return count;
}

static DEVICE_ATTR(use_pointercal, 0644, tsc2046_ts_use_pointercal_show, tsc2046_ts_use_pointercal_store);

static ssize_t tsc2046_ts_pointercal_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;

	return sprintf(buf, "%d %d %d %d %d %d %d\n", PA, PB, PC, PD, PE, PF, PS);
}

static ssize_t tsc2046_ts_pointercal_store(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;
	char *endp;
	int i;
   unsigned int index = 0;

	spin_lock_irq(&ts->lock);

   endp = buf;
   for (index = 0; index < 7; index++) {
      /* Grab the numbers */
      i = simple_strtol(endp, &endp, 10);
      ts->pointercal[index] = i;

      /* Skip the space */
      endp++;
   }

	spin_unlock_irq(&ts->lock);

	return count;
}

static DEVICE_ATTR(pointercal, 0644, tsc2046_ts_pointercal_show, tsc2046_ts_pointercal_store);

static ssize_t tsc2046_ts_screen_res_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;

	return sprintf(buf, "%u %u\n", ts->x_res, ts->y_res);
}

static ssize_t tsc2046_ts_screen_res_store(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;
	char *endp;
	unsigned int i;

	spin_lock_irq(&ts->lock);

   endp = buf;

   /* Grab the x res */
   i = simple_strtoul(endp, &endp, 10);
   ts->x_res = i;

   /* Skip the space */
   endp++;

   /* Grab the y res */
   i = simple_strtoul(endp, &endp, 10);
   ts->y_res = i;

   /* Update the device parameters */
	input_set_abs_params(ts->idev, ABS_X, 0, ts->x_res - 1, 0, 0);
	input_set_abs_params(ts->idev, ABS_Y, 0, ts->y_res - 1, 0, 0);

	spin_unlock_irq(&ts->lock);

	return count;
}

static DEVICE_ATTR(screen_res, 0644, tsc2046_ts_screen_res_show, tsc2046_ts_screen_res_store);

/*
 * This procedure is called by the SPI framework after the coordinates
 * have been read from TSC2046
 */
static void tsc2046_ts_rx(void *arg)
{
	struct tsc2046 *tsc = arg;
	struct tsc2046_ts *ts = tsc->ts;
	unsigned int x, y, z1, z2, pressure;
   int xN, yN;

	x  = CONV_DATA(ts->data[2], ts->data[3]);
	y  = CONV_DATA(ts->data[1], ts->data[2]);
	z1 = CONV_DATA(ts->data[3], ts->data[4]);
	z2 = CONV_DATA(ts->data[4], 0);

	if (z1) {
		pressure = ts->x_plate_ohm * x;
		pressure /= 4096;
		pressure *= z2 - z1;
		pressure /= z1;
	} else
		pressure = 0;

   if (ts->use_pointercal) {
      /* Transform pen state for Android */
      xN = ((PA * x) + (PB * y) + PC) / PS;
      yN = ((PD * x) + (PE * y) + PF) / PS;

      x = (xN < 0) ? 0 : (xN > ts->x_res ? ts->x_res - 1 : (unsigned int)xN);
      y = (yN < 0) ? 0 : (yN > ts->y_res ? ts->y_res - 1 : (unsigned int)yN);
   }

	/* If pressure value is above a preset limit (pen is barely
	 * touching the screen) we can't trust the coordinate values.
	 */
	if (pressure < ts->pressure_limit && x < MAX_12BIT && y < MAX_12BIT) {
		ts->pressure_limit = ts->max_pressure;
		if (ts->ignore_last) {
			if (ts->sample_cnt)
				update_pen_state(tsc, ts->x, ts->y, ts->p);
			ts->x = x;
			ts->y = y;
			ts->p = pressure;
		} else
			update_pen_state(tsc, x, y, pressure);
		ts->sample_cnt++;
	}

	mod_timer(&ts->timer,
		  jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
}

static int is_pen_down(struct tsc2046_ts *ts)
{
	return ts->pen_down;
}

/*
 * Timer is called every TSC2046_TS_SCAN_TIME when the pen is down
 */
static void tsc2046_ts_timer(unsigned long arg)
{
	struct tsc2046 *tsc = (void *) arg;
	struct tsc2046_ts *ts = tsc->ts;
	unsigned long flags;
	int ndav;
	int r;

	spin_lock_irqsave(&ts->lock, flags);
	ndav = omap_get_gpio_datain(ts->dav_gpio);
	if (ndav || device_suspended(&tsc->spi->dev)) {
		/* Pen has been lifted */
		if (!device_suspended(&tsc->spi->dev) &&
		    !ts->irq_enabled) {
			ts->irq_enabled = 1;
			enable_irq(ts->irq);
		}
		update_pen_state(tsc, 0, 0, 0);
		ts->pending = 0;
		spin_unlock_irqrestore(&ts->lock, flags);

	} else {
		ts->pen_down = 1;
		spin_unlock_irqrestore(&ts->lock, flags);

		r = spi_async(tsc->spi, &ts->read_msg);
		if (r)
			dev_err(&tsc->spi->dev, "ts: spi_async() failed");
	}
}

/*
 * This interrupt is called when pen is down and first coordinates are
 * available. That is indicated by a falling edge on DEV line.  IRQ is
 * disabled here because while the pen is down the coordinates are
 * read by a timer.
 */
static irqreturn_t tsc2046_ts_irq_handler(int irq, void *dev_id)
{
	struct tsc2046 *tsc = dev_id;
	struct tsc2046_ts *ts = tsc->ts;
	unsigned long flags;

	spin_lock_irqsave(&ts->lock, flags);
	if (ts->irq_enabled) {
		ts->irq_enabled = 0;
		disable_irq(ts->irq);
		ts->pending = 1;
		ts->pressure_limit = ts->touch_pressure;
		ts->sample_cnt = 0;
		mod_timer(&ts->timer,
			  jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
	}
	spin_unlock_irqrestore(&ts->lock, flags);

	return IRQ_HANDLED;
}

/* Must be called with ts->lock held */
static void tsc2046_ts_disable(struct tsc2046 *tsc)
{
	struct tsc2046_ts *ts = tsc->ts;

	if (ts->disabled)
		return;

	ts->disabled = 1;
	if (!ts->pending) {
		ts->irq_enabled = 0;
		disable_irq(ts->irq);
	} else {
		while (ts->pending) {
			spin_unlock_irq(&ts->lock);
			msleep(1);
			spin_lock_irq(&ts->lock);
		}
	}
}

static void tsc2046_ts_enable(struct tsc2046 *tsc)
{
	struct tsc2046_ts *ts = tsc->ts;

	if (!ts->disabled)
		return;

	ts->disabled = 0;
	ts->irq_enabled = 1;
	enable_irq(ts->irq);
}

#ifdef CONFIG_PM
int tsc2046_ts_suspend(struct tsc2046 *tsc)
{
	struct tsc2046_ts *ts = tsc->ts;

	spin_lock_irq(&ts->lock);
	tsc2046_ts_disable(tsc);
	spin_unlock_irq(&ts->lock);

	return 0;
}

void tsc2046_ts_resume(struct tsc2046 *tsc)
{
	struct tsc2046_ts *ts = tsc->ts;

	spin_lock_irq(&ts->lock);
	tsc2046_ts_enable(tsc);
	spin_unlock_irq(&ts->lock);
}
#endif

static void tsc2046_ts_setup_spi_xfer(struct tsc2046 *tsc)
{
	struct tsc2046_ts *ts = tsc->ts;
	struct spi_message *m = &ts->read_msg;
	struct spi_transfer *x = &ts->read_xfer[1];

	spi_message_init(m);

	/* read and write data in one transaction */
	x->tx_buf = &tsc2046_ts_cmd_data;
	x->rx_buf = &ts->data;
	x->len    = 10;
	spi_message_add_tail(x, m);

	/* send another START_BYTE to (re)enable pen interrupts */
	x++;
	x->tx_buf = &tsc2046_ts_cmd_data[0];
	x->rx_buf = &ts->data;
	x->len = 2;
	spi_message_add_tail(x, m);

	m->complete = tsc2046_ts_rx;
	m->context = tsc;
}

static ssize_t tsc2046_ts_pen_down_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	struct tsc2046 *tsc = dev_get_drvdata(dev);

	return sprintf(buf, "%u\n", is_pen_down(tsc->ts));
}

static DEVICE_ATTR(pen_down, S_IRUGO, tsc2046_ts_pen_down_show, NULL);

static ssize_t tsc2046_ts_disable_show(struct device *dev,
				       struct device_attribute *attr, char *buf)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;

	return sprintf(buf, "%u\n", ts->disabled);
}

static ssize_t tsc2046_ts_disable_store(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	struct tsc2046		*tsc = dev_get_drvdata(dev);
	struct tsc2046_ts	*ts = tsc->ts;
	char *endp;
	int i;

	i = simple_strtoul(buf, &endp, 10);
	spin_lock_irq(&ts->lock);

	if (i)
		tsc2046_ts_disable(tsc);
	else
		tsc2046_ts_enable(tsc);

	spin_unlock_irq(&ts->lock);

	return count;
}

static DEVICE_ATTR(disable_ts, 0664, tsc2046_ts_disable_show,
		   tsc2046_ts_disable_store);

int __devinit tsc2046_ts_init(struct tsc2046 *tsc,
			      struct tsc2046_platform_data *pdata)
{
	struct tsc2046_ts *ts;
	struct input_dev *idev;
	int dav_gpio, r;

	if (pdata->dav_gpio < 0) {
		dev_err(&tsc->spi->dev, "need DAV GPIO");
		return -EINVAL;
	}
	dav_gpio = pdata->dav_gpio;

	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
	if (ts == NULL)
		return -ENOMEM;
	tsc->ts = ts;

	ts->dav_gpio = dav_gpio;
#ifdef CONFIG_ARCH_OMAP
	r = omap_request_gpio(dav_gpio);
	if (r < 0) {
		dev_err(&tsc->spi->dev, "unable to get DAV GPIO");
		goto err1;
	}
	omap_set_gpio_direction(dav_gpio, 1);
	if (pdata->gpio_debounce) {
		omap_set_gpio_debounce(tsc->gpio, 1);
		omap_set_gpio_debounce_time(tsc->gpio, pdata->gpio_debounce);
	}

	ts->irq = OMAP_GPIO_IRQ(dav_gpio);
#endif
	init_timer(&ts->timer);
	ts->timer.data = (unsigned long) tsc;
	ts->timer.function = tsc2046_ts_timer;

	spin_lock_init(&ts->lock);

	ts->x_plate_ohm	= pdata->ts_x_plate_ohm ? : 280;
	ts->max_pressure= pdata->ts_max_pressure ? : MAX_12BIT;
	ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure;
	ts->ignore_last	= pdata->ts_ignore_last;

	idev = input_allocate_device();
	if (idev == NULL) {
		r = -ENOMEM;
		goto err2;
	}
	idev->name = "TSC2046 touchscreen";
	snprintf(ts->phys, sizeof(ts->phys),
		 "%s/input-ts", tsc->spi->dev.bus_id);
	idev->phys = ts->phys;

	idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
	idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
	ts->idev = idev;

	tsc2046_ts_setup_spi_xfer(tsc);

   /* X and Y res default to 240x320 */
   ts->x_res = 240;
   ts->y_res = 320;

	input_set_abs_params(idev, ABS_X, 0, ts->x_res - 1, 0, 0);
	input_set_abs_params(idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
	input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);

	set_bit(BTN_TOUCH, idev->keybit);
/*    bitmap_fill(idev->absbit, ABS_MAX);*/

	ts->irq_enabled = 1;
	r = request_irq(ts->irq, tsc2046_ts_irq_handler,
			IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
			"tsc2046-ts", tsc);
	if (r < 0) {
		dev_err(&tsc->spi->dev, "unable to get DAV IRQ");
		goto err3;
	}
	set_irq_wake(ts->irq, 1);

	if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0)
		goto err4;
	if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0)
		goto err5;
   if (device_create_file(&tsc->spi->dev, &dev_attr_use_pointercal) < 0)
      goto err6;
   if (device_create_file(&tsc->spi->dev, &dev_attr_pointercal) < 0)
      goto err7;
   if (device_create_file(&tsc->spi->dev, &dev_attr_screen_res) < 0)
      goto err8;

	r = input_register_device(idev);
	if (r < 0) {
		dev_err(&tsc->spi->dev, "can't register touchscreen device\n");
		goto err9;
	}

	/* kick off a transaction to enable pen interrupts */
	spi_async(tsc->spi, &ts->read_msg);

	return 0;
err9:
   device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
err8:
   device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
err7:
   device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
err6:
	device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
err5:
	device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
err4:
	free_irq(ts->irq, tsc);
err3:
	input_free_device(idev);
err2:
#ifdef CONFIG_ARCH_OMAP
	omap_free_gpio(dav_gpio);
#endif
 err1:
	kfree(ts);
	return r;
}
EXPORT_SYMBOL(tsc2046_ts_init);

void __devexit tsc2046_ts_exit(struct tsc2046 *tsc)
{
	struct tsc2046_ts *ts = tsc->ts;
	unsigned long flags;

	spin_lock_irqsave(&ts->lock, flags);
	tsc2046_ts_disable(tsc);
	spin_unlock_irqrestore(&ts->lock, flags);

	device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
	device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
	device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
	device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
	device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);

	free_irq(ts->irq, tsc);
	input_unregister_device(ts->idev);

#ifdef CONFIG_ARCH_OMAP
	omap_free_gpio(ts->dav_gpio);
#endif
	kfree(ts);
}
EXPORT_SYMBOL(tsc2046_ts_exit);


static int __devinit tsc2046_probe(struct spi_device *spi)
{
	struct tsc2046			*tsc;
	struct tsc2046_platform_data	*pdata = spi->dev.platform_data;
	int r = -ENODEV;

	dev_dbg(&spi->dev, "%s\n", __FUNCTION__);

	if (!pdata) {
		dev_dbg(&spi->dev, "no platform data?\n");
		return -ENODEV;
	}

	tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
	if (tsc == NULL)
		return -ENOMEM;

	dev_set_drvdata(&spi->dev, tsc);
	tsc->spi = spi;
	spi->dev.power.power_state = PMSG_ON;

	spi->mode = SPI_MODE_1;
	spi->bits_per_word = 16;

	/* The max speed might've been defined by the board-specific
	 * struct */
	if (!spi->max_speed_hz)
		spi->max_speed_hz = TSC2046_HZ;
	spi_setup(spi);

	r = tsc2046_ts_init(tsc, pdata);
	if (r)
		goto err1;

	return 0;
 err1:
	kfree(tsc);
	return r;
}

static int __devexit tsc2046_remove(struct spi_device *spi)
{
	struct tsc2046 *tsc = dev_get_drvdata(&spi->dev);

	dev_dbg(&tsc->spi->dev, "%s\n", __FUNCTION__);

        tsc2046_ts_exit(tsc);
	kfree(tsc);

	return 0;
}

static struct spi_driver tsc2046_driver = {
	.driver = {
		   .name = "tsc2046",
		   .bus = &spi_bus_type,
		   .owner = THIS_MODULE,
	},
	.probe = tsc2046_probe,
	.remove = __devexit_p(tsc2046_remove),
};

static int __init tsc2046_init(void)
{
	printk("TSC2046 driver initializing\n");

	return spi_register_driver(&tsc2046_driver);
}
module_init(tsc2046_init);

static void __exit tsc2046_exit(void)
{
	spi_unregister_driver(&tsc2046_driver);
}
module_exit(tsc2046_exit);

MODULE_AUTHOR("Kevin Hilman <khilman@mvista.com>");
MODULE_LICENSE("GPL");
