--- arch/arm/mach-ep93xx/include/mach/hardware.h | 1 arch/arm/mach-ep93xx/include/mach/regs_touch.h | 95 ++ drivers/input/touchscreen/Kconfig | 5 drivers/input/touchscreen/Makefile | 1 drivers/input/touchscreen/ep93xx_ts.c | 1117 +++++++++++++++++++++++++ drivers/input/touchscreen/ep93xx_ts.h | 53 + 6 files changed, 1272 insertions(+) --- linux-2.6.32.orig/arch/arm/mach-ep93xx/include/mach/hardware.h +++ linux-2.6.32/arch/arm/mach-ep93xx/include/mach/hardware.h @@ -6,10 +6,11 @@ #include #include #define pcibios_assign_all_busses() 0 +#include "regs_touch.h" /* * The EP93xx has two external crystal oscillators. To generate the * required high-frequency clocks, the processor uses two phase-locked- * loops (PLLs) to multiply the incoming external clock signal to much --- /dev/null +++ linux-2.6.32/arch/arm/mach-ep93xx/include/mach/regs_touch.h @@ -0,0 +1,95 @@ +/*============================================================================= + * + * FILE: regs_touch.h + * + * DESCRIPTION: Analog Touchscreen Register Definition + * + * Copyright Cirrus Logic, 2001-2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *============================================================================= + */ +#ifndef _REGS_TOUCH_H_ +#define _REGS_TOUCH_H_ + +/* + *----------------------------------------------------------------------------- + * Individual bit #defines + *----------------------------------------------------------------------------- + */ +#define TSSETUP_SDLY_MASK 0x000003FF +#define TSSETUP_SDLY_SHIFT 0 +#define TSSETUP_NSMP_4 0x00000000 +#define TSSETUP_NSMP_8 0x00000400 +#define TSSETUP_NSMP_16 0x00000800 +#define TSSETUP_NSMP_32 0x00000C00 +#define TSSETUP_NSMP_MASK 0x00000C00 +#define TSSETUP_DEV_4 0x00000000 +#define TSSETUP_DEV_8 0x00001000 +#define TSSETUP_DEV_12 0x00002000 +#define TSSETUP_DEV_16 0x00003000 +#define TSSETUP_DEV_24 0x00004000 +#define TSSETUP_DEV_32 0x00005000 +#define TSSETUP_DEV_64 0x00006000 +#define TSSETUP_DEV_128 0x00007000 +#define TSSETUP_ENABLE 0x00008000 +#define TSSETUP_DLY_MASK 0x03FF0000 +#define TSSETUP_DLY_SHIFT 16 +#define TSSETUP_TDTCT 0x80000000 + +#define TSMAXMIN_XMIN_MASK 0x000000FF +#define TSMAXMIN_XMIN_SHIFT 0 +#define TSMAXMIN_YMIN_MASK 0x0000FF00 +#define TSMAXMIN_YMIN_SHIFT 8 +#define TSMAXMIN_XMAX_MASK 0x00FF0000 +#define TSMAXMIN_XMAX_SHIFT 16 +#define TSMAXMIN_YMAX_MASK 0xFF000000 +#define TSMAXMIN_YMAX_SHIFT 24 + +#define TSXYRESULT_X_MASK 0x00000FFF +#define TSXYRESULT_X_SHIFT 0 +#define TSXYRESULT_AD_MASK 0x0000FFFF +#define TSXYRESULT_AD_SHIFT 0 +#define TSXYRESULT_Y_MASK 0x0FFF0000 +#define TSXYRESULT_Y_SHIFT 16 +#define TSXYRESULT_SDR 0x80000000 + +#define TSX_SAMPLE_MASK 0x00003FFF +#define TSX_SAMPLE_SHIFT 0x00 +#define TSY_SAMPLE_MASK 0x3FFF0000 +#define TSY_SAMPLE_SHIFT 0x10 + +#define TSSETUP2_TINT 0x00000001 +#define TSSETUP2_NICOR 0x00000002 +#define TSSETUP2_PINT 0x00000004 +#define TSSETUP2_PENSTS 0x00000008 +#define TSSETUP2_PINTEN 0x00000010 +#define TSSETUP2_DEVINT 0x00000020 +#define TSSETUP2_DINTEN 0x00000040 +#define TSSETUP2_DTMEN 0x00000080 +#define TSSETUP2_DISDEV 0x00000100 +#define TSSETUP2_NSIGND 0x00000200 +#define TSSETUP2_S28EN 0x00000400 +#define TSSETUP2_RINTEN 0x00000800 + +#define TSXYRESULT_SDR 0x80000000 + +/* + *----------------------------------------------------------------------------- + *----------------------------------------------------------------------------- + */ + + +#endif /* _REGS_TOUCH_H_ */ --- linux-2.6.32.orig/drivers/input/touchscreen/Kconfig +++ linux-2.6.32/drivers/input/touchscreen/Kconfig @@ -528,6 +528,11 @@ config TOUCHSCREEN_PCAP Say Y here if you have a Motorola EZX telephone and want to enable support for the built-in touchscreen. To compile this driver as a module, choose M here: the module will be called pcap_ts. + +config TOUCHSCREEN_EP93XX + tristate "EP93xx Touchscreen" + depends on ARM && INPUT && ARCH_EP93XX + endif --- linux-2.6.32.orig/drivers/input/touchscreen/Makefile +++ linux-2.6.32/drivers/input/touchscreen/Makefile @@ -40,5 +40,6 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) + obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o +obj-$(CONFIG_TOUCHSCREEN_EP93XX) += ep93xx_ts.o --- /dev/null +++ linux-2.6.32/drivers/input/touchscreen/ep93xx_ts.c @@ -0,0 +1,1117 @@ +/* + * linux/drivers/input/touchscreen/ep93xx_ts.c + * + * Copyright (C) 2003-2004 Cirrus Corp. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TOUCH_OFFSET 0x100000 +#define TOUCH_BASE (EP93XX_APB_VIRT_BASE|TOUCH_OFFSET) +#define TSSetup (TOUCH_BASE+0x00) /* R/W touchscreen controller setup control register. */ +#define TSXYMaxMin (TOUCH_BASE+0x04) /* R/W touchscreen controller max/min register. */ +#define TSXYResult (TOUCH_BASE+0x08) /* R touchscreen controller result register. */ +#define TSDischarge (TOUCH_BASE+0x0C) /* LOCKED R/W touchscreen Switch Matrix control register. */ +#define TSXSample (TOUCH_BASE+0x10) /* LOCKED R/W touchscreen Switch Matrix control register. */ +#define TSYSample (TOUCH_BASE+0x14) /* LOCKED R/W touchscreen Switch Matrix control register. */ +#define TSDirect (TOUCH_BASE+0x18) /* LOCKED R/W touchscreen Switch Matrix control register. */ +#define TSDetect (TOUCH_BASE+0x1C) /* LOCKED R/W touchscreen Switch Matrix control register. */ +#define TSSWLock (TOUCH_BASE+0x20) /* NA R/W touchscreen software lock register. */ +#define TSSetup2 (TOUCH_BASE+0x24) /* R/W touchscreen setup control register #2. */ + +// +// To customize for a new touchscreen, there are various macros that +// have to be set. If you allow UART_HACK_DEBUG to be defined, you +// will get real time ts data scrolling up your serial terminal +// screen that will help you empirically determine good values for these. +// + +// +// These are used as trigger levels to know when we have pen up/down +// +// The rules: +// 1. TS_HEAVY_INV_PRESSURE < TS_LIGHT_INV_PRESSURE because these +// are Inverse pressure. +// 2. Any touch lighter than TS_LIGHT_INV_PRESSURE is a pen up. +// 3. Any touch heavier than TS_HEAVY_INV_PRESSURE is a pen down. +// +#define TS_HEAVY_INV_PRESSURE 0xFE0 //C00 +#define TS_LIGHT_INV_PRESSURE 0xFFF //e00 + +// +// If the x, y, or inverse pressure changes more than these values +// between two succeeding points, the point is not reported. +// +#define TS_MAX_VALID_XY_CHANGE 0x300 +#define TS_MAX_VALID_PRESSURE_CHANGE 0x100 + +// +// This is the minimum Z1 Value that is valid. +// +#define MIN_Z1_VALUE 0x50 + +// +// Settling delay for taking each ADC measurement. Increase this +// if ts is jittery. +// +#define EP93XX_TS_ADC_DELAY_USEC 2000 + +// +// Delay between TS points. +// +#define EP93XX_TS_PER_POINT_DELAY_USEC 10000 + +//----------------------------------------------------------------------------- +// Debug messaging thru the UARTs +//----------------------------------------------------------------------------- +/* + * Hello there! Are you trying to get this driver to work with a new + * touschscreen? Turn this on and you will get useful info coming + * out of your serial port. + */ +/* #define PRINT_CALIBRATION_FACTORS */ +#ifdef PRINT_CALIBRATION_FACTORS +#define UART_HACK_DEBUG 1 +int iMaxX=0, iMaxY=0, iMinX = 0xfff, iMinY = 0xfff; +#endif + +/* + * For debugging, let's spew messages out serial port 1 or 3 at 57,600 baud. + */ +/* #define UART_HACK_DEBUG 1 */ +#if 0 +#ifdef UART_HACK_DEBUG +static char szBuf[256]; +void UARTWriteString(char * msg); +#define DPRINTK( x... ) \ + sprintf( szBuf, ##x ); \ + UARTWriteString( szBuf ); +#else +static char szBuf[256]; +#define DPRINTK( x... ) \ + sprintf( szBuf, ##x ); \ + printk( szBuf ); +#endif +#endif // 0 +#define DPRINTK( x... ) + +//----------------------------------------------------------------------------- +// A few more macros... +//----------------------------------------------------------------------------- +#define TSSETUP_DEFAULT ( TSSETUP_NSMP_32 | TSSETUP_DEV_64 | \ + ((128<>2] & 0x20); + + // send a char to the uart + pDebugUART[0] = value; +} + +void UARTWriteString(char * msg) +{ + int index = 0; + unsigned int uiTemp; + + //if((pDebugUART[0x14>>2] & 0x1) == 0) + if( bUartInitialized == 0 ) + { + uiTemp = inl(EP93XX_SYSCON_DEVCFG); + uiTemp |= EP93XX_SYSCON_DEVCFG_U1E; + //uiTemp |= EP93XX_SYSCON_DEVCFG_U3E; + SysconSetLocked(EP93XX_SYSCON_DEVCFG, uiTemp); + pDebugUART[0x10>>2] = 0xf; + pDebugUART[0xc>>2] = 0; + pDebugUART[0x8>>2] = 0x70; + pDebugUART[0x14>>2] = 0x1; + bUartInitialized = 1; + } + while (msg[index] != 0) + { + if (msg[index] == '\n') + { + SendChar('\r'); + SendChar('\n'); + } + else + { + SendChar(msg[index]); + } + index++; + } +} +#endif // UART_HACK_DEBUG + +/* + * ep93xx_ts_isr + */ +static irqreturn_t ep93xx_ts_isr(int irq, void *dev_id) +{ + DPRINTK("isr\n"); + + // + // Note that we don't clear the interrupt here. The interrupt + // gets cleared in TS_Soft_Scan_Mode when the TS ENABLE + // bit is cleared. + // + + // + // Set the ts to manual polling mode and schedule a callback. + // That way we can return from the isr in a reasonable amount of + // time and process the touch in the callback after a brief delay. + // + TS_Soft_Scan_Mode(); + + return(IRQ_HANDLED); +} + +/* + * Save the current ts 'event' in an atomic fashion. + */ +static void ee93xx_ts_evt_add( int buttons, int iX, int iY, int iPressure ) +{ +#ifdef PRINT_CALIBRATION_FACTORS + if( iX > iMaxX ) iMaxX = iX; + if( iX < iMinX ) iMinX = iX; + if( iY > iMaxY ) iMaxY = iY; + if( iY < iMinY ) iMinY = iY; +#endif + + + // printk("ee93xx_ts_evt_add\n"); + //DPRINTK("cb\n"); + /* + * Note the event, but use spinlocks to keep it from getting + * halfway read if we get interrupted. + */ + + spin_lock(&event_buffer_lock); + gSample.currentX = iX; + gSample.currentY = iY; + gSample.currentButton = buttons; + gSample.currentPressure = iPressure; + bFreshTouchData = 1; + do_gettimeofday(&gSample.currentTime); + + + spin_unlock(&event_buffer_lock); + + kill_fasync(&ep93xx_fasync, SIGIO, POLL_IN); + wake_up_interruptible(&queue); + +} + + +static ssize_t ep93xx_ts_read(struct file *filp, char *buf, size_t count, loff_t *l) +{ + + unsigned short data[3]; + struct ts_sample ts_data; + int iReturn = -EFAULT; + // printk("ep93xx_ts_read\n"); + +#ifdef PRINT_CALIBRATION_FACTORS + static int lala=0; + if( bFreshTouchData && (lala++ > 9) ) + { + DPRINTK("%4d, %4d - range [%4d to %4d],[%4d to %4d]\n", + f, currentY, iMinX, iMaxX, iMinY, iMaxY ); + lala = 0; + } +#endif + if( !bFreshTouchData) + { + iReturn = 0; + } + else if( (count == sizeof(data)) ) + { + spin_lock_irq(&event_buffer_lock); + bFreshTouchData = 0; + data[0] = gSample.currentX; + data[1] = gSample.currentY; + data[2] = gSample.currentButton; + + spin_unlock_irq(&event_buffer_lock); + + if (copy_to_user(buf, data, sizeof data)) + return -EFAULT; + + count -= sizeof(data); + + /* return the # of bytes that got read */ + iReturn = sizeof(data) ; + } + else if (count == sizeof(struct ts_sample) ) + { + spin_lock_irq(&event_buffer_lock); + bFreshTouchData = 0; + ts_data.x = gSample.currentX; + ts_data.y = gSample.currentY; + ts_data.pressure = gSample.currentPressure; + ts_data.tv = gSample.currentTime; + spin_unlock_irq(&event_buffer_lock); + + if (copy_to_user(buf, &ts_data, sizeof(struct ts_sample))) + { + iReturn = -EFAULT; + } + else + { + count -= sizeof(ts_data); + iReturn = sizeof(ts_data); + } + + } + + return iReturn; +} + +static unsigned int ep93xx_ts_poll(struct file *filp, poll_table *wait) +{ + // printk("ep93xx_ts_poll\n"); + poll_wait(filp, &queue, wait); + + if( bFreshTouchData ) + { + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int ep93xx_ts_open(struct inode *inode, struct file *filp) +{ + printk("ep93xx_ts_open"); + + if( down_trylock(&open_sem) ) + { + return -EBUSY; + } + + ep93xx_ts_setup(); + + return 0; +} + +/* + * Asynchronous I/O support. + */ +static int ep93xx_ts_fasync(int fd, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(fd, filp, on, &ep93xx_fasync); + if (retval < 0) + { + return retval; + } + + return 0; +} + +static int ep93xx_ts_release(struct inode *inode, struct file *filp) +{ + Stop_Timer2(); + + /* + * Call our async I/O support to request that this file + * cease to be used for async I/O. + */ + ep93xx_ts_fasync(-1, filp, 0); + + ep93xx_ts_shutdown(); + + up(&open_sem); + + return 0; +} + +static ssize_t ep93xx_ts_write(struct file *file, const char *buffer, size_t count, + loff_t *ppos) +{ + return -EINVAL; +} + + +static int ep93xx_ts_ioctl(struct inode *inode, struct file *file, uint command, ulong u) +{ + static const int version = EV_VERSION; + static const u_int32_t bit =(1 << EV_ABS); + static const u_int32_t absbit = (1 << ABS_X) | (1 << ABS_Y) | (1 << ABS_PRESSURE); + int iReturn ; + int i = 0; + + switch(command) + { + case EVIOCGVERSION: + DPRINTK("ep93xx_ts_ioctl command = EVIOCGVERSION\r\n"); + i = copy_to_user((void __user *)u, (void *)version, sizeof(version)); + iReturn = i ? -EFAULT : 0; + break; + + case EVIOCGBIT(0,sizeof(u_int32_t) * 8) : + DPRINTK("ep93xx_ts_ioctl command = EVIOCGBIT(0,sizeof(uint32) * 8)\r\n"); + i = copy_to_user((void __user *)u, (void *)bit, sizeof(bit)); + iReturn = i ? -EFAULT : 0; + break; + + case EVIOCGBIT(EV_ABS, sizeof(absbit) * 8): + DPRINTK("ep93xx_ts_ioctl command = EVIOCGBIT(0,sizeof(uint32) * 8)\r\n"); + copy_to_user((void __user *)u, (void *)absbit, sizeof(absbit)); + iReturn = i ? -EFAULT : 0; + break; + default: + DPRINTK(" ep93xx_ts_ioctl unknown command = %d\n",u); + iReturn = -1; + break; + } + + return iReturn; +} + +static struct file_operations ep93xx_ts_fops = { + owner: THIS_MODULE, + read: ep93xx_ts_read, + write: ep93xx_ts_write, + poll: ep93xx_ts_poll, + open: ep93xx_ts_open, + ioctl: ep93xx_ts_ioctl, + release: ep93xx_ts_release, + fasync: ep93xx_ts_fasync, +}; + +static struct miscdevice ep93xx_ts_miscdev = +{ + EP93XX_TS_MINOR, + "ep93xx_ts", + &ep93xx_ts_fops +}; + +void ep93xx_ts_setup(void) +{ + unsigned int uiKTDIV, uiTSXYMaxMin; + // printk("ep93xx_hw_setup\n"); + + /* + * Set the TSEN bit in KTDIV so that we are enabling the clock + * for the touchscreen. + */ + uiKTDIV = inl(SYSCON_KTDIV); + uiKTDIV |= SYSCON_KTDIV_TSEN; + SysconSetLocked( SYSCON_KTDIV, uiKTDIV ); + + // + // Program the TSSetup and TSSetup2 registers. + // + outl( TSSETUP_DEFAULT, TSSetup ); + outl( TSSETUP2_DEFAULT, TSSetup2 ); + + // + // Set the the touch settings. + // + outl( 0xaa, TSSWLock ); + outl( sSwitchSettings.uiDischarge, TSDirect ); + + outl( 0xaa, TSSWLock ); + outl( sSwitchSettings.uiDischarge, TSDischarge ); + + outl( 0xaa, TSSWLock ); + outl( sSwitchSettings.uiSwitchZ1, TSXSample ); + + outl( 0xaa, TSSWLock ); + outl( sSwitchSettings.uiSwitchZ2, TSYSample ); + + outl( 0xaa, TSSWLock ); + outl( sSwitchSettings.uiDetect, TSDetect ); + + // + // X,YMin set to 0x40 = have to drag that many pixels for a new irq. + // X,YMax set to 0x40 = 1024 pixels is the maximum movement within the + // time scan limit. + // + uiTSXYMaxMin = (50 << TSMAXMIN_XMIN_SHIFT) & TSMAXMIN_XMIN_MASK; + uiTSXYMaxMin |= (50 << TSMAXMIN_YMIN_SHIFT) & TSMAXMIN_YMIN_MASK; + uiTSXYMaxMin |= (0xff << TSMAXMIN_XMAX_SHIFT) & TSMAXMIN_XMAX_MASK; + uiTSXYMaxMin |= (0xff << TSMAXMIN_YMAX_SHIFT) & TSMAXMIN_YMAX_MASK; + outl( uiTSXYMaxMin, TSXYMaxMin ); + + bCurrentPenDown = 0; + bFreshTouchData = 0; + guiLastX = 0; + guiLastY = 0; + guiLastInvPressure = 0xffffff; + + // + // Enable the touch screen scanning engine. + // + TS_Hardware_Scan_Mode(); +} + +/* + * ep93xx_ts_shutdown + * + */ +static void +ep93xx_ts_shutdown(void) +{ + unsigned int uiKTDIV; + + DPRINTK("ep93xx_ts_shutdown\n"); + + sTouch.state = TS_STATE_STOPPED; + Stop_Timer2(); + + /* + * Disable the scanning engine. + */ + outl( 0, TSSetup ); + outl( 0, TSSetup2 ); + + /* + * Clear the TSEN bit in KTDIV so that we are disabling the clock + * for the touchscreen. + */ + uiKTDIV = inl(SYSCON_KTDIV); + uiKTDIV &= ~SYSCON_KTDIV_TSEN; + SysconSetLocked( SYSCON_KTDIV, uiKTDIV ); + +} /* ep93xx_ts_shutdown */ + +static irqreturn_t ep93xx_timer2_isr(int irq, void *dev_id) +{ + DPRINTK("%d", (int)sTouch.state ); + + switch( sTouch.state ) + { + case TS_STATE_STOPPED: + TS_Hardware_Scan_Mode(); + break; + + // + // Get the Z1 value for pressure measurement and set up + // the switch register for getting the Z2 measurement. + // + case TS_STATE_Z1: + Set_Timer2_uSec( EP93XX_TS_ADC_DELAY_USEC ); + sTouch.uiZ1 = ADCGetData( 2, 200 ); + ep93xx_ts_set_direct( sSwitchSettings.uiSwitchZ2 ); + sTouch.state = TS_STATE_Z2; + break; + + // + // Get the Z2 value for pressure measurement and set up + // the switch register for getting the Y measurement. + // + case TS_STATE_Z2: + sTouch.uiZ2 = ADCGetData( 2, 200 ); + ep93xx_ts_set_direct( sSwitchSettings.uiYSample ); + sTouch.state = TS_STATE_Y; + break; + + // + // Get the Y value and set up the switch register for + // getting the X measurement. + // + case TS_STATE_Y: + sTouch.uiY = ADCGetData( 4, 20 ); + ep93xx_ts_set_direct( sSwitchSettings.uiXSample ); + sTouch.state = TS_STATE_X; + break; + + // + // Read the X value. This is the last of the 4 adc values + // we need so we continue on to process the data. + // + case TS_STATE_X: + Stop_Timer2(); + + sTouch.uiX = ADCGetData( 4, 20 ); + + outl( 0xaa, TSSWLock ); + outl( sSwitchSettings.uiDischarge, TSDirect ); + + sTouch.state = TS_STATE_DONE; + + /* + * Process this set of ADC readings. + */ + ProcessPointData(); + + break; + + + // + // Shouldn't get here. But if we do, we can recover... + // + case TS_STATE_DONE: + TS_Hardware_Scan_Mode(); + break; + } + + // + // Clear the timer2 interrupt. + // + outl( 1, TIMER2CLEAR ); + return(IRQ_HANDLED); +} + +/*--------------------------------------------------------------------- + * ProcessPointData + * + * This routine processes the ADC data into usable point data and then + * puts the driver into hw or sw scanning mode before returning. + * + * We calculate inverse pressure (lower number = more pressure) then + * do a hystheresis with the two pressure values 'light' and 'heavy'. + * + * If we are above the light, we have pen up. + * If we are below the heavy we have pen down. + * As long as the pressure stays below the light, pen stays down. + * When we get above the light again, pen goes back up. + * + */ +static void ProcessPointData(void) +{ + int bValidPoint = 0; + unsigned int uiXDiff, uiYDiff, uiInvPressureDiff; + unsigned int uiInvPressure; + + // + // Calculate the current pressure. + // + uiInvPressure = CalculateInvPressure(); + + DPRINTK(" X=0x%x, Y=0x%x, Z1=0x%x, Z2=0x%x, InvPressure=0x%x", + sTouch.uiX, sTouch.uiY, sTouch.uiZ1, sTouch.uiZ2, uiInvPressure ); + + // + // If pen pressure is so light that it is greater than the 'max' setting + // then we consider this to be a pen up. + // + if( uiInvPressure >= TS_LIGHT_INV_PRESSURE ) + { + DPRINTK(" -- up \n"); + bCurrentPenDown = 0; + ee93xx_ts_evt_add( 0, guiLastX, guiLastY, 0 ); + TS_Hardware_Scan_Mode(); + return; + } + + // + // Hystheresis: + // If the pen pressure is hard enough to be less than the 'min' OR + // the pen is already down and is still less than the 'max'... + // + if( (uiInvPressure < TS_HEAVY_INV_PRESSURE) || + ( bCurrentPenDown && (uiInvPressure < TS_LIGHT_INV_PRESSURE) ) ) + { + if( bCurrentPenDown ) + { + // + // If pen was previously down, check the difference between + // the last sample and this one... if the difference between + // samples is too great, ignore the sample. + // + uiXDiff = abs(guiLastX - sTouch.uiX); + uiYDiff = abs(guiLastY - sTouch.uiY); + uiInvPressureDiff = abs(guiLastInvPressure - uiInvPressure); + + if( (uiXDiff < TS_MAX_VALID_XY_CHANGE) && (uiYDiff < TS_MAX_VALID_XY_CHANGE) && + (uiInvPressureDiff < TS_MAX_VALID_PRESSURE_CHANGE) ) + { + DPRINTK(" -- valid(two) \n"); + bValidPoint = 1; + } + else + { + DPRINTK(" -- INvalid(two) \n"); + } + } + else + { + DPRINTK(" -- valid \n"); + bValidPoint = 1; + } + + /* + * If either the pen was put down or dragged make a note of it. + */ + if( bValidPoint ) + { + guiLastX = sTouch.uiX; + guiLastY = sTouch.uiY; + guiLastInvPressure = uiInvPressure; + bCurrentPenDown = 1; + ee93xx_ts_evt_add( 1, sTouch.uiX, sTouch.uiY, (0x7000000 /uiInvPressure) ); + } + + TS_Soft_Scan_Mode(); + return; + } + + DPRINTK(" -- fallout \n"); + TS_Hardware_Scan_Mode(); +} + +static void ep93xx_ts_set_direct( unsigned int uiADCSwitch ) +{ + unsigned int uiResult; + + // + // Set the switch settings in the direct register. + // + outl( 0xaa, TSSWLock ); + outl( uiADCSwitch, TSDirect ); + + // + // Read and throw away the first sample. + // + do { + uiResult = inl(TSXYResult); + } while( !(uiResult & TSXYRESULT_SDR) ); + +} + +static unsigned int ADCGetData +( + unsigned int uiSamples, + unsigned int uiMaxDiff +) +{ + unsigned int uiResult, uiValue, uiCount, uiLowest, uiHighest, uiSum, uiAve; + + do + { + // + //Initialize our values. + // + uiLowest = 0xfffffff; + uiHighest = 0; + uiSum = 0; + + for( uiCount = 0 ; uiCount < uiSamples ; uiCount++ ) + { + // + // Read the touch screen four more times and average. + // + do { + uiResult = inl(TSXYResult); + } while( !(uiResult & TSXYRESULT_SDR) ); + + uiValue = (uiResult & TSXYRESULT_AD_MASK) >> TSXYRESULT_AD_SHIFT; + uiValue = ((uiValue >> 4) + ((1 + TSXYRESULT_X_MASK)>>1)) & TSXYRESULT_X_MASK; + + // + // Add up the values. + // + uiSum += uiValue; + + // + // Get the lowest and highest values. + // + if( uiValue < uiLowest ) + { + uiLowest = uiValue; + } + if( uiValue > uiHighest ) + { + uiHighest = uiValue; + } + } + + } while( (uiHighest - uiLowest) > uiMaxDiff ); + + // + // Calculate the Average value. + // + uiAve = uiSum / uiSamples; + + return uiAve; +} + +//**************************************************************************** +// CalculateInvPressure +//**************************************************************************** +// Is the Touch Valid. Touch is not valid if the X or Y value is not +// in range and the pressure is not enough. +// +// Touch resistance can be measured by the following formula: +// +// Rx * X * Z2 +// Rtouch = --------- * (-- - 1) +// 4096 Z1 +// +// This is simplified in the ration of Rtouch to Rx. The lower the value, the +// higher the pressure. +// +// Z2 +// InvPressure = X * (-- - 1) +// Z1 +// +static unsigned int CalculateInvPressure(void) +{ + unsigned int uiInvPressure; + + // + // Check to see if the point is valid. + // + if( sTouch.uiZ1 < MIN_Z1_VALUE ) + { + uiInvPressure = 0x10000; + } + + // + // Can omit the pressure calculation if you need to get rid of the division. + // + else + { + uiInvPressure = ((sTouch.uiX * sTouch.uiZ2) / sTouch.uiZ1) - sTouch.uiX; + } + + return uiInvPressure; +} + + + +//**************************************************************************** +// TS_Hardware_Scan_Mode +//**************************************************************************** +// Enables the ep93xx ts scanning engine so that when the pen goes down +// we will get an interrupt. +// +// +static void TS_Hardware_Scan_Mode(void) +{ + unsigned int uiDevCfg; + + DPRINTK("S\n"); + + // + // Disable the soft scanning engine. + // + sTouch.state = TS_STATE_STOPPED; + Stop_Timer2(); + + // + // Clear the TIN (Touchscreen INactive) bit so we can go to + // automatic scanning mode. + // + uiDevCfg = inl( EP93XX_SYSCON_DEVCFG ); + SysconSetLocked( EP93XX_SYSCON_DEVCFG, (uiDevCfg & ~EP93XX_SYSCON_DEVCFG_TIN) ); + + // + // Enable the touch screen scanning state machine by setting + // the ENABLE bit. + // + outl( (TSSETUP_DEFAULT | TSSETUP_ENABLE), TSSetup ); + + // + // Set the flag to show that we are in interrupt mode. + // + gScanningMode = TS_MODE_HARDWARE_SCAN; + + // + // Initialize TSSetup2 register. + // + outl( TSSETUP2_DEFAULT, TSSetup2 ); + +} + +//**************************************************************************** +// TS_Soft_Scan_Mode +//**************************************************************************** +// Sets the touch screen to manual polling mode. +// +// +static void TS_Soft_Scan_Mode(void) +{ + unsigned int uiDevCfg; + + DPRINTK("M\n"); + + if( gScanningMode != TS_MODE_SOFT_SCAN ) + { + // + // Disable the touch screen scanning state machine by clearing + // the ENABLE bit. + // + outl( TSSETUP_DEFAULT, TSSetup ); + + // + // Set the TIN bit so we can do manual touchscreen polling. + // + uiDevCfg = inl(EP93XX_SYSCON_DEVCFG ); + SysconSetLocked( EP93XX_SYSCON_DEVCFG, (uiDevCfg | EP93XX_SYSCON_DEVCFG_TIN) ); + } + + // + // Set the switch register up for the first ADC reading + // + ep93xx_ts_set_direct( sSwitchSettings.uiSwitchZ1 ); + + // + // Initialize our software state machine to know which ADC + // reading to take + // + sTouch.state = TS_STATE_Z1; + + // + // Set the timer so after a mSec or two settling delay it will + // take the first ADC reading. + // + Set_Timer2_uSec( EP93XX_TS_PER_POINT_DELAY_USEC ); + + // + // Note that we are in sw scanning mode not hw scanning mode. + // + gScanningMode = TS_MODE_SOFT_SCAN; + +} + +static void Set_Timer2_uSec( unsigned int uiDelay_uSec ) +{ + unsigned int uiClockTicks; + + /* + * Stop timer 2 + */ + outl( 0, TIMER2CONTROL ); + + uiClockTicks = ((uiDelay_uSec * 508) + 999) / 1000; + outl( uiClockTicks, TIMER2LOAD ); + outl( uiClockTicks, TIMER2VALUE ); + + /* + * Set up Timer 2 for 508 kHz clock and periodic mode. + */ + outl( 0xC8, TIMER2CONTROL ); + +} + +static void Stop_Timer2(void) +{ + outl( 0, TIMER2CONTROL ); +} + +/* + * Initialization and exit routines + */ +int __init ep93xx_ts_init(void) +{ + int retval; + + //printk("ep93xx_ts_init\n"); + + // printk("request Touchscreen interrupt.\n"); + retval = request_irq( IRQ_EP93XX_TOUCH, ep93xx_ts_isr, IRQF_DISABLED, "ep93xx_ts", 0); + if( retval ) + { + printk(KERN_WARNING "ep93xx_ts: failed to get touchscreen IRQ\n"); + return retval; + } + + // printk("Request Timer interrupt.\n"); + retval = request_irq( IRQ_EP93XX_TIMER2, ep93xx_timer2_isr, + IRQF_DISABLED, "ep93xx_timer2", 0); + if( retval ) + { + printk(KERN_WARNING "ep93xx_ts: failed to get timer2 IRQ\n"); + return retval; + } + + // printk("Register Touchscreen Driver\n"); + misc_register(&ep93xx_ts_miscdev); + + sTouch.state = TS_STATE_STOPPED; + gScanningMode = TS_MODE_UN_INITIALIZED; + + printk(KERN_NOTICE "ep93xx touchscreen driver configured for 4-wire operation\n"); + + return 0; +} +void __exit +ep93xx_ts_exit(void) +{ + DPRINTK("ep93xx_ts_exit\n"); + + Stop_Timer2(); + + free_irq(IRQ_EP93XX_TOUCH, 0); + free_irq(IRQ_EP93XX_TIMER2, 0); + + misc_deregister(&ep93xx_ts_miscdev); +} + +module_init(ep93xx_ts_init); +module_exit(ep93xx_ts_exit); + +MODULE_DESCRIPTION("Cirrus EP93xx touchscreen driver"); +MODULE_SUPPORTED_DEVICE("touchscreen/ep93xx"); --- /dev/null +++ linux-2.6.32/drivers/input/touchscreen/ep93xx_ts.h @@ -0,0 +1,53 @@ +/* + * ep93xx_ts.h + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * The initial developer of the original code is David A. Hinds + * . Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use + * your version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#ifndef _LINUX_EP93XX_TS_H +#define _LINUX_EP93XX_TS_H + +/*touchscreen register defines*/ +#define SYSCON_KTDIV EP93XX_SYSCON_KEY_TOUCH_CLOCK_DIV +#define SYSCON_SWLOCK EP93XX_SYSCON_SWLOCK +#define TSSetup EP93XX_TOUCHSCREEN_TSSetup +#define TSXYMaxMin EP93XX_TOUCHSCREEN_TSXYMaxMin +#define TSXYResult EP93XX_TOUCHSCREEN_TSDischarge +#define TSDischarge EP93XX_TOUCHSCREEN_TSDischarge +#define TSXSample EP93XX_TOUCHSCREEN_TSXSample +#define TSYSample EP93XX_TOUCHSCREEN_TSYSample +#define TSDirect EP93XX_TOUCHSCREEN_TSDirect +#define TSDetect EP93XX_TOUCHSCREEN_TSDetect +#define TSSWLock EP93XX_TOUCHSCREEN_TSSWLock +#define TSSetup2 EP93XX_TOUCHSCREEN_TSSetup2 + + +//#define SYSCON_DEVCFG EP93XX_SYSCON_DEVICE_CONFIG +#define TIMER2CONTROL EP93XX_TIMER2_CONTROL +#define TIMER2LOAD EP93XX_TIMER2_LOAD +#define TIMER2VALUE EP93XX_TIMER2_VALUE +#define TIMER2CLEAR EP93XX_TIMER2_CLEAR +#endif