/*
* Driver interface to the touchscreen on the iPAQ H4000
*
* Copyright (C) 2003 Joshua Wise
*
* Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is
* preserved in its entirety in all copies and derived works.
*
* HAL code based on h5400_asic_io.c, which is
*  Copyright (C) 2003 Compaq Computer Corporation.
*
* TS Code:  Joshua Wise <joshua at joshuawise.com> June 2003
* 
* InputDev Code: Luke Kenneth Casson Leighton <lkcl@lkcl.net> (Project Xanadux)
*
* ??-04-2004: import costant definition from 2.4 kernel and make to compile with 2.6 kenel Shawn Anderson
* 10-04-2004: Renamed h1900_ts.c to h4000_ts.c Eddi De Pieri
* 11-04-2004: merge Input device code from Xanadux Project with h4000_ts.c patched by sa Eddi
* 16-04-2004: small adjustment - removed first and last output from spi_ctrl - removed jitter - Eddi
*
* TODO:
* clean code
* decide to port dejitter to tsdev.c (perhaps better)
*
*/

#include <linux/module.h>
#include <linux/version.h>
#include <linux/config.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>

#include <asm/arch/hardware.h>
// doesnt exist in 2.6 ...
//#include <asm/arch-sa1100/h3600_hal.h>

#include <asm/irq.h>

#include <asm/mach/irq.h>
#include <asm/arch-pxa/h4000-gpio.h>
#include <asm/hardware/ipaq-asic3.h>

#include <linux/input.h>

#define SAMPLE_TIMEOUT 5	/* sample every 10ms */

/**** temporary 
    FIXME: These should be completed and put into asm/mach/pxa-regs.h  
           see the Intel® PXA255 Processor Developer's Manual ****/

#define SSCR0_SSE_ENABLED       0x80 /* bit  7 of SSCR0; 1=on 0=off*/
#define SSCR0_ECS_USE_SSPEXTCLK 0x40 /* bit  6 of SSCR0; 1=SSPEXTCLK 0=on-chip*/
#define SSCR0_FRF_SPI           0x0  /* bits 5:4 of SSCR0  binary 00*/
#define SSCR0_FRF_SSP           0x10 /* bits 5:4 of SSCR0  binary 01*/
#define SSCR0_FRF_MICROWIRE     0x20 /* bits 5:4 of SSCR0  binary 10*/

#define SSCR1_MWDS_16BIT        0x20 /* bit 5 of SSCR1;  1=16bit */  
#define SSCR1_SPH               0x10 /* bit 4 of SSCR1; motorola*/
#define SSCR1_SPO_FALLING       0x8  /* bit 3 of SSCR1; motorola*/
#define SSCR1_LBM               0x4  /* bit 2 of SSCR1 */
#define SSCR1_TIE               0x2  /* bit 1 of SSCR1 */
#define SSCR1_RIE               0x1  /* bit 0 of SSCR1 */

#define SSSR_RNE                0x8 /* bit 3 of SSSR */
#define SSSR_BSY                0x10 /* bit 4 of SSSR */

// FIXME input.h
#define BUS_PXA_NSSP		0x20

static char ads7846_name[] = "ads7846";
static char ads7846_phys[] = "touchscreen/ads7846";

static struct input_dev dev_ads7846;

//#define SAMPLE_TIMEOUT 40/* sample every 10ms */
#define CONVERSION_TIMEOUT 2/* wait 1ms for a conversion */

#undef UBERBUGGY

static struct timer_list timer;

static unsigned int pbytes[4], nbytes[4];

typedef enum
{
	ADS_DO_NOTHING       = 0,
        ADS_PEN_DOWN_SKIP_CONVERT,
        ADS_PEN_DOWN_EXPECT_CONVERT,
        ADS_PEN_DOWN_CONVERT_DONE,
	ADS_PEN_UP,
    
} ADS_PEN_STATE;
    
static int ads_state = ADS_DO_NOTHING;
static unsigned int spi_ctrl(unsigned char ctrlbyte)
{
	/* prevent a race condition in the while loops */
	int antirace;
	
	SSDR = ctrlbyte;
	
	/* wait until we have completed sending. */
	antirace = 100000;
	while ((SSSR & SSSR_BSY) && antirace)
	{
		udelay(10);
		antirace--;
	};
	
	if (antirace == 0)
	{
		printk("%s: warning: timeout while waiting for sssr_bsy to clear\n", __FUNCTION__);
	};
	
	antirace = 100000;
	/* wait for the receive fifo to become not empty. */
	while ((~SSSR & SSSR_RNE) && antirace)
	{
		udelay(10);
		antirace--;
	};
	
	if (antirace == 0)
	{
		printk("%s: warning: timeout while waiting for sssr_rne to set\n", __FUNCTION__);
	};
	
	return SSDR & 0xFFFF;
};

