2010-09-20 12:26:43 -04:00
/*
* Texas Instruments TNETV107X Touchscreen Driver
*
* Copyright ( C ) 2010 Texas Instruments
*
* 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 version 2.
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2011-08-21 12:48:04 -07:00
# include <linux/module.h>
2010-09-20 12:26:43 -04:00
# include <linux/kernel.h>
2011-01-17 20:40:55 -08:00
# include <linux/err.h>
2010-09-20 12:26:43 -04:00
# include <linux/errno.h>
# include <linux/input.h>
# include <linux/platform_device.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/ctype.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <mach/tnetv107x.h>
# define TSC_PENUP_POLL (HZ / 5)
# define IDLE_TIMEOUT 100 /* msec */
/*
* The first and last samples of a touch interval are usually garbage and need
* to be filtered out with these devices . The following definitions control
* the number of samples skipped .
*/
# define TSC_HEAD_SKIP 1
# define TSC_TAIL_SKIP 1
# define TSC_SKIP (TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1)
# define TSC_SAMPLES (TSC_SKIP + 1)
/* Register Offsets */
struct tsc_regs {
u32 rev ;
u32 tscm ;
u32 bwcm ;
u32 swc ;
u32 adcchnl ;
u32 adcdata ;
u32 chval [ 4 ] ;
} ;
/* TSC Mode Configuration Register (tscm) bits */
# define WMODE BIT(0)
# define TSKIND BIT(1)
# define ZMEASURE_EN BIT(2)
# define IDLE BIT(3)
# define TSC_EN BIT(4)
# define STOP BIT(5)
# define ONE_SHOT BIT(6)
# define SINGLE BIT(7)
# define AVG BIT(8)
# define AVGNUM(x) (((x) & 0x03) << 9)
# define PVSTC(x) (((x) & 0x07) << 11)
# define PON BIT(14)
# define PONBG BIT(15)
# define AFERST BIT(16)
/* ADC DATA Capture Register bits */
# define DATA_VALID BIT(16)
/* Register Access Macros */
# define tsc_read(ts, reg) __raw_readl(&(ts)->regs->reg)
# define tsc_write(ts, reg, val) __raw_writel(val, &(ts)->regs->reg);
# define tsc_set_bits(ts, reg, val) \
tsc_write ( ts , reg , tsc_read ( ts , reg ) | ( val ) )
# define tsc_clr_bits(ts, reg, val) \
tsc_write ( ts , reg , tsc_read ( ts , reg ) & ~ ( val ) )
struct sample {
int x , y , p ;
} ;
struct tsc_data {
struct input_dev * input_dev ;
struct resource * res ;
struct tsc_regs __iomem * regs ;
struct timer_list timer ;
spinlock_t lock ;
struct clk * clk ;
struct device * dev ;
int sample_count ;
struct sample samples [ TSC_SAMPLES ] ;
int tsc_irq ;
} ;
static int tsc_read_sample ( struct tsc_data * ts , struct sample * sample )
{
int x , y , z1 , z2 , t , p = 0 ;
u32 val ;
val = tsc_read ( ts , chval [ 0 ] ) ;
if ( val & DATA_VALID )
x = val & 0xffff ;
else
return - EINVAL ;
y = tsc_read ( ts , chval [ 1 ] ) & 0xffff ;
z1 = tsc_read ( ts , chval [ 2 ] ) & 0xffff ;
z2 = tsc_read ( ts , chval [ 3 ] ) & 0xffff ;
if ( z1 ) {
t = ( ( 600 * x ) * ( z2 - z1 ) ) ;
p = t / ( u32 ) ( z1 < < 12 ) ;
if ( p < 0 )
p = 0 ;
}
sample - > x = x ;
sample - > y = y ;
sample - > p = p ;
return 0 ;
}
static void tsc_poll ( unsigned long data )
{
struct tsc_data * ts = ( struct tsc_data * ) data ;
unsigned long flags ;
int i , val , x , y , p ;
spin_lock_irqsave ( & ts - > lock , flags ) ;
if ( ts - > sample_count > = TSC_SKIP ) {
input_report_abs ( ts - > input_dev , ABS_PRESSURE , 0 ) ;
input_report_key ( ts - > input_dev , BTN_TOUCH , 0 ) ;
input_sync ( ts - > input_dev ) ;
} else if ( ts - > sample_count > 0 ) {
/*
* A touch event lasted less than our skip count . Salvage and
* report anyway .
*/
for ( i = 0 , val = 0 ; i < ts - > sample_count ; i + + )
val + = ts - > samples [ i ] . x ;
x = val / ts - > sample_count ;
for ( i = 0 , val = 0 ; i < ts - > sample_count ; i + + )
val + = ts - > samples [ i ] . y ;
y = val / ts - > sample_count ;
for ( i = 0 , val = 0 ; i < ts - > sample_count ; i + + )
val + = ts - > samples [ i ] . p ;
p = val / ts - > sample_count ;
input_report_abs ( ts - > input_dev , ABS_X , x ) ;
input_report_abs ( ts - > input_dev , ABS_Y , y ) ;
input_report_abs ( ts - > input_dev , ABS_PRESSURE , p ) ;
input_report_key ( ts - > input_dev , BTN_TOUCH , 1 ) ;
input_sync ( ts - > input_dev ) ;
}
ts - > sample_count = 0 ;
spin_unlock_irqrestore ( & ts - > lock , flags ) ;
}
static irqreturn_t tsc_irq ( int irq , void * dev_id )
{
struct tsc_data * ts = ( struct tsc_data * ) dev_id ;
struct sample * sample ;
int index ;
spin_lock ( & ts - > lock ) ;
index = ts - > sample_count % TSC_SAMPLES ;
sample = & ts - > samples [ index ] ;
if ( tsc_read_sample ( ts , sample ) < 0 )
goto out ;
if ( + + ts - > sample_count > = TSC_SKIP ) {
index = ( ts - > sample_count - TSC_TAIL_SKIP - 1 ) % TSC_SAMPLES ;
sample = & ts - > samples [ index ] ;
input_report_abs ( ts - > input_dev , ABS_X , sample - > x ) ;
input_report_abs ( ts - > input_dev , ABS_Y , sample - > y ) ;
input_report_abs ( ts - > input_dev , ABS_PRESSURE , sample - > p ) ;
if ( ts - > sample_count = = TSC_SKIP )
input_report_key ( ts - > input_dev , BTN_TOUCH , 1 ) ;
input_sync ( ts - > input_dev ) ;
}
mod_timer ( & ts - > timer , jiffies + TSC_PENUP_POLL ) ;
out :
spin_unlock ( & ts - > lock ) ;
return IRQ_HANDLED ;
}
static int tsc_start ( struct input_dev * dev )
{
struct tsc_data * ts = input_get_drvdata ( dev ) ;
unsigned long timeout = jiffies + msecs_to_jiffies ( IDLE_TIMEOUT ) ;
u32 val ;
clk_enable ( ts - > clk ) ;
/* Go to idle mode, before any initialization */
while ( time_after ( timeout , jiffies ) ) {
if ( tsc_read ( ts , tscm ) & IDLE )
break ;
}
if ( time_before ( timeout , jiffies ) ) {
dev_warn ( ts - > dev , " timeout waiting for idle \n " ) ;
clk_disable ( ts - > clk ) ;
return - EIO ;
}
/* Configure TSC Control register*/
val = ( PONBG | PON | PVSTC ( 4 ) | ONE_SHOT | ZMEASURE_EN ) ;
tsc_write ( ts , tscm , val ) ;
/* Bring TSC out of reset: Clear AFE reset bit */
val & = ~ ( AFERST ) ;
tsc_write ( ts , tscm , val ) ;
/* Configure all pins for hardware control*/
tsc_write ( ts , bwcm , 0 ) ;
/* Finally enable the TSC */
tsc_set_bits ( ts , tscm , TSC_EN ) ;
return 0 ;
}
static void tsc_stop ( struct input_dev * dev )
{
struct tsc_data * ts = input_get_drvdata ( dev ) ;
tsc_clr_bits ( ts , tscm , TSC_EN ) ;
synchronize_irq ( ts - > tsc_irq ) ;
del_timer_sync ( & ts - > timer ) ;
clk_disable ( ts - > clk ) ;
}
static int __devinit tsc_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct tsc_data * ts ;
int error = 0 ;
u32 rev = 0 ;
ts = kzalloc ( sizeof ( struct tsc_data ) , GFP_KERNEL ) ;
if ( ! ts ) {
dev_err ( dev , " cannot allocate device info \n " ) ;
return - ENOMEM ;
}
ts - > dev = dev ;
spin_lock_init ( & ts - > lock ) ;
setup_timer ( & ts - > timer , tsc_poll , ( unsigned long ) ts ) ;
platform_set_drvdata ( pdev , ts ) ;
ts - > tsc_irq = platform_get_irq ( pdev , 0 ) ;
if ( ts - > tsc_irq < 0 ) {
dev_err ( dev , " cannot determine device interrupt \n " ) ;
error = - ENODEV ;
goto error_res ;
}
ts - > res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! ts - > res ) {
dev_err ( dev , " cannot determine register area \n " ) ;
error = - ENODEV ;
goto error_res ;
}
if ( ! request_mem_region ( ts - > res - > start , resource_size ( ts - > res ) ,
pdev - > name ) ) {
dev_err ( dev , " cannot claim register memory \n " ) ;
ts - > res = NULL ;
error = - EINVAL ;
goto error_res ;
}
ts - > regs = ioremap ( ts - > res - > start , resource_size ( ts - > res ) ) ;
if ( ! ts - > regs ) {
dev_err ( dev , " cannot map register memory \n " ) ;
error = - ENOMEM ;
goto error_map ;
}
ts - > clk = clk_get ( dev , NULL ) ;
2011-01-17 20:40:55 -08:00
if ( IS_ERR ( ts - > clk ) ) {
2010-09-20 12:26:43 -04:00
dev_err ( dev , " cannot claim device clock \n " ) ;
2011-01-17 20:40:55 -08:00
error = PTR_ERR ( ts - > clk ) ;
2010-09-20 12:26:43 -04:00
goto error_clk ;
}
2012-07-04 13:02:56 -07:00
error = request_threaded_irq ( ts - > tsc_irq , NULL , tsc_irq , IRQF_ONESHOT ,
2010-09-20 12:26:43 -04:00
dev_name ( dev ) , ts ) ;
if ( error < 0 ) {
dev_err ( ts - > dev , " Could not allocate ts irq \n " ) ;
goto error_irq ;
}
ts - > input_dev = input_allocate_device ( ) ;
if ( ! ts - > input_dev ) {
dev_err ( dev , " cannot allocate input device \n " ) ;
error = - ENOMEM ;
goto error_input ;
}
input_set_drvdata ( ts - > input_dev , ts ) ;
ts - > input_dev - > name = pdev - > name ;
ts - > input_dev - > id . bustype = BUS_HOST ;
ts - > input_dev - > dev . parent = & pdev - > dev ;
ts - > input_dev - > open = tsc_start ;
ts - > input_dev - > close = tsc_stop ;
clk_enable ( ts - > clk ) ;
rev = tsc_read ( ts , rev ) ;
ts - > input_dev - > id . product = ( ( rev > > 8 ) & 0x07 ) ;
ts - > input_dev - > id . version = ( ( rev > > 16 ) & 0xfff ) ;
clk_disable ( ts - > clk ) ;
__set_bit ( EV_KEY , ts - > input_dev - > evbit ) ;
__set_bit ( EV_ABS , ts - > input_dev - > evbit ) ;
__set_bit ( BTN_TOUCH , ts - > input_dev - > keybit ) ;
input_set_abs_params ( ts - > input_dev , ABS_X , 0 , 0xffff , 5 , 0 ) ;
input_set_abs_params ( ts - > input_dev , ABS_Y , 0 , 0xffff , 5 , 0 ) ;
input_set_abs_params ( ts - > input_dev , ABS_PRESSURE , 0 , 4095 , 128 , 0 ) ;
error = input_register_device ( ts - > input_dev ) ;
if ( error < 0 ) {
dev_err ( dev , " failed input device registration \n " ) ;
goto error_reg ;
}
return 0 ;
error_reg :
input_free_device ( ts - > input_dev ) ;
error_input :
free_irq ( ts - > tsc_irq , ts ) ;
error_irq :
clk_put ( ts - > clk ) ;
error_clk :
iounmap ( ts - > regs ) ;
error_map :
release_mem_region ( ts - > res - > start , resource_size ( ts - > res ) ) ;
error_res :
platform_set_drvdata ( pdev , NULL ) ;
kfree ( ts ) ;
return error ;
}
static int __devexit tsc_remove ( struct platform_device * pdev )
{
struct tsc_data * ts = platform_get_drvdata ( pdev ) ;
input_unregister_device ( ts - > input_dev ) ;
free_irq ( ts - > tsc_irq , ts ) ;
clk_put ( ts - > clk ) ;
iounmap ( ts - > regs ) ;
release_mem_region ( ts - > res - > start , resource_size ( ts - > res ) ) ;
platform_set_drvdata ( pdev , NULL ) ;
kfree ( ts ) ;
return 0 ;
}
static struct platform_driver tsc_driver = {
. probe = tsc_probe ,
. remove = __devexit_p ( tsc_remove ) ,
. driver . name = " tnetv107x-ts " ,
. driver . owner = THIS_MODULE ,
} ;
2011-11-29 11:14:13 -08:00
module_platform_driver ( tsc_driver ) ;
2010-09-20 12:26:43 -04:00
MODULE_AUTHOR ( " Cyril Chemparathy " ) ;
MODULE_DESCRIPTION ( " TNETV107X Touchscreen Driver " ) ;
2011-06-27 12:42:12 -07:00
MODULE_ALIAS ( " platform:tnetv107x-ts " ) ;
2010-09-20 12:26:43 -04:00
MODULE_LICENSE ( " GPL " ) ;