[PATCH 2.4] Wolfson AC97 touch screen driver - Input Eventinterface

From: Liam Girdwood
Date: Wed Dec 17 2003 - 09:48:27 EST


This patch adds support for the kernel input event interface to the
Wolfson WM9705/WM9712 touchscreen driver.

Patch is against 2.4.24-pre1

Changes:-

o Added Input event interface (Stanley Cai)
o Improved driver CPU utilisation (Ian Molton)
o Removed ADC source bits from output (me)
o Added coordinate streaming mode on XScale (me)
o Removed old h3600 TS_EVENT interface (me)

Liam
diff -urN a/drivers/sound/ac97_plugin_wm97xx.c b/drivers/sound/ac97_plugin_wm97xx.c
--- a/drivers/sound/ac97_plugin_wm97xx.c 2003-11-28 18:26:20.000000000 +0000
+++ b/drivers/sound/ac97_plugin_wm97xx.c 2003-12-17 14:14:36.000000000 +0000
@@ -5,6 +5,7 @@
* Copyright 2003 Wolfson Microelectronics PLC.
* Author: Liam Girdwood
* liam.girdwood@xxxxxxxxxxxxxxxx or linux@xxxxxxxxxxxxxxxx
+ * parts (c) Ian Molton <spyro@xxxxxxx>
*
* 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
@@ -32,56 +33,82 @@
* - supports WM9705, WM9712
* - polling mode
* - coordinate polling
+ * - continuous mode
* - adjustable rpu/dpp settings
* - adjustable pressure current
* - adjustable sample settle delay
* - 4 and 5 wire touchscreens (5 wire is WM9712 only)
* - pen down detection
- * - battery monitor
- * - sample AUX adc's
* - power management
- * - direct AC97 IO from userspace (#define WM97XX_TS_DEBUG)
*
* TODO:
- * - continuous mode
* - adjustable sample rate
* - AUX adc in coordinate / continous modes
- * - Official device identifier or misc device ?
+ * - Codec GPIO
+ * - battery monitor
+ * - sample AUX adc's
*
* Revision history
* 7th May 2003 Initial version.
* 6th June 2003 Added non module support and AC97 registration.
* 18th June 2003 Added AUX adc sampling.
* 23rd June 2003 Did some minimal reformatting, fixed a couple of
- * locking bugs and noted a race to fix.
+ * locking bugs and noted a race to fix.
* 24th June 2003 Added power management and fixed race condition.
+ * 10th July 2003 Changed to a misc device.
+ * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h
+ * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch()
+ * because some ac97_read/ac_97_write call schedule()
+ * 7th Nov 2003 Added Input touch event interface, stanley.cai@xxxxxxxxx
+ * 13th Nov 2003 Removed h3600 touch interface, added interrupt based
+ * pen down notification and implemented continous mode
+ * on XScale arch.
+ * 4th Dec 2003 Removed ADC src bits from sample data.
*/

#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/fs.h>
+#include <linux/input.h>
#include <linux/delay.h>
-#include <linux/poll.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
-#include <linux/miscdevice.h>
#include <linux/pm.h>
-#include <linux/wm97xx.h> /* WM97xx registers and bits */
#include <asm/uaccess.h> /* get_user,copy_to_user */
#include <asm/io.h>

-#define TS_NAME "ac97_plugin_wm97xx"
+#include "wm97xx.h"
+
+#define TS_NAME "wm97xx"
#define TS_MINOR 16
-#define WM_TS_VERSION "0.6"
+#define WM_TS_VERSION "0.10"
#define AC97_NUM_REG 64

+/*
+ * Machine specific set up.
+ *
+ * This is for targets that can support a PEN down interrupt and/or
+ * streaming back touch data in an AC97 slot (not slot 1). The
+ * streaming touch data is read back via the targets AC97 FIFO's
+ */
+#if defined(CONFIG_ARCH_PXA)
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/irqs.h>
+#define WM97XX_IRQ IRQ_AC97
+#define WM97XX_FIFO_HAS_DATA MISR & (1 << 2)
+#define WM97XX_READ_FIFO MODR & (0xffff)
+#endif
+
+#ifndef WM97XX_IRQ
+#define WM97XX_IRQ 0
+#define WM97XX_FIFO_HAS_DATA 0
+#define WM97XX_READ_FIFO 0
+#endif

/*
* Debug
*/
-
#define PFX TS_NAME
#define WM97XX_TS_DEBUG 0

@@ -97,8 +124,7 @@
/*
* Module parameters
*/
-
-
+
/*
* Set the codec sample mode.
*
@@ -124,8 +150,8 @@
*/
MODULE_PARM(mode,"i");
MODULE_PARM_DESC(mode, "Set WM97XX operation mode");
-static int mode = 0;
-
+static int mode = 0;
+
/*
* WM9712 - Set internal pull up for pen detect.
*
@@ -137,7 +163,7 @@
*/
MODULE_PARM(rpu,"i");
MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
-static int rpu = 0;
+static int rpu = 0;

/*
* WM9705 - Pen detect comparator threshold.
@@ -151,8 +177,8 @@
*/
MODULE_PARM(pdd,"i");
MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
-static int pdd = 0;
-
+static int pdd = 0;
+
/*
* Set current used for pressure measurement.
*
@@ -174,7 +200,7 @@
*/
MODULE_PARM(five_wire,"i");
MODULE_PARM_DESC(five_wire, "Set 5 wire touchscreen.");
-static int five_wire = 0;
+static int five_wire = 0;

/*
* Set adc sample delay.
@@ -190,104 +216,71 @@
*/
MODULE_PARM(delay,"i");
MODULE_PARM_DESC(delay, "Set adc sample delay.");
-static int delay = 4;
-
+static int delay = 4;

-/* +++++++++++++ Lifted from include/linux/h3600_ts.h ++++++++++++++*/
-typedef struct {
- unsigned short pressure; // touch pressure
- unsigned short x; // calibrated X
- unsigned short y; // calibrated Y
- unsigned short millisecs; // timestamp of this event
-} TS_EVENT;
-
-typedef struct {
- int xscale;
- int xtrans;
- int yscale;
- int ytrans;
- int xyswap;
-} TS_CAL;
-
-/* Use 'f' as magic number */
-#define IOC_MAGIC 'f'
-
-#define TS_GET_RATE _IO(IOC_MAGIC, 8)
-#define TS_SET_RATE _IO(IOC_MAGIC, 9)
-#define TS_GET_CAL _IOR(IOC_MAGIC, 10, TS_CAL)
-#define TS_SET_CAL _IOW(IOC_MAGIC, 11, TS_CAL)
-
-/* +++++++++++++ Done lifted from include/linux/h3600_ts.h +++++++++*/
-
-#define TS_GET_COMP1 _IOR(IOC_MAGIC, 12, short)
-#define TS_GET_COMP2 _IOR(IOC_MAGIC, 13, short)
-#define TS_GET_BMON _IOR(IOC_MAGIC, 14, short)
-#define TS_GET_WIPER _IOR(IOC_MAGIC, 15, short)
-
-#ifdef WM97XX_TS_DEBUG
-/* debug get/set ac97 codec register ioctl's */
-#define TS_GET_AC97_REG _IOR(IOC_MAGIC, 20, short)
-#define TS_SET_AC97_REG _IOW(IOC_MAGIC, 21, short)
-#define TS_SET_AC97_INDEX _IOW(IOC_MAGIC, 22, short)
-#endif
-
-#define EVENT_BUFSIZE 128
+/*
+ * Pen down detection
+ *
+ * Pen down detection can either be via an interrupt (preferred) or
+ * by polling the PDEN bit. This is an option because some systems may
+ * not support the pen down interrupt.
+ *
+ * Set pen_int to 1 to enable interrupt driven pen down detection.
+ */
+MODULE_PARM(pen_int,"i");
+MODULE_PARM_DESC(pen_int, "Set pen down interrupt");
+static int pen_int = 0;

typedef struct {
- TS_CAL cal; /* Calibration values */
- TS_EVENT event_buf[EVENT_BUFSIZE];/* The event queue */
- int nextIn, nextOut;
- int event_count;
int is_wm9712:1; /* are we a WM912 or a WM9705 */
int is_registered:1; /* Is the driver AC97 registered */
- int line_pgal:5;
- int line_pgar:5;
- int phone_pga:5;
- int mic_pgal:5;
- int mic_pgar:5;
- int overruns; /* event buffer overruns */
int adc_errs; /* sample read back errors */
-#ifdef WM97XX_TS_DEBUG
- short ac97_index;
-#endif
- struct fasync_struct *fasync; /* asynch notification */
- struct timer_list acq_timer; /* Timer for triggering acquisitions */
wait_queue_head_t wait; /* read wait queue */
spinlock_t lock;
struct ac97_codec *codec;
+#if defined(CONFIG_PROC_FS)
struct proc_dir_entry *wm97xx_ts_ps;
-#ifdef WM97XX_TS_DEBUG
- struct proc_dir_entry *wm97xx_debug_ts_ps;
#endif
+#if defined(CONFIG_PM)
struct pm_dev * pm;
+ int line_pgal:5;
+ int line_pgar:5;
+ int phone_pga:5;
+ int mic_pgal:5;
+ int mic_pgar:5;
+#endif
+ struct input_dev *idev;
+ struct completion init_exit;
+ struct task_struct *rtask;
+ struct semaphore sem;
+ int use_count;
+ int restart;
} wm97xx_ts_t;

static inline void poll_delay (void);
static int __init wm97xx_ts_init_module(void);
-static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample);
-static int wm97xx_coord_read_adc (wm97xx_ts_t* ts, u16* x, u16* y,
- u16* pressure);
static inline int pendown (wm97xx_ts_t *ts);
-static void wm97xx_acq_timer(unsigned long data);
-static int wm97xx_fasync(int fd, struct file *filp, int mode);
-static int wm97xx_ioctl(struct inode * inode, struct file *filp,
- unsigned int cmd, unsigned long arg);
-static unsigned int wm97xx_poll(struct file * filp, poll_table * wait);
-static ssize_t wm97xx_read(struct file * filp, char * buf, size_t count,
- loff_t * l);
-static int wm97xx_open(struct inode * inode, struct file * filp);
-static int wm97xx_release(struct inode * inode, struct file * filp);
+static void wm97xx_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample);
static void init_wm97xx_phy(void);
-static int adc_get (wm97xx_ts_t *ts, unsigned short *value, int id);
static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver);
static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver);
static void wm97xx_ts_cleanup_module(void);
+static int wm97xx_ts_evt_add(wm97xx_ts_t* ts, u16 pressure, u16 x, u16 y);
+static int wm97xx_ts_evt_release(wm97xx_ts_t* ts);
+
+#if defined(CONFIG_PM)
static int wm97xx_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data);
static void wm97xx_suspend(void);
static void wm97xx_resume(void);
static void wm9712_pga_save(wm97xx_ts_t* ts);
static void wm9712_pga_restore(wm97xx_ts_t* ts);
+#endif