#define CTRL_START 0x80
#define CTRL_YPOS  0x10
#define CTRL_Z1POS 0x30
#define CTRL_Z2POS 0x40
#define CTRL_XPOS  0x50


static irqreturn_t h4000_pen_up(int irq, void* data, struct pt_regs *regs)
{
	struct input_dev *dev = (struct input_dev *)data;

//	nss_cmd_adc_stopscan_c3d2(0x2);
//nssp_disable();

	/* tell the input thingy that we have been released. */
	input_report_abs(dev, ABS_X, nbytes[0] & 0xfff);
	input_report_abs(dev, ABS_Y, nbytes[1] & 0xfff);
	input_report_abs(dev, ABS_PRESSURE, 0);
	input_sync(dev);

        pbytes[0] =  pbytes[1] = pbytes[2] = pbytes[3] = 0xffff;
	
	ads_state = ADS_PEN_UP;
	    
	return IRQ_HANDLED;
}

static irqreturn_t h4000_pen_down(int irq, void* data, struct pt_regs *regs)
{
	int buswait = 4;

//	nss_cmd_adc_stopscan_c3d2(0x2);
//	nss_cmd_adc_staradsan(
//			ADS2200_CTRLREG_ADC_PSM_ADS2200 |  // controlled by penirq
//			ADS2200_CTRLREG_ADC_AD1 |
//			ADS2200_CTRLREG_ADC_RES(ADS2200_CTRLREG_ADC_RES_12BIT) |
//			ADS2200_CTRLREG_ADC_AVG(ADS2200_CTRLREG_ADC_16AVG) |
//			ADS2200_CTRLREG_ADC_CL(ADS2200_CTRLREG_ADC_CL_1MHZ_12BIT) |
//			ADS2200_CTRLREG_ADC_PV(ADS2200_CTRLREG_ADC_PV_100uS));
	
//	printk("%s: GRER: %8x\n", __FUNCTION__, GRER0);
	
	while (((GRER0 & GPIO_bit(GPIO18_RDY))) && buswait)
	{
	        udelay(1000);
	        buswait--;
	}

	if (buswait)
	{
//		nss_cmd_adc_stopscan_c3d2(0x2);
//		ads_state = ADS_PEN_DOWN_EXPECT_CONVERT;
		ads_state = ADS_PEN_DOWN_SKIP_CONVERT;
		/* and, timerize it. */
		disable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N));
		mod_timer (&timer, jiffies + (CONVERSION_TIMEOUT * HZ) / 1000);
        }
	else
	{
//		unsigned short r4;
    
//		nss_initialise();
//		r4 = get_key(0, 4);
//		printk("%s: bus wait timeout: %d\n", __FUNCTION__, r4);
			    
		//nssp_disable();
		//
		ads_state = ADS_PEN_UP;
	}
    
	return IRQ_HANDLED;
}
    
