2005-04-17 02:20:36 +04:00
/*
2005-09-13 12:25:33 +04:00
* Touchscreen driver for Sharp SL - C7xx and SL - Cxx00 models
2005-04-17 02:20:36 +04:00
*
* Copyright ( c ) 2004 - 2005 Richard Purdie
*
* 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 <linux/delay.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/slab.h>
2006-04-30 18:34:29 +04:00
//#include <asm/irq.h>
2005-04-17 02:20:36 +04:00
2005-09-13 12:25:33 +04:00
# include <asm/arch/sharpsl.h>
2005-04-17 02:20:36 +04:00
# include <asm/arch/hardware.h>
# include <asm/arch/pxa-regs.h>
# define PWR_MODE_ACTIVE 0
# define PWR_MODE_SUSPEND 1
# define X_AXIS_MAX 3830
# define X_AXIS_MIN 150
# define Y_AXIS_MAX 3830
# define Y_AXIS_MIN 190
# define PRESSURE_MIN 0
# define PRESSURE_MAX 15000
struct ts_event {
short pressure ;
short x ;
short y ;
} ;
struct corgi_ts {
2005-09-15 11:01:46 +04:00
struct input_dev * input ;
2005-04-17 02:20:36 +04:00
struct timer_list timer ;
struct ts_event tc ;
int pendown ;
int power_mode ;
2005-09-13 12:25:33 +04:00
int irq_gpio ;
struct corgits_machinfo * machinfo ;
2005-04-17 02:20:36 +04:00
} ;
2005-09-13 12:25:33 +04:00
# ifdef CONFIG_PXA25x
2005-04-17 02:20:36 +04:00
# define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C0, 0" : "=r"(a))
2005-09-07 02:19:01 +04:00
# define PMNC_GET(x) asm volatile ("mrc p14, 0, %0, C0, C0, 0" : "=r"(x))
# define PMNC_SET(x) asm volatile ("mcr p14, 0, %0, C0, C0, 0" : : "r"(x))
2005-09-13 12:25:33 +04:00
# endif
# ifdef CONFIG_PXA27x
# define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a))
# define PMNC_GET(x) asm volatile ("mrc p14, 0, %0, C0, C1, 0" : "=r"(x))
# define PMNC_SET(x) asm volatile ("mcr p14, 0, %0, C0, C1, 0" : : "r"(x))
# endif
2005-04-17 02:20:36 +04:00
/* ADS7846 Touch Screen Controller bit definitions */
# define ADSCTRL_PD0 (1u << 0) /* PD0 */
# define ADSCTRL_PD1 (1u << 1) /* PD1 */
# define ADSCTRL_DFR (1u << 2) /* SER/DFR */
# define ADSCTRL_MOD (1u << 3) /* Mode */
# define ADSCTRL_ADR_SH 4 /* Address setting */
# define ADSCTRL_STS (1u << 7) /* Start Bit */
/* External Functions */
extern unsigned int get_clk_frequency_khz ( int info ) ;
2005-09-13 12:25:33 +04:00
static unsigned long calc_waittime ( struct corgi_ts * corgi_ts )
2005-04-17 02:20:36 +04:00
{
2005-09-13 12:25:33 +04:00
unsigned long hsync_len = corgi_ts - > machinfo - > get_hsync_len ( ) ;
2005-04-17 02:20:36 +04:00
2005-09-07 02:19:05 +04:00
if ( hsync_len )
return get_clk_frequency_khz ( 0 ) * 1000 / hsync_len ;
else
2005-09-07 02:19:02 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-09-13 12:25:33 +04:00
static int sync_receive_data_send_cmd ( struct corgi_ts * corgi_ts , int doRecive , int doSend ,
unsigned int address , unsigned long wait_time )
2005-04-17 02:20:36 +04:00
{
2005-09-07 02:19:01 +04:00
unsigned long timer1 = 0 , timer2 , pmnc = 0 ;
2005-04-17 02:20:36 +04:00
int pos = 0 ;
2005-09-07 02:19:02 +04:00
if ( wait_time & & doSend ) {
2005-09-07 02:19:01 +04:00
PMNC_GET ( pmnc ) ;
if ( ! ( pmnc & 0x01 ) )
2005-09-07 02:19:02 +04:00
PMNC_SET ( 0x01 ) ;
2005-09-07 02:19:01 +04:00
2005-04-17 02:20:36 +04:00
/* polling HSync */
2005-09-13 12:25:33 +04:00
corgi_ts - > machinfo - > wait_hsync ( ) ;
2005-04-17 02:20:36 +04:00
/* get CCNT */
CCNT ( timer1 ) ;
}
if ( doRecive )
pos = corgi_ssp_ads7846_get ( ) ;
if ( doSend ) {
int cmd = ADSCTRL_PD0 | ADSCTRL_PD1 | ( address < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ;
/* dummy command */
corgi_ssp_ads7846_put ( cmd ) ;
corgi_ssp_ads7846_get ( ) ;
2005-09-07 02:19:02 +04:00
if ( wait_time ) {
2005-04-17 02:20:36 +04:00
/* Wait after HSync */
CCNT ( timer2 ) ;
if ( timer2 - timer1 > wait_time ) {
2005-09-07 02:19:02 +04:00
/* too slow - timeout, try again */
2005-09-13 12:25:33 +04:00
corgi_ts - > machinfo - > wait_hsync ( ) ;
2005-04-17 02:20:36 +04:00
/* get OSCR */
CCNT ( timer1 ) ;
/* Wait after HSync */
CCNT ( timer2 ) ;
}
while ( timer2 - timer1 < wait_time )
CCNT ( timer2 ) ;
}
corgi_ssp_ads7846_put ( cmd ) ;
2005-09-07 02:19:02 +04:00
if ( wait_time & & ! ( pmnc & 0x01 ) )
2005-09-07 02:19:01 +04:00
PMNC_SET ( pmnc ) ;
2005-04-17 02:20:36 +04:00
}
return pos ;
}
static int read_xydata ( struct corgi_ts * corgi_ts )
{
unsigned int x , y , z1 , z2 ;
unsigned long flags , wait_time ;
/* critical section */
local_irq_save ( flags ) ;
corgi_ssp_ads7846_lock ( ) ;
2005-09-13 12:25:33 +04:00
wait_time = calc_waittime ( corgi_ts ) ;
2005-04-17 02:20:36 +04:00
/* Y-axis */
2005-09-13 12:25:33 +04:00
sync_receive_data_send_cmd ( corgi_ts , 0 , 1 , 1u , wait_time ) ;
2005-04-17 02:20:36 +04:00
/* Y-axis */
2005-09-13 12:25:33 +04:00
sync_receive_data_send_cmd ( corgi_ts , 1 , 1 , 1u , wait_time ) ;
2005-04-17 02:20:36 +04:00
/* X-axis */
2005-09-13 12:25:33 +04:00
y = sync_receive_data_send_cmd ( corgi_ts , 1 , 1 , 5u , wait_time ) ;
2005-04-17 02:20:36 +04:00
/* Z1 */
2005-09-13 12:25:33 +04:00
x = sync_receive_data_send_cmd ( corgi_ts , 1 , 1 , 3u , wait_time ) ;
2005-04-17 02:20:36 +04:00
/* Z2 */
2005-09-13 12:25:33 +04:00
z1 = sync_receive_data_send_cmd ( corgi_ts , 1 , 1 , 4u , wait_time ) ;
z2 = sync_receive_data_send_cmd ( corgi_ts , 1 , 0 , 4u , wait_time ) ;
2005-04-17 02:20:36 +04:00
/* Power-Down Enable */
corgi_ssp_ads7846_put ( ( 1u < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ) ;
corgi_ssp_ads7846_get ( ) ;
corgi_ssp_ads7846_unlock ( ) ;
local_irq_restore ( flags ) ;
if ( x = = 0 | | y = = 0 | | z1 = = 0 | | ( x * ( z2 - z1 ) / z1 ) > = 15000 ) {
corgi_ts - > tc . pressure = 0 ;
return 0 ;
}
corgi_ts - > tc . x = x ;
corgi_ts - > tc . y = y ;
corgi_ts - > tc . pressure = ( x * ( z2 - z1 ) ) / z1 ;
return 1 ;
}
static void new_data ( struct corgi_ts * corgi_ts , struct pt_regs * regs )
{
if ( corgi_ts - > power_mode ! = PWR_MODE_ACTIVE )
return ;
if ( ! corgi_ts - > tc . pressure & & corgi_ts - > pendown = = 0 )
return ;
2005-09-15 11:01:46 +04:00
input_regs ( corgi_ts - > input , regs ) ;
input_report_abs ( corgi_ts - > input , ABS_X , corgi_ts - > tc . x ) ;
input_report_abs ( corgi_ts - > input , ABS_Y , corgi_ts - > tc . y ) ;
input_report_abs ( corgi_ts - > input , ABS_PRESSURE , corgi_ts - > tc . pressure ) ;
input_report_key ( corgi_ts - > input , BTN_TOUCH , ( corgi_ts - > pendown ! = 0 ) ) ;
input_sync ( corgi_ts - > input ) ;
2005-04-17 02:20:36 +04:00
}
static void ts_interrupt_main ( struct corgi_ts * corgi_ts , int isTimer , struct pt_regs * regs )
{
2005-09-13 12:25:33 +04:00
if ( ( GPLR ( IRQ_TO_GPIO ( corgi_ts - > irq_gpio ) ) & GPIO_bit ( IRQ_TO_GPIO ( corgi_ts - > irq_gpio ) ) ) = = 0 ) {
2005-04-17 02:20:36 +04:00
/* Disable Interrupt */
2005-09-13 12:25:33 +04:00
set_irq_type ( corgi_ts - > irq_gpio , IRQT_NOEDGE ) ;
2005-04-17 02:20:36 +04:00
if ( read_xydata ( corgi_ts ) ) {
corgi_ts - > pendown = 1 ;
new_data ( corgi_ts , regs ) ;
}
mod_timer ( & corgi_ts - > timer , jiffies + HZ / 100 ) ;
} else {
if ( corgi_ts - > pendown = = 1 | | corgi_ts - > pendown = = 2 ) {
mod_timer ( & corgi_ts - > timer , jiffies + HZ / 100 ) ;
corgi_ts - > pendown + + ;
return ;
}
if ( corgi_ts - > pendown ) {
corgi_ts - > tc . pressure = 0 ;
new_data ( corgi_ts , regs ) ;
}
/* Enable Falling Edge */
2005-09-13 12:25:33 +04:00
set_irq_type ( corgi_ts - > irq_gpio , IRQT_FALLING ) ;
2005-04-17 02:20:36 +04:00
corgi_ts - > pendown = 0 ;
}
}
static void corgi_ts_timer ( unsigned long data )
{
struct corgi_ts * corgits_data = ( struct corgi_ts * ) data ;
ts_interrupt_main ( corgits_data , 1 , NULL ) ;
}
static irqreturn_t ts_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct corgi_ts * corgits_data = dev_id ;
ts_interrupt_main ( corgits_data , 0 , regs ) ;
return IRQ_HANDLED ;
}
# ifdef CONFIG_PM
2005-11-10 01:32:44 +03:00
static int corgits_suspend ( struct platform_device * dev , pm_message_t state )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct corgi_ts * corgi_ts = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-10-28 20:52:56 +04:00
if ( corgi_ts - > pendown ) {
del_timer_sync ( & corgi_ts - > timer ) ;
corgi_ts - > tc . pressure = 0 ;
new_data ( corgi_ts , NULL ) ;
corgi_ts - > pendown = 0 ;
2005-04-17 02:20:36 +04:00
}
2005-10-28 20:52:56 +04:00
corgi_ts - > power_mode = PWR_MODE_SUSPEND ;
corgi_ssp_ads7846_putget ( ( 1u < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int corgits_resume ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct corgi_ts * corgi_ts = platform_get_drvdata ( dev ) ;
2005-10-28 20:52:56 +04:00
corgi_ssp_ads7846_putget ( ( 4u < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ) ;
/* Enable Falling Edge */
set_irq_type ( corgi_ts - > irq_gpio , IRQT_FALLING ) ;
corgi_ts - > power_mode = PWR_MODE_ACTIVE ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
# else
# define corgits_suspend NULL
# define corgits_resume NULL
# endif
2005-11-10 01:32:44 +03:00
static int __init corgits_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
struct corgi_ts * corgi_ts ;
2005-09-15 11:01:46 +04:00
struct input_dev * input_dev ;
int err = - ENOMEM ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:46 +04:00
corgi_ts = kzalloc ( sizeof ( struct corgi_ts ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! corgi_ts | | ! input_dev )
goto fail ;
2005-04-17 02:20:36 +04:00
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , corgi_ts ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 01:32:44 +03:00
corgi_ts - > machinfo = pdev - > dev . platform_data ;
2005-09-13 12:25:33 +04:00
corgi_ts - > irq_gpio = platform_get_irq ( pdev , 0 ) ;
if ( corgi_ts - > irq_gpio < 0 ) {
2005-09-15 11:01:46 +04:00
err = - ENODEV ;
goto fail ;
2005-09-13 12:25:33 +04:00
}
2005-09-15 11:01:46 +04:00
corgi_ts - > input = input_dev ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:46 +04:00
init_timer ( & corgi_ts - > timer ) ;
corgi_ts - > timer . data = ( unsigned long ) corgi_ts ;
corgi_ts - > timer . function = corgi_ts_timer ;
2005-04-17 02:20:36 +04:00
2005-09-15 11:01:46 +04:00
input_dev - > name = " Corgi Touchscreen " ;
input_dev - > phys = " corgits/input0 " ;
input_dev - > id . bustype = BUS_HOST ;
input_dev - > id . vendor = 0x0001 ;
input_dev - > id . product = 0x0002 ;
input_dev - > id . version = 0x0100 ;
2005-11-10 01:32:44 +03:00
input_dev - > cdev . dev = & pdev - > dev ;
2005-09-15 11:01:46 +04:00
input_dev - > private = corgi_ts ;
input_dev - > evbit [ 0 ] = BIT ( EV_KEY ) | BIT ( EV_ABS ) ;
input_dev - > keybit [ LONG ( BTN_TOUCH ) ] = BIT ( BTN_TOUCH ) ;
input_set_abs_params ( input_dev , ABS_X , X_AXIS_MIN , X_AXIS_MAX , 0 , 0 ) ;
input_set_abs_params ( input_dev , ABS_Y , Y_AXIS_MIN , Y_AXIS_MAX , 0 , 0 ) ;
input_set_abs_params ( input_dev , ABS_PRESSURE , PRESSURE_MIN , PRESSURE_MAX , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
2005-09-13 12:25:33 +04:00
pxa_gpio_mode ( IRQ_TO_GPIO ( corgi_ts - > irq_gpio ) | GPIO_IN ) ;
2005-04-17 02:20:36 +04:00
/* Initiaize ADS7846 Difference Reference mode */
corgi_ssp_ads7846_putget ( ( 1u < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ) ;
mdelay ( 5 ) ;
corgi_ssp_ads7846_putget ( ( 3u < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ) ;
mdelay ( 5 ) ;
corgi_ssp_ads7846_putget ( ( 4u < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ) ;
mdelay ( 5 ) ;
corgi_ssp_ads7846_putget ( ( 5u < < ADSCTRL_ADR_SH ) | ADSCTRL_STS ) ;
mdelay ( 5 ) ;
2005-09-13 12:25:33 +04:00
if ( request_irq ( corgi_ts - > irq_gpio , ts_interrupt , SA_INTERRUPT , " ts " , corgi_ts ) ) {
2005-09-15 11:01:46 +04:00
err = - EBUSY ;
goto fail ;
2005-04-17 02:20:36 +04:00
}
2005-09-15 11:01:46 +04:00
input_register_device ( corgi_ts - > input ) ;
corgi_ts - > power_mode = PWR_MODE_ACTIVE ;
2005-04-17 02:20:36 +04:00
/* Enable Falling Edge */
2005-09-13 12:25:33 +04:00
set_irq_type ( corgi_ts - > irq_gpio , IRQT_FALLING ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-09-15 11:01:46 +04:00
fail : input_free_device ( input_dev ) ;
kfree ( corgi_ts ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 01:32:44 +03:00
static int corgits_remove ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct corgi_ts * corgi_ts = platform_get_drvdata ( pdev ) ;
2005-04-17 02:20:36 +04:00
2005-09-13 12:25:33 +04:00
free_irq ( corgi_ts - > irq_gpio , NULL ) ;
2005-04-17 02:20:36 +04:00
del_timer_sync ( & corgi_ts - > timer ) ;
2005-09-13 12:25:33 +04:00
corgi_ts - > machinfo - > put_hsync ( ) ;
2005-09-15 11:01:46 +04:00
input_unregister_device ( corgi_ts - > input ) ;
2005-04-17 02:20:36 +04:00
kfree ( corgi_ts ) ;
return 0 ;
}
2005-11-10 01:32:44 +03:00
static struct platform_driver corgits_driver = {
2005-04-17 02:20:36 +04:00
. probe = corgits_probe ,
. remove = corgits_remove ,
. suspend = corgits_suspend ,
. resume = corgits_resume ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " corgi-ts " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
static int __devinit corgits_init ( void )
{
2005-11-10 01:32:44 +03:00
return platform_driver_register ( & corgits_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit corgits_exit ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & corgits_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( corgits_init ) ;
module_exit ( corgits_exit ) ;
MODULE_AUTHOR ( " Richard Purdie <rpurdie@rpsys.net> " ) ;
MODULE_DESCRIPTION ( " Corgi TouchScreen Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;