+/* we only support a single touchscreen */
+static wm97xx_ts_t wm97xx_ts;
+static struct input_dev wm97xx_input;
+
/* AC97 registration info */
static struct ac97_driver wm9705_driver = {
codec_id: 0x574D4C05,
@@ -304,38 +297,40 @@
probe: wm97xx_probe,
remove: __devexit_p(wm97xx_remove),
};
-
-/* we only support a single touchscreen */
-static wm97xx_ts_t wm97xx_ts;
-
+
/*
* ADC sample delay times in uS
*/
static const int delay_table[16] = {
- 21, // 1 AC97 Link frames
- 42, // 2
- 84, // 4
- 167, // 8
- 333, // 16
- 667, // 32
- 1000, // 48
- 1333, // 64
- 2000, // 96
- 2667, // 128
- 3333, // 160
- 4000, // 192
- 4667, // 224
- 5333, // 256
- 6000, // 288
- 0 // No delay, switch matrix always on
+ 21,// 1 AC97 Link frames
+ 42,// 2
+ 84,// 4
+ 167,// 8
+ 333,// 16
+ 667,// 32
+ 1000,// 48
+ 1333,// 64
+ 2000,// 96
+ 2667,// 128
+ 3333,// 160
+ 4000,// 192
+ 4667,// 224
+ 5333,// 256
+ 6000,// 288
+ 0 // No delay, switch matrix always on
};

+// Todo
+static void wm97xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ info("int recv");
+}
+
/*
* Delay after issuing a POLL command.
*
* The delay is 3 AC97 link frames + the touchpanel settling delay
*/
-
static inline void poll_delay(void)
{
int pdelay = 3 * AC97_LINK_FRAME + delay_table[delay];
@@ -344,63 +339,6 @@


/*
- * sample the auxillary ADC's
- */
-
-static int adc_get(wm97xx_ts_t* ts, unsigned short * value, int id)
-{
- short adcsel = 0;
-
- /* first find out our adcsel flag */
- if (ts->is_wm9712) {
- switch (id) {
- case TS_COMP1:
- adcsel = WM9712_ADCSEL_COMP1;
- break;
- case TS_COMP2:
- adcsel = WM9712_ADCSEL_COMP2;
- break;
- case TS_BMON:
- adcsel = WM9712_ADCSEL_BMON;
- break;
- case TS_WIPER:
- adcsel = WM9712_ADCSEL_WIPER;
- break;
- }
- } else {
- switch (id) {
- case TS_COMP1:
- adcsel = WM9705_ADCSEL_PCBEEP;
- break;
- case TS_COMP2:
- adcsel = WM9705_ADCSEL_PHONE;
- break;
- case TS_BMON:
- adcsel = WM9705_ADCSEL_BMON;
- break;
- case TS_WIPER:
- adcsel = WM9705_ADCSEL_AUX;
- break;
- }
- }
-
- /* now sample the adc */
- if (mode == 1) {
- /* coordinate mode - not currently available (TODO) */
- return 0;
- }
- else
- {
- /* polling mode */
- if (!wm97xx_poll_read_adc(ts, adcsel, value))
- return 0;
- }
-
- return 1;
-}
-
-
-/*
* Read a sample from the adc in polling mode.
*/
static int wm97xx_poll_read_adc (wm97xx_ts_t* ts, u16 adcsel, u16* sample)
@@ -412,7 +350,7 @@
dig1 = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1);
dig1&=0x0fff;
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1 | adcsel |
- WM97XX_POLL);
+ WM97XX_POLL);

