2005-04-16 15:20:36 -07:00
/*
* linux / arch / mips / au1000 / db1x00 / mirage_ts . c
*
* BRIEF MODULE DESCRIPTION
* Glue between Mirage board - specific touchscreen pieces
* and generic Wolfson Codec touchscreen support .
*
* Based on pb1100_ts . c used in Hydrogen II .
*
* Copyright ( c ) 2003 Embedded Edge , LLC
* dan @ embeddededge . com
*
* 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 SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF
* USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/config.h>
# include <linux/types.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/fs.h>
# include <linux/poll.h>
# include <linux/proc_fs.h>
# include <linux/smp.h>
# include <linux/smp_lock.h>
# include <linux/wait.h>
# include <asm/segment.h>
# include <asm/irq.h>
# include <asm/uaccess.h>
# include <asm/delay.h>
# include <asm/au1000.h>
/*
* Imported interface to Wolfson Codec driver .
*/
extern void * wm97xx_ts_get_handle ( int which ) ;
extern int wm97xx_ts_ready ( void * ts_handle ) ;
extern void wm97xx_ts_set_cal ( void * ts_handle , int xscale , int xtrans , int yscale , int ytrans ) ;
extern u16 wm97xx_ts_get_ac97 ( void * ts_handle , u8 reg ) ;
extern void wm97xx_ts_set_ac97 ( void * ts_handle , u8 reg , u16 val ) ;
extern int wm97xx_ts_read_data ( void * ts_handle , long * x , long * y , long * pressure ) ;
extern void wm97xx_ts_send_data ( void * ts_handle , long x , long y , long z ) ;
int wm97xx_comodule_present = 1 ;
# define TS_NAME "mirage_ts"
# define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
# define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
# define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
# define DPRINTK(format, arg...) printk("%s: " format "\n", __FUNCTION__ , ## arg)
# define PEN_DOWN_IRQ AU1000_GPIO_7
static struct task_struct * ts_task = 0 ;
static DECLARE_COMPLETION ( ts_complete ) ;
static DECLARE_WAIT_QUEUE_HEAD ( pendown_wait ) ;
# ifdef CONFIG_WM97XX_FIVEWIRETS
static int release_pressure = 1 ;
# else
static int release_pressure = 50 ;
# endif
typedef struct {
long x ;
long y ;
} DOWN_EVENT ;
# define SAMPLE_RATE 50 /* samples per second */
# define PEN_DEBOUNCE 5 /* samples for settling - fn of SAMPLE_RATE */
# define PEN_UP_TIMEOUT 10 /* in seconds */
# define PEN_UP_SETTLE 5 /* samples per second */
static struct {
int xscale ;
int xtrans ;
int yscale ;
int ytrans ;
} mirage_ts_cal =
{
#if 0
2005-03-01 10:38:58 +00:00
. xscale = 84 ,
. xtrans = - 157 ,
. yscale = 66 ,
. ytrans = - 150 ,
2005-04-16 15:20:36 -07:00
# else
2005-03-01 10:38:58 +00:00
. xscale = 84 ,
. xtrans = - 150 ,
. yscale = 66 ,
. ytrans = - 146 ,
2005-04-16 15:20:36 -07:00
# endif
} ;
static void pendown_irq ( int irqnr , void * devid , struct pt_regs * regs )
{
//DPRINTK("got one 0x%x", au_readl(SYS_PINSTATERD));
wake_up ( & pendown_wait ) ;
}
static int ts_thread ( void * id )
{
static int pen_was_down = 0 ;
static DOWN_EVENT pen_xy ;
long x , y , z ;
void * ts ; /* handle */
struct task_struct * tsk = current ;
int timeout = HZ / SAMPLE_RATE ;
ts_task = tsk ;
daemonize ( ) ;
tsk - > tty = NULL ;
tsk - > policy = SCHED_FIFO ;
tsk - > rt_priority = 1 ;
strcpy ( tsk - > comm , " touchscreen " ) ;
/* only want to receive SIGKILL */
spin_lock_irq ( & tsk - > sigmask_lock ) ;
siginitsetinv ( & tsk - > blocked , sigmask ( SIGKILL ) ) ;
recalc_sigpending ( tsk ) ;
spin_unlock_irq ( & tsk - > sigmask_lock ) ;
/* get handle for codec */
ts = wm97xx_ts_get_handle ( 0 ) ;
/* proceed only after everybody is ready */
wait_event_timeout ( pendown_wait , wm97xx_ts_ready ( ts ) , HZ / 4 ) ;
/* board-specific calibration */
wm97xx_ts_set_cal ( ts ,
mirage_ts_cal . xscale ,
mirage_ts_cal . xtrans ,
mirage_ts_cal . yscale ,
mirage_ts_cal . ytrans ) ;
/* route Wolfson pendown interrupts to our GPIO */
au_sync ( ) ;
wm97xx_ts_set_ac97 ( ts , 0x4c , wm97xx_ts_get_ac97 ( ts , 0x4c ) & ~ 0x0008 ) ;
au_sync ( ) ;
wm97xx_ts_set_ac97 ( ts , 0x56 , wm97xx_ts_get_ac97 ( ts , 0x56 ) & ~ 0x0008 ) ;
au_sync ( ) ;
wm97xx_ts_set_ac97 ( ts , 0x52 , wm97xx_ts_get_ac97 ( ts , 0x52 ) | 0x2008 ) ;
au_sync ( ) ;
for ( ; ; ) {
interruptible_sleep_on_timeout ( & pendown_wait , timeout ) ;
disable_irq ( PEN_DOWN_IRQ ) ;
if ( signal_pending ( tsk ) ) {
break ;
}
/* read codec */
if ( ! wm97xx_ts_read_data ( ts , & x , & y , & z ) )
z = 0 ; /* treat no-data and pen-up the same */
if ( signal_pending ( tsk ) ) {
break ;
}
if ( z > = release_pressure ) {
y = ~ y ; /* top to bottom */
if ( pen_was_down > 1 /*&& pen_was_down < PEN_DEBOUNCE*/ ) { //THXXX
/* bounce ? */
x = pen_xy . x ;
y = pen_xy . y ;
- - pen_was_down ;
} else if ( pen_was_down < = 1 ) {
pen_xy . x = x ;
pen_xy . y = y ;
if ( pen_was_down )
wm97xx_ts_send_data ( ts , x , y , z ) ;
pen_was_down = PEN_DEBOUNCE ;
}
//wm97xx_ts_send_data(ts, x, y, z);
timeout = HZ / SAMPLE_RATE ;
} else {
if ( pen_was_down ) {
if ( - - pen_was_down )
z = release_pressure ;
else //THXXX
wm97xx_ts_send_data ( ts , pen_xy . x , pen_xy . y , z ) ;
}
/* The pendown signal takes some time to settle after
* reading the pen pressure so wait a little
* before enabling the pen .
*/
if ( ! pen_was_down ) {
// interruptible_sleep_on_timeout(&pendown_wait, HZ / PEN_UP_SETTLE);
timeout = HZ * PEN_UP_TIMEOUT ;
}
}
enable_irq ( PEN_DOWN_IRQ ) ;
}
enable_irq ( PEN_DOWN_IRQ ) ;
ts_task = NULL ;
complete ( & ts_complete ) ;
return 0 ;
}
static int __init ts_mirage_init ( void )
{
int ret ;
/* pen down signal is connected to GPIO 7 */
ret = request_irq ( PEN_DOWN_IRQ , pendown_irq , 0 , " ts-pendown " , NULL ) ;
if ( ret ) {
err ( " unable to get pendown irq%d: [%d] " , PEN_DOWN_IRQ , ret ) ;
return ret ;
}
lock_kernel ( ) ;
ret = kernel_thread ( ts_thread , NULL , CLONE_FS | CLONE_FILES ) ;
if ( ret < 0 ) {
unlock_kernel ( ) ;
return ret ;
}
unlock_kernel ( ) ;
info ( " Mirage touchscreen IRQ initialized. " ) ;
return 0 ;
}
static void __exit ts_mirage_exit ( void )
{
if ( ts_task ) {
send_sig ( SIGKILL , ts_task , 1 ) ;
wait_for_completion ( & ts_complete ) ;
}
free_irq ( PEN_DOWN_IRQ , NULL ) ;
}
module_init ( ts_mirage_init ) ;
module_exit ( ts_mirage_exit ) ;