2006-01-09 00:34:21 +03:00
/*
* ADS7846 based touchscreen and sensor driver
*
* Copyright ( c ) 2005 David Brownell
2006-04-12 07:43:55 +04:00
* Copyright ( c ) 2006 Nokia Corporation
* Various changes : Imre Deak < imre . deak @ nokia . com >
2006-01-09 00:34:21 +03:00
*
* Using code from :
* - corgi_ts . c
* Copyright ( C ) 2004 - 2005 Richard Purdie
* - omap_ts . [ hc ] , ads7846 . h , ts_osk . c
* Copyright ( C ) 2002 MontaVista Software
* Copyright ( C ) 2004 Texas Instruments
* Copyright ( C ) 2005 Dirk Behme
*
* 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/device.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/spi/spi.h>
# include <linux/spi/ads7846.h>
2006-03-26 13:37:33 +04:00
# include <asm/irq.h>
2006-01-09 00:34:21 +03:00
# ifdef CONFIG_ARM
# include <asm/mach-types.h>
# ifdef CONFIG_ARCH_OMAP
# include <asm/arch/gpio.h>
# endif
# endif
/*
2006-04-12 07:43:55 +04:00
* This code has been tested on an ads7846 / N770 device .
2006-01-09 00:34:21 +03:00
* Support for ads7843 and ads7845 has only been stubbed in .
*
2006-04-12 07:43:55 +04:00
* Not yet done : How accurate are the temperature and voltage
* readings ? ( System - specific calibration should support
2006-01-09 00:34:21 +03:00
* accuracy of 0.3 degrees C ; otherwise it ' s 2.0 degrees . )
*
2006-04-12 07:43:55 +04:00
* IRQ handling needs a workaround because of a shortcoming in handling
* edge triggered IRQs on some platforms like the OMAP1 / 2. These
* platforms don ' t handle the ARM lazy IRQ disabling properly , thus we
* have to maintain our own SW IRQ disabled status . This should be
* removed as soon as the affected platform ' s IRQ handling is fixed .
*
2006-01-09 00:34:21 +03:00
* app note sbaa036 talks in more detail about accurate sampling . . .
* that ought to help in situations like LCDs inducing noise ( which
* can also be helped by using synch signals ) and more generally .
2006-04-12 07:43:55 +04:00
* This driver tries to utilize the measures described in the app
* note . The strength of filtering can be set in the board - * specific
* files .
2006-01-09 00:34:21 +03:00
*/
# define TS_POLL_PERIOD msecs_to_jiffies(10)
2006-02-15 08:49:35 +03:00
/* this driver doesn't aim at the peak continuous sample rate */
# define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */ )
2006-01-09 00:34:21 +03:00
struct ts_event {
/* For portability, we can't read 12 bit values using SPI (which
* would make the controller deliver them as native byteorder u16
2006-02-15 08:49:35 +03:00
* with msbs zeroed ) . Instead , we read them as two 8 - bit values ,
2006-01-09 00:34:21 +03:00
* which need byteswapping then range adjustment .
*/
__be16 x ;
__be16 y ;
__be16 z1 , z2 ;
} ;
struct ads7846 {
2006-02-15 08:49:22 +03:00
struct input_dev * input ;
2006-01-09 00:34:21 +03:00
char phys [ 32 ] ;
struct spi_device * spi ;
u16 model ;
u16 vref_delay_usecs ;
u16 x_plate_ohms ;
2006-04-12 07:41:49 +04:00
u8 read_x , read_y , read_z1 , read_z2 , pwrdown ;
u16 dummy ; /* for the pwrdown read */
2006-01-09 00:34:21 +03:00
struct ts_event tc ;
2006-04-12 07:41:49 +04:00
struct spi_transfer xfer [ 10 ] ;
2006-04-12 07:42:03 +04:00
struct spi_message msg [ 5 ] ;
int msg_idx ;
int read_cnt ;
int last_read ;
u16 debounce_max ;
u16 debounce_tol ;
2006-01-09 00:34:21 +03:00
spinlock_t lock ;
struct timer_list timer ; /* P: lock */
unsigned pendown : 1 ; /* P: lock */
unsigned pending : 1 ; /* P: lock */
// FIXME remove "irq_disabled"
unsigned irq_disabled : 1 ; /* P: lock */
2006-04-12 07:43:55 +04:00
unsigned disabled : 1 ;
2006-04-12 07:44:05 +04:00
int ( * get_pendown_state ) ( void ) ;
2006-01-09 00:34:21 +03:00
} ;
/* leave chip selected when we're done, for quicker re-select? */
#if 0
# define CS_CHANGE(xfer) ((xfer).cs_change = 1)
# else
# define CS_CHANGE(xfer) ((xfer).cs_change = 0)
# endif
/*--------------------------------------------------------------------------*/
/* The ADS7846 has touchscreen and other sensors.
* Earlier ads784x chips are somewhat compatible .
*/
# define ADS_START (1 << 7)
# define ADS_A2A1A0_d_y (1 << 4) /* differential */
# define ADS_A2A1A0_d_z1 (3 << 4) /* differential */
# define ADS_A2A1A0_d_z2 (4 << 4) /* differential */
# define ADS_A2A1A0_d_x (5 << 4) /* differential */
# define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */
# define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */
# define ADS_A2A1A0_vaux (6 << 4) /* non-differential */
# define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */
# define ADS_8_BIT (1 << 3)
# define ADS_12_BIT (0 << 3)
# define ADS_SER (1 << 2) /* non-differential */
# define ADS_DFR (0 << 2) /* differential */
# define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */
# define ADS_PD10_ADC_ON (1 << 0) /* ADC on */
# define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */
# define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */
# define MAX_12BIT ((1<<12)-1)
/* leave ADC powered up (disables penirq) between differential samples */
# define READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \
| ADS_12_BIT | ADS_DFR )
2006-02-15 08:49:35 +03:00
# define READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ADC_ON)
# define READ_Z1 (READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON)
# define READ_Z2 (READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON)
2006-04-12 07:41:49 +04:00
# define READ_X (READ_12BIT_DFR(x) | ADS_PD10_ADC_ON)
# define PWRDOWN (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) /* LAST */
2006-01-09 00:34:21 +03:00
/* single-ended samples need to first power up reference voltage;
* we leave both ADC and VREF powered
*/
# define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \
| ADS_12_BIT | ADS_SER )
2006-02-15 08:49:35 +03:00
# define REF_ON (READ_12BIT_DFR(x) | ADS_PD10_ALL_ON)
# define REF_OFF (READ_12BIT_DFR(y) | ADS_PD10_PDOWN)
2006-01-09 00:34:21 +03:00
/*--------------------------------------------------------------------------*/
/*
* Non - touchscreen sensors only use single - ended conversions .
*/
struct ser_req {
2006-02-15 08:49:35 +03:00
u8 ref_on ;
2006-01-09 00:34:21 +03:00
u8 command ;
2006-02-15 08:49:35 +03:00
u8 ref_off ;
2006-01-09 00:34:21 +03:00
u16 scratch ;
__be16 sample ;
struct spi_message msg ;
struct spi_transfer xfer [ 6 ] ;
} ;
2006-04-12 07:43:55 +04:00
static void ads7846_enable ( struct ads7846 * ts ) ;
static void ads7846_disable ( struct ads7846 * ts ) ;
2006-04-12 07:44:05 +04:00
static int device_suspended ( struct device * dev )
{
struct ads7846 * ts = dev_get_drvdata ( dev ) ;
return dev - > power . power_state . event ! = PM_EVENT_ON | | ts - > disabled ;
}
2006-01-09 00:34:21 +03:00
static int ads7846_read12_ser ( struct device * dev , unsigned command )
{
struct spi_device * spi = to_spi_device ( dev ) ;
struct ads7846 * ts = dev_get_drvdata ( dev ) ;
struct ser_req * req = kzalloc ( sizeof * req , SLAB_KERNEL ) ;
int status ;
int sample ;
2006-02-15 08:49:22 +03:00
int i ;
2006-01-09 00:34:21 +03:00
if ( ! req )
return - ENOMEM ;
2006-04-12 07:42:03 +04:00
spi_message_init ( & req - > msg ) ;
2006-01-09 00:34:28 +03:00
2006-01-09 00:34:21 +03:00
/* activate reference, so it has time to settle; */
2006-02-15 08:49:35 +03:00
req - > ref_on = REF_ON ;
req - > xfer [ 0 ] . tx_buf = & req - > ref_on ;
2006-01-09 00:34:21 +03:00
req - > xfer [ 0 ] . len = 1 ;
req - > xfer [ 1 ] . rx_buf = & req - > scratch ;
req - > xfer [ 1 ] . len = 2 ;
/*
* for external VREF , 0 usec ( and assume it ' s always on ) ;
* for 1u F , use 800 usec ;
* no cap , 100 usec .
*/
req - > xfer [ 1 ] . delay_usecs = ts - > vref_delay_usecs ;
/* take sample */
req - > command = ( u8 ) command ;
req - > xfer [ 2 ] . tx_buf = & req - > command ;
req - > xfer [ 2 ] . len = 1 ;
req - > xfer [ 3 ] . rx_buf = & req - > sample ;
req - > xfer [ 3 ] . len = 2 ;
/* REVISIT: take a few more samples, and compare ... */
/* turn off reference */
2006-02-15 08:49:35 +03:00
req - > ref_off = REF_OFF ;
req - > xfer [ 4 ] . tx_buf = & req - > ref_off ;
2006-01-09 00:34:21 +03:00
req - > xfer [ 4 ] . len = 1 ;
req - > xfer [ 5 ] . rx_buf = & req - > scratch ;
req - > xfer [ 5 ] . len = 2 ;
CS_CHANGE ( req - > xfer [ 5 ] ) ;
/* group all the transfers together, so we can't interfere with
* reading touchscreen state ; disable penirq while sampling
*/
2006-01-09 00:34:28 +03:00
for ( i = 0 ; i < 6 ; i + + )
spi_message_add_tail ( & req - > xfer [ i ] , & req - > msg ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:44:05 +04:00
ts - > irq_disabled = 1 ;
2006-01-09 00:34:21 +03:00
disable_irq ( spi - > irq ) ;
status = spi_sync ( spi , & req - > msg ) ;
2006-04-12 07:44:05 +04:00
ts - > irq_disabled = 0 ;
2006-01-09 00:34:21 +03:00
enable_irq ( spi - > irq ) ;
if ( req - > msg . status )
status = req - > msg . status ;
sample = be16_to_cpu ( req - > sample ) ;
sample = sample > > 4 ;
kfree ( req ) ;
return status ? status : sample ;
}
# define SHOW(name) static ssize_t \
name # # _show ( struct device * dev , struct device_attribute * attr , char * buf ) \
{ \
ssize_t v = ads7846_read12_ser ( dev , \
READ_12BIT_SER ( name ) | ADS_PD10_ALL_ON ) ; \
if ( v < 0 ) \
return v ; \
return sprintf ( buf , " %u \n " , ( unsigned ) v ) ; \
} \
static DEVICE_ATTR ( name , S_IRUGO , name # # _show , NULL ) ;
SHOW ( temp0 )
SHOW ( temp1 )
SHOW ( vaux )
SHOW ( vbatt )
2006-04-12 07:41:32 +04:00
static int is_pen_down ( struct device * dev )
{
struct ads7846 * ts = dev_get_drvdata ( dev ) ;
return ts - > pendown ;
}
static ssize_t ads7846_pen_down_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " %u \n " , is_pen_down ( dev ) ) ;
}
static DEVICE_ATTR ( pen_down , S_IRUGO , ads7846_pen_down_show , NULL ) ;
2006-04-12 07:43:55 +04:00
static ssize_t ads7846_disable_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct ads7846 * ts = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %u \n " , ts - > disabled ) ;
}
static ssize_t ads7846_disable_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct ads7846 * ts = dev_get_drvdata ( dev ) ;
char * endp ;
int i ;
i = simple_strtoul ( buf , & endp , 10 ) ;
spin_lock_irq ( & ts - > lock ) ;
if ( i )
ads7846_disable ( ts ) ;
else
ads7846_enable ( ts ) ;
spin_unlock_irq ( & ts - > lock ) ;
return count ;
}
static DEVICE_ATTR ( disable , 0664 , ads7846_disable_show , ads7846_disable_store ) ;
2006-01-09 00:34:21 +03:00
/*--------------------------------------------------------------------------*/
/*
* PENIRQ only kicks the timer . The timer only reissues the SPI transfer ,
* to retrieve touchscreen status .
*
* The SPI transfer completion callback does the real work . It reports
* touchscreen events and reactivates the timer ( or IRQ ) as appropriate .
*/
static void ads7846_rx ( void * ads )
{
2006-02-15 08:49:22 +03:00
struct ads7846 * ts = ads ;
struct input_dev * input_dev = ts - > input ;
unsigned Rt ;
unsigned sync = 0 ;
u16 x , y , z1 , z2 ;
unsigned long flags ;
2006-01-09 00:34:21 +03:00
/* adjust: 12 bit samples (left aligned), built from
* two 8 bit values writen msb - first .
*/
x = be16_to_cpu ( ts - > tc . x ) > > 4 ;
y = be16_to_cpu ( ts - > tc . y ) > > 4 ;
z1 = be16_to_cpu ( ts - > tc . z1 ) > > 4 ;
z2 = be16_to_cpu ( ts - > tc . z2 ) > > 4 ;
/* range filtering */
if ( x = = MAX_12BIT )
x = 0 ;
2006-04-12 07:44:05 +04:00
if ( likely ( x & & z1 & & ! device_suspended ( & ts - > spi - > dev ) ) ) {
2006-01-09 00:34:21 +03:00
/* compute touch pressure resistance using equation #2 */
Rt = z2 ;
Rt - = z1 ;
Rt * = x ;
Rt * = ts - > x_plate_ohms ;
Rt / = z1 ;
Rt = ( Rt + 2047 ) > > 12 ;
} else
Rt = 0 ;
/* NOTE: "pendown" is inferred from pressure; we don't rely on
* being able to check nPENIRQ status , or " friendly " trigger modes
* ( both - edges is much better than just - falling or low - level ) .
*
* REVISIT : some boards may require reading nPENIRQ ; it ' s
* needed on 7843. and 7845 reads pressure differently . . .
*
* REVISIT : the touchscreen might not be connected ; this code
* won ' t notice that , even if nPENIRQ never fires . . .
*/
if ( ! ts - > pendown & & Rt ! = 0 ) {
2006-02-15 08:49:22 +03:00
input_report_key ( input_dev , BTN_TOUCH , 1 ) ;
2006-01-09 00:34:21 +03:00
sync = 1 ;
} else if ( ts - > pendown & & Rt = = 0 ) {
2006-02-15 08:49:22 +03:00
input_report_key ( input_dev , BTN_TOUCH , 0 ) ;
2006-01-09 00:34:21 +03:00
sync = 1 ;
}
if ( Rt ) {
2006-02-15 08:49:22 +03:00
input_report_abs ( input_dev , ABS_X , x ) ;
input_report_abs ( input_dev , ABS_Y , y ) ;
input_report_abs ( input_dev , ABS_PRESSURE , Rt ) ;
2006-01-09 00:34:21 +03:00
sync = 1 ;
}
if ( sync )
2006-02-15 08:49:22 +03:00
input_sync ( input_dev ) ;
2006-01-09 00:34:21 +03:00
# ifdef VERBOSE
if ( Rt | | ts - > pendown )
pr_debug ( " %s: %d/%d/%d%s \n " , ts - > spi - > dev . bus_id ,
x , y , Rt , Rt ? " " : " UP " ) ;
# endif
spin_lock_irqsave ( & ts - > lock , flags ) ;
ts - > pendown = ( Rt ! = 0 ) ;
2006-04-12 07:44:05 +04:00
mod_timer ( & ts - > timer , jiffies + TS_POLL_PERIOD ) ;
2006-01-09 00:34:21 +03:00
spin_unlock_irqrestore ( & ts - > lock , flags ) ;
}
2006-04-12 07:42:03 +04:00
static void ads7846_debounce ( void * ads )
{
struct ads7846 * ts = ads ;
struct spi_message * m ;
struct spi_transfer * t ;
u16 val ;
int status ;
m = & ts - > msg [ ts - > msg_idx ] ;
t = list_entry ( m - > transfers . prev , struct spi_transfer , transfer_list ) ;
val = ( * ( u16 * ) t - > rx_buf ) > > 3 ;
if ( ! ts - > read_cnt | | ( abs ( ts - > last_read - val ) > ts - > debounce_tol
& & ts - > read_cnt < ts - > debounce_max ) ) {
/* Repeat it, if this was the first read or the read wasn't
* consistent enough
*/
ts - > read_cnt + + ;
ts - > last_read = val ;
} else {
/* Go for the next read */
ts - > msg_idx + + ;
ts - > read_cnt = 0 ;
m + + ;
}
status = spi_async ( ts - > spi , m ) ;
if ( status )
dev_err ( & ts - > spi - > dev , " spi_async --> %d \n " ,
status ) ;
}
2006-01-09 00:34:21 +03:00
static void ads7846_timer ( unsigned long handle )
{
struct ads7846 * ts = ( void * ) handle ;
int status = 0 ;
2006-04-12 07:42:03 +04:00
2006-04-12 07:44:05 +04:00
spin_lock_irq ( & ts - > lock ) ;
if ( unlikely ( ts - > msg_idx & & ! ts - > pendown ) ) {
/* measurment cycle ended */
if ( ! device_suspended ( & ts - > spi - > dev ) ) {
ts - > irq_disabled = 0 ;
enable_irq ( ts - > spi - > irq ) ;
}
ts - > pending = 0 ;
ts - > msg_idx = 0 ;
} else {
/* pen is still down, continue with the measurement */
ts - > msg_idx = 0 ;
status = spi_async ( ts - > spi , & ts - > msg [ 0 ] ) ;
if ( status )
dev_err ( & ts - > spi - > dev , " spi_async --> %d \n " , status ) ;
}
spin_unlock_irq ( & ts - > lock ) ;
2006-04-12 07:42:03 +04:00
}
static irqreturn_t ads7846_irq ( int irq , void * handle , struct pt_regs * regs )
{
struct ads7846 * ts = handle ;
unsigned long flags ;
2006-01-09 00:34:21 +03:00
spin_lock_irqsave ( & ts - > lock , flags ) ;
2006-04-12 07:44:05 +04:00
if ( likely ( ts - > get_pendown_state ( ) ) ) {
2006-01-09 00:34:21 +03:00
if ( ! ts - > irq_disabled ) {
2006-04-12 07:42:03 +04:00
/* REVISIT irq logic for many ARM chips has cloned a
* bug wherein disabling an irq in its handler won ' t
* work ; ( it ' s disabled lazily , and too late to work .
* until all their irq logic is fixed , we must shadow
* that state here .
*/
2006-01-09 00:34:21 +03:00
ts - > irq_disabled = 1 ;
disable_irq ( ts - > spi - > irq ) ;
2006-04-12 07:42:03 +04:00
ts - > pending = 1 ;
mod_timer ( & ts - > timer , jiffies ) ;
}
2006-01-09 00:34:21 +03:00
}
spin_unlock_irqrestore ( & ts - > lock , flags ) ;
2006-04-12 07:43:55 +04:00
return IRQ_HANDLED ;
2006-01-09 00:34:21 +03:00
}
/*--------------------------------------------------------------------------*/
2006-04-12 07:43:55 +04:00
/* Must be called with ts->lock held */
static void ads7846_disable ( struct ads7846 * ts )
2006-01-09 00:34:21 +03:00
{
2006-04-12 07:43:55 +04:00
if ( ts - > disabled )
return ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:44:05 +04:00
ts - > disabled = 1 ;
2006-01-09 00:34:21 +03:00
/* are we waiting for IRQ, or polling? */
2006-04-12 07:44:05 +04:00
if ( ! ts - > pending ) {
ts - > irq_disabled = 1 ;
disable_irq ( ts - > spi - > irq ) ;
2006-01-09 00:34:21 +03:00
} else {
2006-04-12 07:44:05 +04:00
/* the timer will run at least once more, and
* leave everything in a clean state , IRQ disabled
2006-01-09 00:34:21 +03:00
*/
2006-04-12 07:44:05 +04:00
while ( ts - > pending ) {
2006-04-12 07:43:55 +04:00
spin_unlock_irq ( & ts - > lock ) ;
2006-04-12 07:42:25 +04:00
msleep ( 1 ) ;
2006-04-12 07:43:55 +04:00
spin_lock_irq ( & ts - > lock ) ;
2006-01-09 00:34:21 +03:00
}
}
/* we know the chip's in lowpower mode since we always
* leave it that way after every request
*/
2006-04-12 07:43:55 +04:00
}
/* Must be called with ts->lock held */
static void ads7846_enable ( struct ads7846 * ts )
{
if ( ! ts - > disabled )
return ;
ts - > disabled = 0 ;
ts - > irq_disabled = 0 ;
enable_irq ( ts - > spi - > irq ) ;
}
static int ads7846_suspend ( struct spi_device * spi , pm_message_t message )
{
struct ads7846 * ts = dev_get_drvdata ( & spi - > dev ) ;
spin_lock_irq ( & ts - > lock ) ;
spi - > dev . power . power_state = message ;
ads7846_disable ( ts ) ;
spin_unlock_irq ( & ts - > lock ) ;
2006-01-09 00:34:21 +03:00
return 0 ;
2006-04-12 07:43:55 +04:00
2006-01-09 00:34:21 +03:00
}
2006-01-09 00:34:25 +03:00
static int ads7846_resume ( struct spi_device * spi )
2006-01-09 00:34:21 +03:00
{
2006-01-09 00:34:25 +03:00
struct ads7846 * ts = dev_get_drvdata ( & spi - > dev ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:43:55 +04:00
spin_lock_irq ( & ts - > lock ) ;
2006-01-09 00:34:25 +03:00
spi - > dev . power . power_state = PMSG_ON ;
2006-04-12 07:43:55 +04:00
ads7846_enable ( ts ) ;
spin_unlock_irq ( & ts - > lock ) ;
2006-01-09 00:34:21 +03:00
return 0 ;
}
2006-01-09 00:34:25 +03:00
static int __devinit ads7846_probe ( struct spi_device * spi )
2006-01-09 00:34:21 +03:00
{
struct ads7846 * ts ;
2006-02-15 08:49:22 +03:00
struct input_dev * input_dev ;
2006-01-09 00:34:25 +03:00
struct ads7846_platform_data * pdata = spi - > dev . platform_data ;
2006-04-12 07:42:03 +04:00
struct spi_message * m ;
2006-01-09 00:34:21 +03:00
struct spi_transfer * x ;
2006-02-15 08:49:22 +03:00
int err ;
2006-01-09 00:34:21 +03:00
if ( ! spi - > irq ) {
2006-01-09 00:34:25 +03:00
dev_dbg ( & spi - > dev , " no IRQ? \n " ) ;
2006-01-09 00:34:21 +03:00
return - ENODEV ;
}
if ( ! pdata ) {
2006-01-09 00:34:25 +03:00
dev_dbg ( & spi - > dev , " no platform data? \n " ) ;
2006-01-09 00:34:21 +03:00
return - ENODEV ;
}
/* don't exceed max specified sample rate */
2006-02-15 08:49:35 +03:00
if ( spi - > max_speed_hz > ( 125000 * SAMPLE_BITS ) ) {
2006-01-09 00:34:25 +03:00
dev_dbg ( & spi - > dev , " f(sample) %d KHz? \n " ,
2006-02-15 08:49:35 +03:00
( spi - > max_speed_hz / SAMPLE_BITS ) / 1000 ) ;
2006-01-09 00:34:21 +03:00
return - EINVAL ;
}
2006-04-12 07:44:05 +04:00
if ( pdata - > get_pendown_state = = NULL ) {
dev_dbg ( & spi - > dev , " no get_pendown_state function? \n " ) ;
return - EINVAL ;
}
2006-01-09 00:34:21 +03:00
/* We'd set the wordsize to 12 bits ... except that some controllers
* will then treat the 8 bit command words as 12 bits ( and drop the
* four MSBs of the 12 bit result ) . Result : inputs must be shifted
* to discard the four garbage LSBs .
*/
2006-02-15 08:49:22 +03:00
ts = kzalloc ( sizeof ( struct ads7846 ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! ts | | ! input_dev ) {
err = - ENOMEM ;
goto err_free_mem ;
}
2006-01-09 00:34:21 +03:00
2006-01-09 00:34:25 +03:00
dev_set_drvdata ( & spi - > dev , ts ) ;
2006-02-15 08:49:22 +03:00
spi - > dev . power . power_state = PMSG_ON ;
2006-01-09 00:34:21 +03:00
ts - > spi = spi ;
2006-02-15 08:49:22 +03:00
ts - > input = input_dev ;
2006-01-09 00:34:21 +03:00
init_timer ( & ts - > timer ) ;
ts - > timer . data = ( unsigned long ) ts ;
ts - > timer . function = ads7846_timer ;
2006-04-12 07:43:55 +04:00
spin_lock_init ( & ts - > lock ) ;
2006-01-09 00:34:21 +03:00
ts - > model = pdata - > model ? : 7846 ;
ts - > vref_delay_usecs = pdata - > vref_delay_usecs ? : 100 ;
ts - > x_plate_ohms = pdata - > x_plate_ohms ? : 400 ;
2006-04-12 07:42:03 +04:00
ts - > debounce_max = pdata - > debounce_max ? : 1 ;
ts - > debounce_tol = pdata - > debounce_tol ? : 10 ;
2006-04-12 07:44:05 +04:00
ts - > get_pendown_state = pdata - > get_pendown_state ;
2006-01-09 00:34:21 +03:00
2006-02-15 08:49:22 +03:00
snprintf ( ts - > phys , sizeof ( ts - > phys ) , " %s/input0 " , spi - > dev . bus_id ) ;
2006-01-09 00:34:21 +03:00
2006-02-15 08:49:22 +03:00
input_dev - > name = " ADS784x Touchscreen " ;
input_dev - > phys = ts - > phys ;
input_dev - > cdev . dev = & spi - > dev ;
2006-01-09 00:34:21 +03:00
2006-02-15 08:49:22 +03:00
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 ,
2006-01-09 00:34:21 +03:00
pdata - > x_min ? : 0 ,
pdata - > x_max ? : MAX_12BIT ,
0 , 0 ) ;
2006-02-15 08:49:22 +03:00
input_set_abs_params ( input_dev , ABS_Y ,
2006-01-09 00:34:21 +03:00
pdata - > y_min ? : 0 ,
pdata - > y_max ? : MAX_12BIT ,
0 , 0 ) ;
2006-02-15 08:49:22 +03:00
input_set_abs_params ( input_dev , ABS_PRESSURE ,
2006-01-09 00:34:21 +03:00
pdata - > pressure_min , pdata - > pressure_max , 0 , 0 ) ;
/* set up the transfers to read touchscreen state; this assumes we
* use formula # 2 for pressure , not # 3.
*/
2006-04-12 07:42:03 +04:00
m = & ts - > msg [ 0 ] ;
2006-01-09 00:34:21 +03:00
x = ts - > xfer ;
2006-04-12 07:42:03 +04:00
spi_message_init ( m ) ;
2006-01-09 00:34:21 +03:00
/* y- still on; turn on only y+ (and ADC) */
2006-02-15 08:49:35 +03:00
ts - > read_y = READ_Y ;
x - > tx_buf = & ts - > read_y ;
2006-01-09 00:34:21 +03:00
x - > len = 1 ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
2006-02-15 08:49:35 +03:00
2006-01-09 00:34:21 +03:00
x + + ;
x - > rx_buf = & ts - > tc . y ;
x - > len = 2 ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
m - > complete = ads7846_debounce ;
m - > context = ts ;
m + + ;
spi_message_init ( m ) ;
/* turn y- off, x+ on, then leave in lowpower */
x + + ;
ts - > read_x = READ_X ;
x - > tx_buf = & ts - > read_x ;
x - > len = 1 ;
spi_message_add_tail ( x , m ) ;
x + + ;
x - > rx_buf = & ts - > tc . x ;
x - > len = 2 ;
spi_message_add_tail ( x , m ) ;
m - > complete = ads7846_debounce ;
m - > context = ts ;
2006-01-09 00:34:21 +03:00
/* turn y+ off, x- on; we'll use formula #2 */
if ( ts - > model = = 7846 ) {
2006-04-12 07:42:03 +04:00
m + + ;
spi_message_init ( m ) ;
2006-02-15 08:49:35 +03:00
x + + ;
ts - > read_z1 = READ_Z1 ;
x - > tx_buf = & ts - > read_z1 ;
2006-01-09 00:34:21 +03:00
x - > len = 1 ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
2006-02-15 08:49:35 +03:00
2006-01-09 00:34:21 +03:00
x + + ;
x - > rx_buf = & ts - > tc . z1 ;
x - > len = 2 ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
m - > complete = ads7846_debounce ;
m - > context = ts ;
m + + ;
spi_message_init ( m ) ;
2006-01-09 00:34:21 +03:00
2006-02-15 08:49:35 +03:00
x + + ;
ts - > read_z2 = READ_Z2 ;
x - > tx_buf = & ts - > read_z2 ;
2006-01-09 00:34:21 +03:00
x - > len = 1 ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
2006-02-15 08:49:35 +03:00
2006-01-09 00:34:21 +03:00
x + + ;
x - > rx_buf = & ts - > tc . z2 ;
x - > len = 2 ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:42:03 +04:00
m - > complete = ads7846_debounce ;
m - > context = ts ;
}
2006-04-12 07:41:49 +04:00
/* power down */
2006-04-12 07:42:03 +04:00
m + + ;
spi_message_init ( m ) ;
2006-04-12 07:41:49 +04:00
x + + ;
ts - > pwrdown = PWRDOWN ;
x - > tx_buf = & ts - > pwrdown ;
x - > len = 1 ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
2006-04-12 07:41:49 +04:00
x + + ;
x - > rx_buf = & ts - > dummy ;
x - > len = 2 ;
2006-02-15 08:49:35 +03:00
CS_CHANGE ( * x ) ;
2006-04-12 07:42:03 +04:00
spi_message_add_tail ( x , m ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:42:03 +04:00
m - > complete = ads7846_rx ;
m - > context = ts ;
2006-01-09 00:34:21 +03:00
2006-01-19 15:26:57 +03:00
if ( request_irq ( spi - > irq , ads7846_irq ,
SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING ,
spi - > dev . bus_id , ts ) ) {
2006-01-09 00:34:25 +03:00
dev_dbg ( & spi - > dev , " irq %d busy? \n " , spi - > irq ) ;
2006-02-15 08:49:22 +03:00
err = - EBUSY ;
goto err_free_mem ;
2006-01-09 00:34:21 +03:00
}
2006-01-09 00:34:25 +03:00
dev_info ( & spi - > dev , " touchscreen, irq %d \n " , spi - > irq ) ;
2006-01-09 00:34:21 +03:00
/* take a first sample, leaving nPENIRQ active; avoid
* the touchscreen , in case it ' s not connected .
*/
2006-01-09 00:34:25 +03:00
( void ) ads7846_read12_ser ( & spi - > dev ,
2006-01-09 00:34:21 +03:00
READ_12BIT_SER ( vaux ) | ADS_PD10_ALL_ON ) ;
/* ads7843/7845 don't have temperature sensors, and
* use the other sensors a bit differently too
*/
if ( ts - > model = = 7846 ) {
2006-01-09 00:34:25 +03:00
device_create_file ( & spi - > dev , & dev_attr_temp0 ) ;
device_create_file ( & spi - > dev , & dev_attr_temp1 ) ;
2006-01-09 00:34:21 +03:00
}
if ( ts - > model ! = 7845 )
2006-01-09 00:34:25 +03:00
device_create_file ( & spi - > dev , & dev_attr_vbatt ) ;
device_create_file ( & spi - > dev , & dev_attr_vaux ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:41:32 +04:00
device_create_file ( & spi - > dev , & dev_attr_pen_down ) ;
2006-04-12 07:43:55 +04:00
device_create_file ( & spi - > dev , & dev_attr_disable ) ;
2006-02-15 08:49:22 +03:00
err = input_register_device ( input_dev ) ;
if ( err )
2006-04-12 07:43:55 +04:00
goto err_remove_attr ;
2006-02-15 08:49:22 +03:00
2006-01-09 00:34:21 +03:00
return 0 ;
2006-02-15 08:49:22 +03:00
2006-04-12 07:43:55 +04:00
err_remove_attr :
device_remove_file ( & spi - > dev , & dev_attr_disable ) ;
device_remove_file ( & spi - > dev , & dev_attr_pen_down ) ;
if ( ts - > model = = 7846 ) {
device_remove_file ( & spi - > dev , & dev_attr_temp1 ) ;
device_remove_file ( & spi - > dev , & dev_attr_temp0 ) ;
}
if ( ts - > model ! = 7845 )
device_remove_file ( & spi - > dev , & dev_attr_vbatt ) ;
device_remove_file ( & spi - > dev , & dev_attr_vaux ) ;
2006-02-15 08:49:22 +03:00
free_irq ( spi - > irq , ts ) ;
err_free_mem :
input_free_device ( input_dev ) ;
kfree ( ts ) ;
return err ;
2006-01-09 00:34:21 +03:00
}
2006-01-09 00:34:25 +03:00
static int __devexit ads7846_remove ( struct spi_device * spi )
2006-01-09 00:34:21 +03:00
{
2006-01-09 00:34:25 +03:00
struct ads7846 * ts = dev_get_drvdata ( & spi - > dev ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:43:55 +04:00
input_unregister_device ( ts - > input ) ;
2006-01-09 00:34:25 +03:00
ads7846_suspend ( spi , PMSG_SUSPEND ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:43:55 +04:00
device_remove_file ( & spi - > dev , & dev_attr_disable ) ;
2006-04-12 07:41:32 +04:00
device_remove_file ( & spi - > dev , & dev_attr_pen_down ) ;
2006-01-09 00:34:21 +03:00
if ( ts - > model = = 7846 ) {
2006-01-09 00:34:25 +03:00
device_remove_file ( & spi - > dev , & dev_attr_temp1 ) ;
2006-04-12 07:43:55 +04:00
device_remove_file ( & spi - > dev , & dev_attr_temp0 ) ;
2006-01-09 00:34:21 +03:00
}
if ( ts - > model ! = 7845 )
2006-01-09 00:34:25 +03:00
device_remove_file ( & spi - > dev , & dev_attr_vbatt ) ;
device_remove_file ( & spi - > dev , & dev_attr_vaux ) ;
2006-01-09 00:34:21 +03:00
2006-04-12 07:43:55 +04:00
free_irq ( ts - > spi - > irq , ts ) ;
2006-04-12 07:44:05 +04:00
/* suspend left the IRQ disabled */
enable_irq ( ts - > spi - > irq ) ;
2006-04-12 07:43:55 +04:00
2006-01-09 00:34:21 +03:00
kfree ( ts ) ;
2006-01-09 00:34:25 +03:00
dev_dbg ( & spi - > dev , " unregistered touchscreen \n " ) ;
2006-01-09 00:34:21 +03:00
return 0 ;
}
2006-01-09 00:34:25 +03:00
static struct spi_driver ads7846_driver = {
. driver = {
. name = " ads7846 " ,
. bus = & spi_bus_type ,
. owner = THIS_MODULE ,
} ,
2006-01-09 00:34:21 +03:00
. probe = ads7846_probe ,
2006-01-09 00:34:25 +03:00
. remove = __devexit_p ( ads7846_remove ) ,
2006-01-09 00:34:21 +03:00
. suspend = ads7846_suspend ,
. resume = ads7846_resume ,
} ;
static int __init ads7846_init ( void )
{
/* grr, board-specific init should stay out of drivers!! */
# ifdef CONFIG_ARCH_OMAP
if ( machine_is_omap_osk ( ) ) {
/* GPIO4 = PENIRQ; GPIO6 = BUSY */
omap_request_gpio ( 4 ) ;
omap_set_gpio_direction ( 4 , 1 ) ;
omap_request_gpio ( 6 ) ;
omap_set_gpio_direction ( 6 , 1 ) ;
}
// also TI 1510 Innovator, bitbanging through FPGA
// also Nokia 770
// also Palm Tungsten T2
# endif
// PXA:
// also Dell Axim X50
// also HP iPaq H191x/H192x/H415x/H435x
2006-01-09 00:34:25 +03:00
// also Intel Lubbock (additional to UCB1400; as temperature sensor)
2006-01-09 00:34:21 +03:00
// also Sharp Zaurus C7xx, C8xx (corgi/sheperd/husky)
2006-01-09 00:34:25 +03:00
// Atmel at91sam9261-EK uses ads7843
2006-01-09 00:34:21 +03:00
// also various AMD Au1x00 devel boards
2006-01-09 00:34:25 +03:00
return spi_register_driver ( & ads7846_driver ) ;
2006-01-09 00:34:21 +03:00
}
module_init ( ads7846_init ) ;
static void __exit ads7846_exit ( void )
{
2006-01-09 00:34:25 +03:00
spi_unregister_driver ( & ads7846_driver ) ;
2006-01-09 00:34:21 +03:00
# ifdef CONFIG_ARCH_OMAP
if ( machine_is_omap_osk ( ) ) {
omap_free_gpio ( 4 ) ;
omap_free_gpio ( 6 ) ;
}
# endif
}
module_exit ( ads7846_exit ) ;
MODULE_DESCRIPTION ( " ADS7846 TouchScreen Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;