/* wait 3 AC97 time slots + delay for conversion */
poll_delay();
@@ -420,7 +358,7 @@
/* wait for POLL to go low */
while ((ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) {
udelay(AC97_LINK_FRAME);
- timeout--;
+ timeout--;
}
if (timeout > 0)
*sample = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD);
@@ -429,7 +367,7 @@
err ("adc sample timeout");
return 0;
}
-
+
/* check we have correct sample */
if ((*sample & 0x7000) != adcsel ) {
err ("adc wrong sample, read %x got %x", adcsel, *sample & 0x7000);
@@ -487,334 +425,91 @@
}

/*
- * Is the pen down ?
+ * Sample the touchscreen in polling mode
*/
-static inline int pendown (wm97xx_ts_t *ts)
+int wm97xx_poll_touch(wm97xx_ts_t *ts)
{
- return ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN;
-}
-
-/*
- * X,Y coordinates and pressure aquisition function.
- * This function is run by a kernel timer and it's frequency between
- * calls is the touchscreen polling rate;
- */
-
-static void wm97xx_acq_timer(unsigned long data)
-{
- wm97xx_ts_t* ts = (wm97xx_ts_t*)data;
- unsigned long flags;
- long x,y;
- TS_EVENT event;
+ u16 x, y, p;

- spin_lock_irqsave(&ts->lock, flags);
-
- /* are we still registered ? */
- if (!ts->is_registered) {
- spin_unlock_irqrestore(&ts->lock, flags);
- return; /* we better stop then */
+ if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_X, &x)) {
+ info("x %d\n", x);
+ return -EIO;
}
-
- /* read coordinates if pen is down */
- if (!pendown(ts))
- goto acq_exit;
-
- if (mode == 1) {
- /* coordinate mode */
- if (!wm97xx_coord_read_adc(ts, (u16*)&x, (u16*)&y, &event.pressure))
- goto acq_exit;
- } else
- {
- /* polling mode */
- if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_X, (u16*)&x))
- goto acq_exit;
- if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_Y, (u16*)&y))
- goto acq_exit;
-
- /* only read pressure if we have to */
- if (!five_wire && pil) {
- if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_PRES, &event.pressure))
- goto acq_exit;
- }
- else
- event.pressure = 0;
+ if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_Y, &y)) {
+ info("y %d\n", y);
+ return -EIO;
}
- /* timestamp this new event. */
- event.millisecs = jiffies;
-
- /* calibrate and remove unwanted bits from samples */
- event.pressure &= 0x0fff;
-
- x &= 0x00000fff;
- x = ((ts->cal.xscale * x) >> 8) + ts->cal.xtrans;
- event.x = (u16)x;
-
- y &= 0x00000fff;
- y = ((ts->cal.yscale * y) >> 8) + ts->cal.ytrans;
- event.y = (u16)y;
-
- /* add this event to the event queue */
- ts->event_buf[ts->nextIn++] = event;
- if (ts->nextIn == EVENT_BUFSIZE)
- ts->nextIn = 0;
- if (ts->event_count < EVENT_BUFSIZE) {
- ts->event_count++;
- } else {
- /* throw out the oldest event */
- if (++ts->nextOut == EVENT_BUFSIZE) {
- ts->nextOut = 0;
- ts->overruns++;
- }
- }
-
- /* async notify */
- if (ts->fasync)
- kill_fasync(&ts->fasync, SIGIO, POLL_IN);
- /* wake up any read call */
- if (waitqueue_active(&ts->wait))
- wake_up_interruptible(&ts->wait);
-
- /* schedule next acquire */
-acq_exit:
- ts->acq_timer.expires = jiffies + HZ / 100;
- add_timer(&ts->acq_timer);
-
- spin_unlock_irqrestore(&ts->lock, flags);
-}
-
-
-/* +++++++++++++ File operations ++++++++++++++*/
-
-static int wm97xx_fasync(int fd, struct file *filp, int mode)
-{
- wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
- return fasync_helper(fd, filp, mode, &ts->fasync);
-}
-
-static int wm97xx_ioctl(struct inode * inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- unsigned short adc_value;
-#ifdef WM97XX_TS_DEBUG
- short data;
-#endif
- wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
-
- switch(cmd) {
- case TS_GET_RATE: /* TODO: what is this? */
- break;
- case TS_SET_RATE: /* TODO: what is this? */
- break;
- case TS_GET_CAL:
- if(copy_to_user((char *)arg, (char *)&ts->cal, sizeof(TS_CAL)))
- return -EFAULT;
- break;
- case TS_SET_CAL:
- if(copy_from_user((char *)&ts->cal, (char *)arg, sizeof(TS_CAL)))
- return -EFAULT;
- break;
- case TS_GET_COMP1:
- if (adc_get(ts, &adc_value, TS_COMP1)) {
- if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
- return -EFAULT;
- }
- else
- return -EIO;
- break;
- case TS_GET_COMP2:
- if (adc_get(ts, &adc_value, TS_COMP2)) {
- if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
- return -EFAULT;
- }
- else
- return -EIO;
- break;
- case TS_GET_BMON:
- if (adc_get(ts, &adc_value, TS_BMON)) {
- if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
- return -EFAULT;
- }
- else
+ if (pil && !five_wire) {
+ if (!wm97xx_poll_read_adc(ts, WM97XX_ADCSEL_PRES, &p)) {
+ info("p %d\n", p);
return -EIO;
- break;
- case TS_GET_WIPER:
- if (adc_get(ts, &adc_value, TS_WIPER)) {
- if(copy_to_user((char *)arg, (char *)&adc_value, sizeof(adc_value)))
- return -EFAULT;
}
- else
- return -EIO;
- break;
-#ifdef WM97XX_TS_DEBUG
- /* debug get/set ac97 codec register ioctl's
- *
- * This is direct IO to the codec registers - BE CAREFULL
- */
- case TS_GET_AC97_REG: /* read from ac97 reg (index) */
- data = ts->codec->codec_read(ts->codec, ts->ac97_index);
- if(copy_to_user((char *)arg, (char *)&data, sizeof(data)))
- return -EFAULT;
- break;
- case TS_SET_AC97_REG: /* write to ac97 reg (index) */
- if(copy_from_user((char *)&data, (char *)arg, sizeof(data)))
- return -EFAULT;
- ts->codec->codec_write(ts->codec, ts->ac97_index, data);
- break;
- case TS_SET_AC97_INDEX: /* set ac97 reg index */
- if(copy_from_user((char *)&ts->ac97_index, (char *)arg, sizeof(ts->ac97_index)))
- return -EFAULT;
- break;
-#endif
- default:
- return -EINVAL;
+ } else {
+ p = 0xffff;
}
-
- return 0;
-}
-
-static unsigned int wm97xx_poll(struct file * filp, poll_table * wait)
-{
- wm97xx_ts_t *ts = (wm97xx_ts_t *)filp->private_data;
- poll_wait(filp, &ts->wait, wait);
- if (ts->event_count)
- return POLLIN | POLLRDNORM;
- return 0;
+
+ wm97xx_ts_evt_add(ts, p, x, y);
+ return 1;
}

-static ssize_t wm97xx_read(struct file *filp, char *buf, size_t count, loff_t *l)
+/*
+ * Sample the touchscreen in polling coordinate mode
+ */
+int wm97xx_poll_coord_touch(wm97xx_ts_t *ts)
{
- wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
- unsigned long flags;
- TS_EVENT event;
- int i;
-
- /* are we still registered with AC97 layer ? */
- spin_lock_irqsave(&ts->lock, flags);
- if (!ts->is_registered) {
- spin_unlock_irqrestore(&ts->lock, flags);
- return -ENXIO;
- }
-
- if (ts->event_count == 0) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- spin_unlock_irqrestore(&ts->lock, flags);
-
- wait_event_interruptible(ts->wait, ts->event_count != 0);
-
- /* are we still registered after sleep ? */
- spin_lock_irqsave(&ts->lock, flags);
- if (!ts->is_registered) {
- spin_unlock_irqrestore(&ts->lock, flags);
- return -ENXIO;
- }
- if (signal_pending(current))
- return -ERESTARTSYS;
- }
+ u16 x, y, p;

- for (i = count; i >= sizeof(TS_EVENT);
- i -= sizeof(TS_EVENT), buf += sizeof(TS_EVENT)) {
- if (ts->event_count == 0)
- break;
- spin_lock_irqsave(&ts->lock, flags);
- event = ts->event_buf[ts->nextOut++];
- if (ts->nextOut == EVENT_BUFSIZE)
- ts->nextOut = 0;
- if (ts->event_count)
- ts->event_count--;
- spin_unlock_irqrestore(&ts->lock, flags);
- if(copy_to_user(buf, &event, sizeof(TS_EVENT)))
- return i != count ? count - i : -EFAULT;
- }
- return count - i;
+ if (wm97xx_coord_read_adc(ts, &x, &y, &p)) {
+ wm97xx_ts_evt_add(ts, p, x, y);
+ return 1;
+ } else
+ return -EIO;
}

-
-static int wm97xx_open(struct inode * inode, struct file * filp)
+/*
+ * Sample the touchscreen in continous mode
+ */
+int wm97xx_cont_touch(wm97xx_ts_t *ts)
{
- wm97xx_ts_t* ts;
- unsigned long flags;
- u16 val;
- int minor = MINOR(inode->i_rdev);
+ u16 x, y;
+ int count = 0;

- if (minor != TS_MINOR)
- return -ENODEV;
-
- filp->private_data = ts = &wm97xx_ts;
-
- spin_lock_irqsave(&ts->lock, flags);
-
- /* are we registered with AC97 layer ? */
- if (!ts->is_registered) {
- spin_unlock_irqrestore(&ts->lock, flags);
- return -ENXIO;
+ /* currently assuming x is followed by y from the FIFO */
+ while (WM97XX_FIFO_HAS_DATA) {
+ x = WM97XX_READ_FIFO;
+ y = WM97XX_READ_FIFO;
+ wm97xx_ts_evt_add(ts, 0, x, y);
+ count++;
}
-
- /* start digitiser */
- val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
- ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
- val | WM97XX_PRP_DET_DIG);
-
- /* flush event queue */
- ts->nextIn = ts->nextOut = ts->event_count = 0;
-
- /* Set up timer. */
- init_timer(&ts->acq_timer);
- ts->acq_timer.function = wm97xx_acq_timer;
- ts->acq_timer.data = (unsigned long)ts;
- ts->acq_timer.expires = jiffies + HZ / 100;
- add_timer(&ts->acq_timer);
-
- spin_unlock_irqrestore(&ts->lock, flags);
- return 0;
+ return count;
}

-static int wm97xx_release(struct inode * inode, struct file * filp)
+/*
+ * Is the pen down ?
+ */
+static inline int pendown (wm97xx_ts_t *ts)
{
- wm97xx_ts_t* ts = (wm97xx_ts_t*)filp->private_data;
- unsigned long flags;
- u16 val;
-
- wm97xx_fasync(-1, filp, 0);
- del_timer_sync(&ts->acq_timer);
-
- spin_lock_irqsave(&ts->lock, flags);
-
- /* stop digitiser */
- val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
- ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
- val & ~WM97XX_PRP_DET_DIG);
-
- spin_unlock_irqrestore(&ts->lock, flags);
- return 0;
+ if (pen_int) {
+
+ } else {
+ return ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER_RD)
+ & WM97XX_PEN_DOWN;
+ }
}