static irqreturn_t h4000_pen_down_read(int irq, void* data, struct pt_regs *regs)
{
//	unsigned int bytes[4];
        struct input_dev *dev = (struct input_dev *)data;
	
	unsigned int bytes[5][4];
	int cntr;
		
	//nssp_enable();
	//nss_initialise();
		    
	/* read positions. */
    	
	// the order how data (CTRL_XXXX) is read is very important
	// since the last data is spurious, this tricks read the data 
	// from previous cycle
//	nbytes[1] = pbytes[1]; pbytes[1] = spi_ctrl(CTRL_START | CTRL_YPOS);
//	nbytes[2] = pbytes[2]; pbytes[2] = spi_ctrl(CTRL_START | CTRL_Z1POS);
//	nbytes[3] = pbytes[3]; pbytes[3] = spi_ctrl(CTRL_START | CTRL_Z2POS);
//	nbytes[0] = pbytes[0]; pbytes[0] = spi_ctrl(CTRL_START | CTRL_XPOS);
//	pbytes[0] ^= 0xfff;

nbytes[1] = pbytes[1];
nbytes[2] = pbytes[2];
nbytes[3] = pbytes[3];
nbytes[0] = pbytes[0];

pbytes[0] =  pbytes[1] = pbytes[2] = pbytes[3] = 0;

for ( cntr=0; cntr<5; cntr++ ) {
 bytes[cntr][1] = spi_ctrl(CTRL_START | CTRL_YPOS);
 bytes[cntr][2] = spi_ctrl(CTRL_START | CTRL_Z1POS);
 bytes[cntr][3] = spi_ctrl(CTRL_START | CTRL_Z2POS);
 bytes[cntr][0] = spi_ctrl(CTRL_START | CTRL_XPOS);
 udelay(5);
}
for ( cntr=2; cntr<5; cntr++ ) {
 pbytes[1] +=  bytes[cntr][1];
 pbytes[2] +=  bytes[cntr][2];
 pbytes[3] +=  bytes[cntr][3];
 pbytes[0] +=  bytes[cntr][0];
}
 pbytes[1] =  pbytes[1] / 3;
 pbytes[2] =  pbytes[2] / 3;
 pbytes[3] =  pbytes[3] / 3;
 pbytes[0] =  pbytes[0] / 3;

 pbytes[0] ^= 0xfff;
 
//	nss_cmd_adc_staradsan_83d6(0x2);
	//nssp_disable();
	
	/* tell the input thingy about the position */
	/*h3600_hal_touchpanel(bytes[3], bytes[0], 1);*/

	/* FIXME: find out why the read data reg conversion isn't completing
	 * on time
	 */

	if (ads_state == ADS_PEN_DOWN_SKIP_CONVERT){

	if (nbytes[0] != 0xffff && nbytes[1] != 0xffff)
	{
	/* output positions. */
	/* <Hymie> You're an uberdebuggy, NonToxic
	 * <Hymie> YOU"RE AN UBERDEBUGGY */
		 
//#if 1
//#ifdef UBERBUGGY
//    printk("%s: %03X-%03x-%03x-%03x\n", __FUNCTION__, nbytes[0], nbytes[1], nbytes[2], nbytes[3]);
//#endif
//#endif
	        input_report_abs(dev, ABS_X, nbytes[0] & 0xfff);
		input_report_abs(dev, ABS_Y, nbytes[1] & 0xfff);
		input_report_abs(dev, ABS_PRESSURE, 0xfff);
		input_sync(dev);
	}

	}
	ads_state = ADS_PEN_DOWN_CONVERT_DONE;
    
		    
	/* and, timerize it. */
	disable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N));
	mod_timer (&timer, jiffies + (SAMPLE_TIMEOUT * HZ) / 1000);
	
	return IRQ_HANDLED;
}

static irqreturn_t h4000_pen(int irq, void* data, struct pt_regs *regs)
{
	int pressed;
//	struct input_dev *dev = (struct input_dev *)data;
	
	//unsigned char ctrlbyte;
//	unsigned int bytes[4];

	pressed = GPLR(GPIO_NR_H4000_PEN_IRQ_N) & GPIO_bit(GPIO_NR_H4000_PEN_IRQ_N);
	pressed = !pressed;

//#if 0
//#ifdev UBERBUGGY
//	printk("%s: pressed: %d state: %d\n", __FUNCTION__, pressed, ads_state);
//#endif
//#endif

	/* tell the HAL about the position */
        // FIXME: need a replacement for linux 2.6
	//h3600_hal_touchpanel(bytes[3], bytes[0], 1);
	
//	/* and, timerize it. */
//	mod_timer (&timer, jiffies + (SAMPLE_TIMEOUT * HZ) / 1000);

	if (ads_state == ADS_PEN_DOWN_CONVERT_DONE)
	{
		// a conversion has been completed, we need to check if
		// pen is up: if so, do pen up.
		if (!pressed)
		{
			return h4000_pen_up(irq, data, regs);
		};
		// a conversion has been completed, pen is still down, start again
		ads_state = ADS_DO_NOTHING;
	}

	if (ads_state == ADS_PEN_UP && pressed)
		ads_state = ADS_DO_NOTHING;

	if (ads_state == ADS_DO_NOTHING)
	{
		return h4000_pen_down(irq, data, regs);
	}

	/* initiated pen down already, been expecting convert to finish */
	if (ads_state == ADS_PEN_DOWN_EXPECT_CONVERT || ads_state == ADS_PEN_DOWN_SKIP_CONVERT )
    		return h4000_pen_down_read(irq, data, regs);

        return IRQ_HANDLED;
};

static void h4000_ts_timer(unsigned long nr)
{
	h4000_pen(0,(void*)nr,NULL);
	enable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N));
};

