262 lines
6.8 KiB
C
262 lines
6.8 KiB
C
|
/*
|
||
|
* 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 02139, 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
|
||
|
xscale: 84,
|
||
|
xtrans: -157,
|
||
|
yscale: 66,
|
||
|
ytrans: -150,
|
||
|
#else
|
||
|
xscale: 84,
|
||
|
xtrans: -150,
|
||
|
yscale: 66,
|
||
|
ytrans: -146,
|
||
|
#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);
|
||
|
|