-static struct file_operations ts_fops = {
- owner: THIS_MODULE,
- read: wm97xx_read,
- poll: wm97xx_poll,
- ioctl: wm97xx_ioctl,
- fasync: wm97xx_fasync,
- open: wm97xx_open,
- release: wm97xx_release,
-};
-
-/* +++++++++++++ End File operations ++++++++++++++*/
-
-#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_PROC_FS)
static int wm97xx_read_proc (char *page, char **start, off_t off,
- int count, int *eof, void *data)
+ int count, int *eof, void *data)
{
int len = 0, prpu;
u16 dig1, dig2, digrd, adcsel, adcsrc, slt, prp, rev;
unsigned long flags;
char srev = ' ';
-
wm97xx_ts_t* ts;

if ((ts = data) == NULL)
return -ENODEV;
-
+
spin_lock_irqsave(&ts->lock, flags);
if (!ts->is_registered) {
spin_unlock_irqrestore(&ts->lock, flags);
@@ -828,38 +523,35 @@
rev = (ts->codec->codec_read(ts->codec, AC97_WM9712_REV) & 0x000c) >> 2;

spin_unlock_irqrestore(&ts->lock, flags);
-
+
adcsel = dig1 & 0x7000;
adcsrc = digrd & 0x7000;
slt = (dig1 & 0x7) + 5;
prp = dig2 & 0xc000;
prpu = dig2 & 0x003f;

- /* driver version */
len += sprintf (page+len, "Wolfson WM97xx Version %s\n", WM_TS_VERSION);
-
- /* what we are using */
+
len += sprintf (page+len, "Using %s", ts->is_wm9712 ? "WM9712" : "WM9705");
if (ts->is_wm9712) {
switch (rev) {
- case 0x0:
- srev = 'A';
+ case 0x0:
+ srev = 'A';
break;
- case 0x1:
- srev = 'B';
+ case 0x1:
+ srev = 'B';
break;
- case 0x2:
- srev = 'D';
+ case 0x2:
+ srev = 'D';
break;
- case 0x3:
- srev = 'E';
+ case 0x3:
+ srev = 'E';
break;
}
len += sprintf (page+len, " silicon rev %c\n",srev);
} else
len += sprintf (page+len, "\n");
-
- /* WM97xx settings */
+
len += sprintf (page+len, "Settings :\n%s%s%s%s",
dig1 & WM97XX_POLL ? " -sampling adc data(poll)\n" : "",
adcsel == WM97XX_ADCSEL_X ? " -adc set to X coordinate\n" : "",
@@ -867,18 +559,18 @@
adcsel == WM97XX_ADCSEL_PRES ? " -adc set to pressure\n" : "");
if (ts->is_wm9712) {
len += sprintf (page+len, "%s%s%s%s",
- adcsel == WM9712_ADCSEL_COMP1 ? " -adc set to COMP1/AUX1\n" : "",
- adcsel == WM9712_ADCSEL_COMP2 ? " -adc set to COMP2/AUX2\n" : "",
- adcsel == WM9712_ADCSEL_BMON ? " -adc set to BMON\n" : "",
- adcsel == WM9712_ADCSEL_WIPER ? " -adc set to WIPER\n" : "");
- } else {
+ adcsel == WM9712_ADCSEL_COMP1 ? " -adc set to COMP1/AUX1\n" : "",
+ adcsel == WM9712_ADCSEL_COMP2 ? " -adc set to COMP2/AUX2\n" : "",
+ adcsel == WM9712_ADCSEL_BMON ? " -adc set to BMON\n" : "",
+ adcsel == WM9712_ADCSEL_WIPER ? " -adc set to WIPER\n" : "");
+ } else {
len += sprintf (page+len, "%s%s%s%s",
- adcsel == WM9705_ADCSEL_PCBEEP ? " -adc set to PCBEEP\n" : "",
- adcsel == WM9705_ADCSEL_PHONE ? " -adc set to PHONE\n" : "",
- adcsel == WM9705_ADCSEL_BMON ? " -adc set to BMON\n" : "",
- adcsel == WM9705_ADCSEL_AUX ? " -adc set to AUX\n" : "");
- }
-
+ adcsel == WM9705_ADCSEL_PCBEEP ? " -adc set to PCBEEP\n" : "",
+ adcsel == WM9705_ADCSEL_PHONE ? " -adc set to PHONE\n" : "",
+ adcsel == WM9705_ADCSEL_BMON ? " -adc set to BMON\n" : "",
+ adcsel == WM9705_ADCSEL_AUX ? " -adc set to AUX\n" : "");
+ }
+
len += sprintf (page+len, "%s%s%s%s%s%s",
dig1 & WM97XX_COO ? " -coordinate sampling\n" : " -individual sampling\n",
dig1 & WM97XX_CTC ? " -continuous mode\n" : " -polling mode\n",
@@ -886,11 +578,11 @@
prp == WM97XX_PRP_DETW ? " -pen detect enabled, wake up\n" : "",
prp == WM97XX_PRP_DET_DIG ? " -pen digitiser and pen detect enabled\n" : "",
dig1 & WM97XX_SLEN ? " -read back using slot " : " -read back using AC97\n");
-
- if ((dig1 & WM97XX_SLEN) && slt !=12)
+
+ if ((dig1 & WM97XX_SLEN) && slt !=12)
len += sprintf(page+len, "%d\n", slt);
len += sprintf (page+len, " -adc sample delay %d uSecs\n", delay_table[(dig1 & 0x00f0) >> 4]);
-
+
if (ts->is_wm9712) {
if (prpu)
len += sprintf (page+len, " -rpu %d Ohms\n", 64000/ prpu);
@@ -900,75 +592,38 @@
len += sprintf (page+len, " -pressure current %s uA\n", dig2 & WM9705_PIL ? "400" : "200");
len += sprintf (page+len, " -%s impedance for PHONE and PCBEEP\n", dig2 & WM9705_PHIZ ? "high" : "low");
}
-
- /* WM97xx digitiser read */
+
len += sprintf(page+len, "\nADC data:\n%s%d\n%s%s\n",
- " -adc value (decimal) : ", digrd & 0x0fff,
- " -pen ", digrd & 0x8000 ? "Down" : "Up");
+ " -adc value (decimal) : ", digrd & 0x0fff,
+ " -pen ", digrd & 0x8000 ? "Down" : "Up");
if (ts->is_wm9712) {
len += sprintf (page+len, "%s%s%s%s",
- adcsrc == WM9712_ADCSEL_COMP1 ? " -adc value is COMP1/AUX1\n" : "",
- adcsrc == WM9712_ADCSEL_COMP2 ? " -adc value is COMP2/AUX2\n" : "",
- adcsrc == WM9712_ADCSEL_BMON ? " -adc value is BMON\n" : "",
- adcsrc == WM9712_ADCSEL_WIPER ? " -adc value is WIPER\n" : "");
- } else {
+ adcsrc == WM9712_ADCSEL_COMP1 ? " -adc value is COMP1/AUX1\n" : "",
+ adcsrc == WM9712_ADCSEL_COMP2 ? " -adc value is COMP2/AUX2\n" : "",
+ adcsrc == WM9712_ADCSEL_BMON ? " -adc value is BMON\n" : "",
+ adcsrc == WM9712_ADCSEL_WIPER ? " -adc value is WIPER\n" : "");
+ } else {
len += sprintf (page+len, "%s%s%s%s",
- adcsrc == WM9705_ADCSEL_PCBEEP ? " -adc value is PCBEEP\n" : "",
- adcsrc == WM9705_ADCSEL_PHONE ? " -adc value is PHONE\n" : "",
- adcsrc == WM9705_ADCSEL_BMON ? " -adc value is BMON\n" : "",
- adcsrc == WM9705_ADCSEL_AUX ? " -adc value is AUX\n" : "");
- }
-
- /* register dump */
+ adcsrc == WM9705_ADCSEL_PCBEEP ? " -adc value is PCBEEP\n" : "",
+ adcsrc == WM9705_ADCSEL_PHONE ? " -adc value is PHONE\n" : "",
+ adcsrc == WM9705_ADCSEL_BMON ? " -adc value is BMON\n" : "",
+ adcsrc == WM9705_ADCSEL_AUX ? " -adc value is AUX\n" : "");
+ }
+
len += sprintf(page+len, "\nRegisters:\n%s%x\n%s%x\n%s%x\n",
- " -digitiser 1 (0x76) : 0x", dig1,
- " -digitiser 2 (0x78) : 0x", dig2,
- " -digitiser read (0x7a) : 0x", digrd);
-
- /* errors */
- len += sprintf(page+len, "\nErrors:\n%s%d\n%s%d\n",
- " -buffer overruns ", ts->overruns,
- " -coordinate errors ", ts->adc_errs);
-
- return len;
-}
+ " -digitiser 1 (0x76) : 0x", dig1,
+ " -digitiser 2 (0x78) : 0x", dig2,
+ " -digitiser read (0x7a) : 0x", digrd);
+
+ len += sprintf(page+len, "\nErrors:\n%s%d\n",
+ " -coordinate errors ", ts->adc_errs);