int h4000_ts_open( struct input_dev *dev )
{
	if ( !(machine_is_h4000() || machine_is_h4000()) ) {
		printk("%s: unknown iPAQ model\n", __FUNCTION__/*, h3600_generic_name()*/ );
		return -ENODEV;
	}
	
	printk("%s: initializing the touchscreen.\n", __FUNCTION__);
	
	/* disable before mucking about */
	SSCR0 &= ~SSCR0_SSE_ENABLED;
	
	/* set to Microwire mode */
	SSCR0 &= ~0x00000030;
	SSCR0 |= SSCR0_FRF_MICROWIRE;
	
	/* don't use the external clock */
	SSCR0 &= ~SSCR0_ECS_USE_SSPEXTCLK;
	
	/* set to 12-bit mode */
	SSCR0 &= ~0xF;
	SSCR0 |= 0xB;
	
	/* set to 100mhz. */
	SSCR0 &= ~0xFF00;
	SSCR0 |=  0x1100; /* 17 decimal */
	
	/* set to 8-bit command packets */
	SSCR1 &= ~SSCR1_MWDS_16BIT;
	
	/* set polarity and phase to 0 */
	SSCR1 &= ~SSCR1_SPO_FALLING;
	SSCR1 &= ~SSCR1_SPH;

	/* no interrupts, and no loopback mode. */
	SSCR1 &= ~(SSCR1_RIE|SSCR1_TIE|SSCR1_LBM);
	
	/* now to set up GPIOs... */
	GPDR(GPIO23_SCLK) |=  GPIO_bit(GPIO23_SCLK);
	GPDR(GPIO24_SFRM) |=  GPIO_bit(GPIO24_SFRM);
	GPDR(GPIO25_STXD) |=  GPIO_bit(GPIO25_STXD);
	GPDR(GPIO26_SRXD) &= ~GPIO_bit(GPIO26_SRXD);
	//pxa_gpio_mode(GPIO23_SCLK_MD);
	pxa_gpio_mode(GPIO23_SCLK_md); //???
	pxa_gpio_mode(GPIO24_SFRM_MD);
	pxa_gpio_mode(GPIO25_STXD_MD);
	pxa_gpio_mode(GPIO26_SRXD_MD);
	
	/* and enable it! */
	SSCR0 |= SSCR0_SSE_ENABLED;	

        pbytes[0] =  pbytes[1] = pbytes[2] = pbytes[3] = 0xffff;

	printk("%s: init timer...\n", __FUNCTION__);
	/* set up the timer. */
	init_timer(&timer);
	timer.function = h4000_ts_timer;
	timer.data = (unsigned long)dev;
	
	printk("%s: set irq...\n", __FUNCTION__);
	/* now set up the pen action GPIO */

	set_irq_type(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N), IRQT_BOTHEDGE);
	request_irq (IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N), h4000_pen, SA_SAMPLE_RANDOM, "h4000_ts", (void*)dev);
		
	/* we may have to do a read here to make sure that penint comes on, in case it's in a weird state. */
	spi_ctrl(CTRL_START);
	
	printk("%s: set irq...\n", __FUNCTION__);
	
	return 0;
}

void h4000_ts_close( struct input_dev *dev )
{
	disable_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N));

	SSCR0 &= ~SSCR0_SSE_ENABLED;
	
	/* make sure to free the IRQ... */
	free_irq(IRQ_GPIO(GPIO_NR_H4000_PEN_IRQ_N), dev);
	
	/* now free the timer... */
	del_timer_sync (&timer);
}


static int h4000_ts_setup(struct input_dev *dev)
{
    memset(dev, 0x0, sizeof(*dev));

    init_input_dev(dev);
    
    dev->private = dev;
	
    dev->name = ads7846_name;
    dev->phys = ads7846_phys;
    dev->open = h4000_ts_open;
    dev->close = h4000_ts_close;
    
    set_bit(EV_ABS, dev->evbit);
    set_bit(ABS_X, dev->absbit);
    set_bit(ABS_Y, dev->absbit);
    set_bit(ABS_PRESSURE, dev->absbit);
    dev->absmin[ABS_PRESSURE] = 0;
    dev->absmax[ABS_PRESSURE] = 1;
//    dev->absmax[ABS_PRESSURE] = 0xfff;
    dev->absmax[ABS_X] = 0xfff;
    dev->absmax[ABS_Y] = 0xfff;
    dev->absfuzz[ABS_X] = 2;
    dev->absfuzz[ABS_Y] = 2;
    dev->absflat[ABS_X] = 4;
    dev->absflat[ABS_Y] = 4;
    dev->id.bustype = BUS_PXA_NSSP;
    dev->id.vendor = 0xffff;
    dev->id.product = 0xffff;
    dev->id.version = 0xffff;

    return 0;
}
    
static void h4000_ts_uninit(struct input_dev *dev)
{
    dev->private = NULL;
}

static int __init h4000_ts_init(void)
{
    h4000_ts_setup(&dev_ads7846);
    input_register_device(&dev_ads7846);
    return 0;
}
	    
static void __exit h4000_ts_exit(void)
{
    input_unregister_device(&dev_ads7846);
    h4000_ts_uninit(&dev_ads7846);
}

module_init(h4000_ts_init)
module_exit(h4000_ts_exit)

MODULE_AUTHOR("h4000 port team");
MODULE_DESCRIPTION("Touchscreen support for the iPAQ H4000 (ADS7846 over SPI)");
MODULE_DESCRIPTION(" ");
MODULE_LICENSE("Dual BSD/GPL");