-#ifdef WM97XX_TS_DEBUG
-/* dump all the AC97 register space */
-static int wm_debug_read_proc (char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- int len = 0, i;
- unsigned long flags;
- wm97xx_ts_t* ts;
- u16 reg[AC97_NUM_REG];
-
- if ((ts = data) == NULL)
- return -ENODEV;
-
- spin_lock_irqsave(&ts->lock, flags);
- if (!ts->is_registered) {
- spin_unlock_irqrestore(&ts->lock, flags);
- len += sprintf (page+len, "Not registered\n");
- return len;
- }
-
- for (i=0; i < AC97_NUM_REG; i++) {
- reg[i] = ts->codec->codec_read(ts->codec, i * 2);
- }
- spin_unlock_irqrestore(&ts->lock, flags);
-
- for (i=0; i < AC97_NUM_REG; i++) {
- len += sprintf (page+len, "0x%2.2x : 0x%4.4x\n",i * 2, reg[i]);
- }
-
return len;
}
-#endif

#endif

-#ifdef CONFIG_PM
+#if defined(CONFIG_PM)
/* WM97xx Power Management
* The WM9712 has extra powerdown states that are controlled in
* seperate registers from the AC97 power management.
@@ -996,40 +651,39 @@
wm97xx_ts_t* ts = &wm97xx_ts;
u16 reg;
unsigned long flags;
-
- /* are we registered */
+
spin_lock_irqsave(&ts->lock, flags);
if (!ts->is_registered) {
spin_unlock_irqrestore(&ts->lock, flags);
return;
}
-
+
/* wm9705 does not have extra PM */
if (!ts->is_wm9712) {
spin_unlock_irqrestore(&ts->lock, flags);
return;
}
-
+
/* save and mute the PGA's */
wm9712_pga_save(ts);
-
+
reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | 0x001f);
-
+
reg = ts->codec->codec_read(ts->codec, AC97_MIC_VOL);
ts->codec->codec_write(ts->codec, AC97_MIC_VOL, reg | 0x1f1f);
-
+
reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | 0x1f1f);
-
+
/* power down, dont disable the AC link */
- ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, WM9712_PD(14) | WM9712_PD(13) |
- WM9712_PD(12) | WM9712_PD(11) | WM9712_PD(10) |
- WM9712_PD(9) | WM9712_PD(8) | WM9712_PD(7) |
- WM9712_PD(6) | WM9712_PD(5) | WM9712_PD(4) |
- WM9712_PD(3) | WM9712_PD(2) | WM9712_PD(1) |
- WM9712_PD(0));
-
+ ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, WM9712_PD(14) |
+ WM9712_PD(13) | WM9712_PD(12) | WM9712_PD(11) |
+ WM9712_PD(10) | WM9712_PD(9) | WM9712_PD(8) |
+ WM9712_PD(7) | WM9712_PD(6) | WM9712_PD(5) |
+ WM9712_PD(4) | WM9712_PD(3) | WM9712_PD(2) |
+ WM9712_PD(1) | WM9712_PD(0));
+
spin_unlock_irqrestore(&ts->lock, flags);
}

@@ -1040,14 +694,14 @@
{
wm97xx_ts_t* ts = &wm97xx_ts;
unsigned long flags;
-
+
/* are we registered */
spin_lock_irqsave(&ts->lock, flags);
if (!ts->is_registered) {
spin_unlock_irqrestore(&ts->lock, flags);
return;
}
-
+
/* wm9705 does not have extra PM */
if (!ts->is_wm9712) {
spin_unlock_irqrestore(&ts->lock, flags);
@@ -1056,14 +710,13 @@

/* power up */
ts->codec->codec_write(ts->codec, AC97_WM9712_POWER, 0x0);
-
+
/* restore PGA state */
wm9712_pga_restore(ts);
-
+
spin_unlock_irqrestore(&ts->lock, flags);
}

-
/* save state of wm9712 PGA's */
static void wm9712_pga_save(wm97xx_ts_t* ts)
{
@@ -1078,10 +731,10 @@
static void wm9712_pga_restore(wm97xx_ts_t* ts)
{
u16 reg;
-
+
reg = ts->codec->codec_read(ts->codec, AC97_PHONE_VOL);
ts->codec->codec_write(ts->codec, AC97_PHONE_VOL, reg | ts->phone_pga);
-
+
reg = ts->codec->codec_read(ts->codec, AC97_LINEIN_VOL);
ts->codec->codec_write(ts->codec, AC97_LINEIN_VOL, reg | ts->line_pgar | (ts->line_pgal << 8));

@@ -1094,19 +747,18 @@
/*
* set up the physical settings of the device
*/
-
static void init_wm97xx_phy(void)
{
u16 dig1, dig2, aux, vid;
wm97xx_ts_t *ts = &wm97xx_ts;

/* default values */
- dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6);
+ dig1 = WM97XX_DELAY(4) | WM97XX_SLT(5);
if (ts->is_wm9712)
dig2 = WM9712_RPU(1);
else {
dig2 = 0x0;
-
+
/*
* mute VIDEO and AUX as they share X and Y touchscreen
* inputs on the WM9705
@@ -1116,21 +768,21 @@
info("muting AUX mixer as it shares X touchscreen coordinate");
ts->codec->codec_write(ts->codec, AC97_AUX_VOL, 0x8000 | aux);
}
-
+
vid = ts->codec->codec_read(ts->codec, AC97_VIDEO_VOL);
if (!(vid & 0x8000)) {
info("muting VIDEO mixer as it shares Y touchscreen coordinate");
ts->codec->codec_write(ts->codec, AC97_VIDEO_VOL, 0x8000 | vid);
}
}
-
+
/* WM9712 rpu */
if (ts->is_wm9712 && rpu) {
dig2 &= 0xffc0;
dig2 |= WM9712_RPU(rpu);
info("setting pen detect pull-up to %d Ohms",64000 / rpu);
}
-
+
/* touchpanel pressure */
if (pil == 2) {
if (ts->is_wm9712)
@@ -1140,13 +792,13 @@
info("setting pressure measurement current to 400uA.");
} else if (pil)
info ("setting pressure measurement current to 200uA.");
-
+
/* WM9712 five wire */
if (ts->is_wm9712 && five_wire) {
dig2 |= WM9712_45W;
info("setting 5-wire touchscreen mode.");
- }
-
+ }
+
/* sample settling delay */
if (delay!=4) {
if (delay < 0 || delay > 15) {
@@ -1157,19 +809,29 @@
dig1 |= WM97XX_DELAY(delay);
info("setting adc sample delay to %d u Secs.", delay_table[delay]);
}
-
+
/* coordinate mode */
if (mode == 1) {
dig1 |= WM97XX_COO;
info("using coordinate mode");
- }
+ }

+ /* continous mode */
+ if (mode == 2) {
+ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_CM_RATE_375 | WM97XX_SLEN | WM97XX_SLT(5);
+ info("using continous mode");
+ if (ts->is_wm9712)
+ dig2 |= WM9712_PDEN;
+ else
+ dig2 |= WM9705_PDEN;
+ }
+
/* WM9705 pdd */
if (pdd && !ts->is_wm9712) {
dig2 |= (pdd & 0x000f);
info("setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
}
-
+
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER1, dig1);
ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2, dig2);
}
@@ -1182,27 +844,27 @@

static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver)
{
- unsigned long flags;
+ unsigned long flags;
u16 id1, id2;
wm97xx_ts_t *ts = &wm97xx_ts;
-
+
spin_lock_irqsave(&ts->lock, flags);
-
+
/* we only support 1 touchscreen at the moment */
if (ts->is_registered) {
spin_unlock_irqrestore(&ts->lock, flags);
return -1;
}
-
+
/*
* We can only use a WM9705 or WM9712 that has been *first* initialised
* by the AC97 audio driver. This is because we have to use the audio
- * drivers codec read() and write() functions to sample the touchscreen
- *
+ * drivers codec read() and write() functions to sample the touchscreen
+ *
* If an initialsed WM97xx is found then get the codec read and write
- * functions.
+ * functions.
*/
-
+
/* test for a WM9712 or a WM9705 */
id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
@@ -1210,131 +872,276 @@
ts->is_wm9712 = 1;
info("registered a WM9712");
} else if (id1 == WM97XX_ID1 && id2 == WM9705_ID2) {
- ts->is_wm9712 = 0;
- info("registered a WM9705");
+ ts->is_wm9712 = 0;
+ info("registered a WM9705");
} else {
err("could not find a WM97xx codec. Found a 0x%4x:0x%4x instead",
id1, id2);
spin_unlock_irqrestore(&ts->lock, flags);
return -1;
}
-
+
/* set up AC97 codec interface */
ts->codec = codec;
codec->driver_private = (void*)&ts;
- codec->codec_unregister = 0;
-
+
/* set up physical characteristics */
init_wm97xx_phy();
-
+
ts->is_registered = 1;
spin_unlock_irqrestore(&ts->lock, flags);
return 0;
}

-/* this is called by the audio driver when ac97_codec is unloaded */
-
+/*
+ * Called by ac97_codec when it is unloaded.
+ */
static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver)
{
unsigned long flags;
u16 dig1, dig2;
wm97xx_ts_t *ts = codec->driver_private;

+ dbg("Unloading codec\n");
spin_lock_irqsave(&ts->lock, flags);
-
+
/* check that are registered */
if (!ts->is_registered) {
err("double unregister");
spin_unlock_irqrestore(&ts->lock, flags);
return;
}
-
+
ts->is_registered = 0;
wake_up_interruptible(&ts->wait); /* So we see its gone */
-
+
/* restore default digitiser values */
dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6);
if (ts->is_wm9712)
dig2 = WM9712_RPU(1);
else
dig2 = 0x0;
-
+
codec->codec_write(codec, AC97_WM97XX_DIGITISER1, dig1);
codec->codec_write(codec, AC97_WM97XX_DIGITISER2, dig2);
ts->codec = NULL;
-
+
spin_unlock_irqrestore(&ts->lock, flags);
}

-static struct miscdevice wm97xx_misc = {
- minor: TS_MINOR,
- name: "touchscreen/wm97xx",
- fops: &ts_fops,
-};
+/*
+ * add a touch event
+ */
+static int wm97xx_ts_evt_add(wm97xx_ts_t* ts, u16 pressure, u16 x, u16 y)
+{
+ /* add event and remove adc src bits */
+ input_report_abs(ts->idev, ABS_X, x & 0xfff);
+ input_report_abs(ts->idev, ABS_Y, y & 0xfff);
+ input_report_abs(ts->idev, ABS_PRESSURE, pressure & 0xfff);
+ return 0;
+}
+
+/*
+ * add a pen up event
+ */
+static int wm97xx_ts_evt_release(wm97xx_ts_t* ts)
+{
+// dbg("release the stylus.\n");
+ input_report_abs(ts->idev, ABS_PRESSURE, 0);
+ return 0;
+}
+
+/*
+ * The touchscreen sample reader thread
+ */
+static int wm97xx_thread(void *_ts)
+{
+ wm97xx_ts_t *ts = (wm97xx_ts_t *)_ts;
+ struct task_struct *tsk = current;
+ int valid;
+
+ ts->rtask = tsk;
+
+ /* set up thread context */
+ daemonize();
+ reparent_to_init();
+ strcpy(tsk->comm, "ktsd");
+ tsk->tty = NULL;
+
+ /* we will die when we receive SIGKILL */
+ spin_lock_irq(&tsk->sigmask_lock);
+ siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
+ recalc_sigpending(tsk);
+ spin_unlock_irq(&tsk->sigmask_lock);
+
+ /* init is complete */
+ complete(&ts->init_exit);
+ valid = 0;
+
+ /* touch reader loop */
+ for (;;) {
+ ts->restart = 0;
+ if( signal_pending(tsk))
+ break;
+
+ if(pendown(ts)) {
+ switch (mode) {
+ case 0:
+ wm97xx_poll_touch(ts);
+ valid = 1;
+ break;
+ case 1:
+ wm97xx_poll_coord_touch(ts);
+ valid = 1;
+ break;
+ case 2:
+ wm97xx_cont_touch(ts);
+ valid = 1;
+ break;
+ }
+ } else {
+ if (valid) {
+ valid = 0;
+ wm97xx_ts_evt_release(ts);
+ }
+ }
+
+ set_task_state(tsk, TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/100);
+ }
+ ts->rtask = NULL;
+ complete_and_exit(&ts->init_exit, 0);
+
+ return 0;
+}
+
+/*
+ * Start the touchscreen thread and
+ * the touch digitiser.
+ */
+static int wm97xx_ts_input_open(struct input_dev *idev)
+{
+ wm97xx_ts_t *ts = (wm97xx_ts_t *) &wm97xx_ts;
+ u32 flags;
+ int ret, val;
+
+ if ( ts->use_count++ != 0 )
+ return 0;
+
+ /* start touchscreen thread */
+ ts->idev = idev;
+ init_completion(&ts->init_exit);
+ ret = kernel_thread(wm97xx_thread, ts, 0);
+ if (ret >= 0)
+ wait_for_completion(&ts->init_exit);
+
+ spin_lock_irqsave( &ts->lock, flags );
+
+ /* start digitiser */
+ val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
+ ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
+ val | WM97XX_PRP_DET_DIG);
+
+ spin_unlock_irqrestore( &ts->lock, flags );
+
+ return 0;
+}
+
+/*
+ * Kill the touchscreen thread and stop
+ * the touch digitiser.
+ */
+static void wm97xx_ts_input_close(struct input_dev *idev)
+{
+ wm97xx_ts_t *ts = (wm97xx_ts_t *) &wm97xx_ts;
+ u32 flags;
+ int val;
+
+ if (--ts->use_count == 0) {
+ /* kill thread */
+ if (ts->rtask) {
+ send_sig(SIGKILL, ts->rtask, 1);
+ wait_for_completion(&ts->init_exit);
+ }
+
+ down(&ts->sem);
+ spin_lock_irqsave(&ts->lock, flags);
+
+ /* stop digitiser */
+ val = ts->codec->codec_read(ts->codec, AC97_WM97XX_DIGITISER2);
+ ts->codec->codec_write(ts->codec, AC97_WM97XX_DIGITISER2,
+ val & ~WM97XX_PRP_DET_DIG);
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+ up(&ts->sem);
+ }
+}

static int __init wm97xx_ts_init_module(void)
{
wm97xx_ts_t* ts = &wm97xx_ts;
int ret;
char proc_str[64];
-
+
info("Wolfson WM9705/WM9712 Touchscreen Controller");
info("Version %s liam.girdwood@xxxxxxxxxxxxxxxx", WM_TS_VERSION);
-
+
memset(ts, 0, sizeof(wm97xx_ts_t));
-
- /* register our misc device */
- if ((ret = misc_register(&wm97xx_misc)) < 0) {
- err("can't register misc device");
- return ret;
- }
-
+
+ /* tell input system what we events we accept and register */
+ wm97xx_input.name = "wm97xx touchscreen";
+ wm97xx_input.open = wm97xx_ts_input_open;
+ wm97xx_input.close = wm97xx_ts_input_close;
+ __set_bit(EV_ABS, wm97xx_input.evbit);
+ __set_bit(ABS_X, wm97xx_input.absbit);
+ __set_bit(ABS_Y, wm97xx_input.absbit);
+ __set_bit(ABS_PRESSURE, wm97xx_input.absbit);
+ input_register_device(&wm97xx_input);
+
init_waitqueue_head(&ts->wait);
spin_lock_init(&ts->lock);
+ init_MUTEX(&ts->sem);
+

- // initial calibration values
- ts->cal.xscale = 256;
- ts->cal.xtrans = 0;
- ts->cal.yscale = 256;
- ts->cal.ytrans = 0;
-
- /* reset error counters */
- ts->overruns = 0;
- ts->adc_errs = 0;
+ /* register pendown interrupt handler */
+ if (pen_int) {
+#if defined(WM97XX_IRQ)
+ if ((ret = request_irq(WM97XX_IRQ, wm97xx_interrupt, 0, "AC97-touchscreen", &ts)) != 0) {
+ err("can't get irq %d falling back to pendown polling\n", WM97XX_IRQ);
+ pen_int = 0;
+ }
+#else
+ err("touchscreen interrupt not supported/implemented on this arch\n");
+ pen_int = 0;
+#endif
+ }

/* register with the AC97 layer */
ac97_register_driver(&wm9705_driver);
ac97_register_driver(&wm9712_driver);
-
-#ifdef CONFIG_PROC_FS
- /* register proc interface */
+
+#if defined(CONFIG_PROC_FS)
sprintf(proc_str, "driver/%s", TS_NAME);
if ((ts->wm97xx_ts_ps = create_proc_read_entry (proc_str, 0, NULL,
- wm97xx_read_proc, ts)) == 0)
+ wm97xx_read_proc, ts)) == 0)
err("could not register proc interface /proc/%s", proc_str);
-#ifdef WM97XX_TS_DEBUG
- if ((ts->wm97xx_debug_ts_ps = create_proc_read_entry ("driver/ac97_registers",
- 0, NULL,wm_debug_read_proc, ts)) == 0)
- err("could not register proc interface /proc/driver/ac97_registers");
-#endif
#endif
-#ifdef CONFIG_PM
+#if defined(CONFIG_PM)
if ((ts->pm = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, wm97xx_pm_event)) == 0)
err("could not register with power management");
#endif
+
return 0;
}

static void wm97xx_ts_cleanup_module(void)
{
- wm97xx_ts_t* ts = &wm97xx_ts;
-
-#ifdef CONFIG_PM
- pm_unregister (ts->pm);
+#if defined(CONFIG_PM)
+ pm_unregister (wm97xx_ts.pm);
#endif
- ac97_unregister_driver(&wm9705_driver);
- ac97_unregister_driver(&wm9712_driver);
- misc_deregister(&wm97xx_misc);
+ if (pen_int)
+ free_irq(WM97XX_IRQ, NULL);
+ input_unregister_device(&wm97xx_input);
}

/* Module information */
@@ -1355,8 +1162,8 @@
return 0;

/* parse the options and check for out of range values */
- for(this_opt=strtok(options, ",");
- this_opt; this_opt=strtok(NULL, ",")) {
+ for(this_opt=strsep(options, ",");
+ this_opt; this_opt=strsep(NULL, ",")) {
if (!strncmp(this_opt, "pil:", 4)) {
this_opt+=4;
pil = simple_strtol(this_opt, NULL, 0);
@@ -1399,6 +1206,13 @@
mode = 0;
continue;
}
+ if (!strncmp(this_opt, "pen_int:", 8)) {
+ this_opt+=8;
+ pen_int = simple_strtol(this_opt, NULL, 0);
+ if (pen_int < 0 || pen_int > 1)
+ pen_int = 0;
+ continue;
+ }
}
return 1;
}