2008/8/30 Felipe Balbi <[EMAIL PROTECTED]>:
> From: Felipe Balbi <[EMAIL PROTECTED]>
>
> Signed-off-by: Felipe Balbi <[EMAIL PROTECTED]>
> ---
> drivers/input/touchscreen/Kconfig | 5 +
> drivers/input/touchscreen/Makefile | 1 +
> drivers/input/touchscreen/tsc2005.c | 736
> +++++++++++++++++++++++++++++++++++
> include/linux/spi/tsc2005.h | 29 ++
> 4 files changed, 771 insertions(+), 0 deletions(-)
> create mode 100644 drivers/input/touchscreen/tsc2005.c
> create mode 100644 include/linux/spi/tsc2005.h
>
> diff --git a/drivers/input/touchscreen/Kconfig
> b/drivers/input/touchscreen/Kconfig
> index 25287e8..a115f38 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -217,6 +217,11 @@ config TOUCHSCREEN_ATMEL_TSADCC
> To compile this driver as a module, choose M here: the
> module will be called atmel_tsadcc.
>
> +config TOUCHSCREEN_TSC2005
> + tristate "TSC2005 touchscreen support"
> + help
> + Say Y here for if you are using the touchscreen features of TSC2301.
> +
> config TOUCHSCREEN_UCB1400
> tristate "Philips UCB1400 touchscreen"
> select AC97_BUS
> diff --git a/drivers/input/touchscreen/Makefile
> b/drivers/input/touchscreen/Makefile
> index 15cf290..0342389 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -26,6 +26,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
> obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
> obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
> obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
> obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
> wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
> wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
> diff --git a/drivers/input/touchscreen/tsc2005.c
> b/drivers/input/touchscreen/tsc2005.c
> new file mode 100644
> index 0000000..7fb107e
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2005.c
> @@ -0,0 +1,736 @@
> +/*
> + * TSC2005 touchscreen driver
> + *
> + * Copyright (C) 2006-2008 Nokia Corporation
> + *
> + * Author: Lauri Leukkunen <[EMAIL PROTECTED]>
> + * based on TSC2301 driver by Klaus K. Pedersen <[EMAIL PROTECTED]>
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/spi/spi.h>
> +
> +#ifdef CONFIG_ARCH_OMAP
> +#include <mach/gpio.h>
> +#endif
> +
> +#include <linux/spi/tsc2005.h>
> +
> +/**
> + * The touchscreen interface operates as follows:
> + *
> + * Initialize:
> + * Request access to GPIO103 (DAV)
> + * tsc2005_dav_irq_handler will trigger when DAV line goes down
> + *
> + * 1) Pen is pressed against touchscreeen
> + * 2) TSC2005 performs AD conversion
> + * 3) After the conversion is done TSC2005 drives DAV line down
> + * 4) GPIO IRQ is received and tsc2005_dav_irq_handler is called
> + * 5) tsc2005_ts_irq_handler queues up an spi transfer to fetch
> + * the x, y, z1, z2 values
> + * 6) tsc2005_ts_rx() reports coordinates to input layer and
> + * sets up tsc2005_ts_timer() to be called after TSC2005_TS_SCAN_TIME
> + * 7) When the penup_timer expires, there have not been DAV interrupts
> + * during the last 20ms which means the pen has been lifted.
> + */
> +
> +#define TSC2005_VDD_LOWER_27
> +
> +#ifdef TSC2005_VDD_LOWER_27
> +#define TSC2005_HZ (10000000)
> +#else
> +#define TSC2005_HZ (25000000)
> +#endif
> +
> +#define TSC2005_CMD (0x80)
> +#define TSC2005_REG (0x00)
> +
> +#define TSC2005_CMD_STOP (1)
> +#define TSC2005_CMD_10BIT (0 << 2)
> +#define TSC2005_CMD_12BIT (1 << 2)
> +
> +#define TSC2005_CMD_SCAN_XYZZ (0 << 3)
> +#define TSC2005_CMD_SCAN_XY (1 << 3)
> +#define TSC2005_CMD_SCAN_X (2 << 3)
> +#define TSC2005_CMD_SCAN_Y (3 << 3)
> +#define TSC2005_CMD_SCAN_ZZ (4 << 3)
> +#define TSC2005_CMD_AUX_SINGLE (5 << 3)
> +#define TSC2005_CMD_TEMP1 (6 << 3)
> +#define TSC2005_CMD_TEMP2 (7 << 3)
> +#define TSC2005_CMD_AUX_CONT (8 << 3)
> +#define TSC2005_CMD_TEST_X_CONN (9 << 3)
> +#define TSC2005_CMD_TEST_Y_CONN (10 << 3)
> +/* command 11 reserved */
> +#define TSC2005_CMD_TEST_SHORT (12 << 3)
> +#define TSC2005_CMD_DRIVE_XX (13 << 3)
> +#define TSC2005_CMD_DRIVE_YY (14 << 3)
> +#define TSC2005_CMD_DRIVE_YX (15 << 3)
> +
> +#define TSC2005_REG_X (0 << 3)
> +#define TSC2005_REG_Y (1 << 3)
> +#define TSC2005_REG_Z1 (2 << 3)
> +#define TSC2005_REG_Z2 (3 << 3)
> +#define TSC2005_REG_AUX (4 << 3)
> +#define TSC2005_REG_TEMP1 (5 << 3)
> +#define TSC2005_REG_TEMP2 (6 << 3)
> +#define TSC2005_REG_STATUS (7 << 3)
> +#define TSC2005_REG_AUX_HIGH (8 << 3)
> +#define TSC2005_REG_AUX_LOW (9 << 3)
> +#define TSC2005_REG_TEMP_HIGH (10 << 3)
> +#define TSC2005_REG_TEMP_LOW (11 << 3)
> +#define TSC2005_REG_CFR0 (12 << 3)
> +#define TSC2005_REG_CFR1 (13 << 3)
> +#define TSC2005_REG_CFR2 (14 << 3)
> +#define TSC2005_REG_FUNCTION (15 << 3)
> +
> +#define TSC2005_REG_PND0 (1 << 1)
> +#define TSC2005_REG_READ (0x01)
> +#define TSC2005_REG_WRITE (0x00)
> +
> +
> +#define TSC2005_CFR0_LONGSAMPLING (1)
> +#define TSC2005_CFR0_DETECTINWAIT (1 << 1)
> +#define TSC2005_CFR0_SENSETIME_32US (0)
> +#define TSC2005_CFR0_SENSETIME_96US (1 << 2)
> +#define TSC2005_CFR0_SENSETIME_544US (1 << 3)
> +#define TSC2005_CFR0_SENSETIME_2080US (1 << 4)
> +#define TSC2005_CFR0_SENSETIME_2656US (0x001C)
> +#define TSC2005_CFR0_PRECHARGE_20US (0x0000)
> +#define TSC2005_CFR0_PRECHARGE_84US (0x0020)
> +#define TSC2005_CFR0_PRECHARGE_276US (0x0040)
> +#define TSC2005_CFR0_PRECHARGE_1044US (0x0080)
> +#define TSC2005_CFR0_PRECHARGE_1364US (0x00E0)
> +#define TSC2005_CFR0_STABTIME_0US (0x0000)
> +#define TSC2005_CFR0_STABTIME_100US (0x0100)
> +#define TSC2005_CFR0_STABTIME_500US (0x0200)
> +#define TSC2005_CFR0_STABTIME_1MS (0x0300)
> +#define TSC2005_CFR0_STABTIME_5MS (0x0400)
> +#define TSC2005_CFR0_STABTIME_100MS (0x0700)
> +#define TSC2005_CFR0_CLOCK_4MHZ (0x0000)
> +#define TSC2005_CFR0_CLOCK_2MHZ (0x0800)
> +#define TSC2005_CFR0_CLOCK_1MHZ (0x1000)
> +#define TSC2005_CFR0_RESOLUTION12 (0x2000)
> +#define TSC2005_CFR0_STATUS (0x4000)
> +#define TSC2005_CFR0_PENMODE (0x8000)
> +
> +#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS | \
> + TSC2005_CFR0_CLOCK_1MHZ | \
> + TSC2005_CFR0_RESOLUTION12 | \
> + TSC2005_CFR0_PRECHARGE_276US | \
> + TSC2005_CFR0_PENMODE)
> +
> +#define TSC2005_CFR1_BATCHDELAY_0MS (0x0000)
> +#define TSC2005_CFR1_BATCHDELAY_1MS (0x0001)
> +#define TSC2005_CFR1_BATCHDELAY_2MS (0x0002)
> +#define TSC2005_CFR1_BATCHDELAY_4MS (0x0003)
> +#define TSC2005_CFR1_BATCHDELAY_10MS (0x0004)
> +#define TSC2005_CFR1_BATCHDELAY_20MS (0x0005)
> +#define TSC2005_CFR1_BATCHDELAY_40MS (0x0006)
> +#define TSC2005_CFR1_BATCHDELAY_100MS (0x0007)
> +
> +#define TSC2005_CFR1_INITVALUE (TSC2005_CFR1_BATCHDELAY_2MS)
> +
> +#define TSC2005_CFR2_MAVE_TEMP (0x0001)
> +#define TSC2005_CFR2_MAVE_AUX (0x0002)
> +#define TSC2005_CFR2_MAVE_Z (0x0004)
> +#define TSC2005_CFR2_MAVE_Y (0x0008)
> +#define TSC2005_CFR2_MAVE_X (0x0010)
> +#define TSC2005_CFR2_AVG_1 (0x0000)
> +#define TSC2005_CFR2_AVG_3 (0x0400)
> +#define TSC2005_CFR2_AVG_7 (0x0800)
> +#define TSC2005_CFR2_MEDIUM_1 (0x0000)
> +#define TSC2005_CFR2_MEDIUM_3 (0x1000)
> +#define TSC2005_CFR2_MEDIUM_7 (0x2000)
> +#define TSC2005_CFR2_MEDIUM_15 (0x3000)
> +
> +#define TSC2005_CFR2_IRQ_DAV (0x4000)
> +#define TSC2005_CFR2_IRQ_PEN (0x8000)
> +#define TSC2005_CFR2_IRQ_PENDAV (0x0000)
> +
> +#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_IRQ_DAV | \
> + TSC2005_CFR2_MAVE_X | \
> + TSC2005_CFR2_MAVE_Y | \
> + TSC2005_CFR2_MAVE_Z | \
> + TSC2005_CFR2_MEDIUM_15 | \
> + TSC2005_CFR2_AVG_7)
> +
> +#define MAX_12BIT ((1 << 12) - 1)
> +#define TS_SAMPLES 4
> +#define TS_RECT_SIZE 8
> +#define TSC2005_TS_PENUP_TIME 20
> +
> +static const u32 tsc2005_read_reg[] = {
> + (TSC2005_REG | TSC2005_REG_X | TSC2005_REG_READ) << 16,
> + (TSC2005_REG | TSC2005_REG_Y | TSC2005_REG_READ) << 16,
> + (TSC2005_REG | TSC2005_REG_Z1 | TSC2005_REG_READ) << 16,
> + (TSC2005_REG | TSC2005_REG_Z2 | TSC2005_REG_READ) << 16,
> +};
> +#define NUM_READ_REGS (sizeof(tsc2005_read_reg)/sizeof(tsc2005_read_reg[0]))
> +
> +struct tsc2005 {
> + struct spi_device *spi;
> +
> + struct input_dev *idev;
> + char phys[32];
> + struct timer_list penup_timer;
> + spinlock_t lock;
> + struct mutex mutex;
> +
> + struct spi_message read_msg;
> + struct spi_transfer read_xfer[NUM_READ_REGS];
> + u32 data[NUM_READ_REGS];
> +
> + /* previous x,y,z */
> + int x;
> + int y;
> + int p;
> + /* average accumulators for each component */
> + int sample_cnt;
> + int avg_x;
> + int avg_y;
> + int avg_z1;
> + int avg_z2;
> + /* configuration */
> + int x_plate_ohm;
> + int hw_avg_max;
> + int stab_time;
> + int p_max;
> + int touch_pressure;
> + int irq;
> + s16 dav_gpio;
> + /* status */
> + u8 sample_sent;
> + u8 pen_down;
> + u8 disabled;
> + u8 disable_depth;
> + u8 spi_active;
> +};
> +
> +static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
> +{
> + u16 data = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
> + struct spi_message msg;
> + struct spi_transfer xfer = { 0 };
> +
> + xfer.tx_buf = &data;
> + xfer.rx_buf = NULL;
> + xfer.len = 1;
> + xfer.bits_per_word = 8;
> +
> + spi_message_init(&msg);
> + spi_message_add_tail(&xfer, &msg);
> + spi_sync(ts->spi, &msg);
> +}
> +
> +static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
> +{
> + u32 tx;
> + struct spi_message msg;
> + struct spi_transfer xfer = { 0 };
> +
> + tx = (TSC2005_REG | reg | TSC2005_REG_PND0 |
> + TSC2005_REG_WRITE) << 16;
> + tx |= value;
> +
> + xfer.tx_buf = &tx;
> + xfer.rx_buf = NULL;
> + xfer.len = 4;
> + xfer.bits_per_word = 24;
> +
> + spi_message_init(&msg);
> + spi_message_add_tail(&xfer, &msg);
> + spi_sync(ts->spi, &msg);
> +}
> +
> +static void tsc2005_ts_update_pen_state(struct tsc2005 *ts,
> + int x, int y, int pressure)
> +{
> + if (pressure) {
> + input_report_abs(ts->idev, ABS_X, x);
> + input_report_abs(ts->idev, ABS_Y, y);
> + input_report_abs(ts->idev, ABS_PRESSURE, pressure);
> + if (!ts->pen_down) {
> + input_report_key(ts->idev, BTN_TOUCH, 1);
> + ts->pen_down = 1;
> + }
> + } else {
> + input_report_abs(ts->idev, ABS_PRESSURE, 0);
> + if (ts->pen_down) {
> + input_report_key(ts->idev, BTN_TOUCH, 0);
> + ts->pen_down = 0;
> + }
> + }
> +
> + input_sync(ts->idev);
> +}
> +
> +/*
> + * This function is called by the SPI framework after the coordinates
> + * have been read from TSC2005
> + */
> +static void tsc2005_ts_rx(void *arg)
> +{
> + struct tsc2005 *ts = arg;
> + unsigned long flags;
> + int inside_rect, pressure_limit;
> + int x, y, z1, z2, pressure;
> +
> + spin_lock_irqsave(&ts->lock, flags);
> +
> + x = ts->data[0];
> + y = ts->data[1];
> + z1 = ts->data[2];
> + z2 = ts->data[3];
> +
> + /* validate pressure and position */
> + if (x > MAX_12BIT || y > MAX_12BIT)
> + goto out;
> +
> + /* skip coords if the pressure-components are out of range */
> + if (z1 < 100 || z2 > 4000)
> + goto out;
> +
> + /* don't run average on the "pen down" event */
> + if (ts->sample_sent) {
> + ts->avg_x += x;
> + ts->avg_y += y;
> + ts->avg_z1 += z1;
> + ts->avg_z2 += z2;
> +
> + if (++ts->sample_cnt < TS_SAMPLES)
> + goto out;
> +
> + x = ts->avg_x / TS_SAMPLES;
> + y = ts->avg_y / TS_SAMPLES;
> + z1 = ts->avg_z1 / TS_SAMPLES;
> + z2 = ts->avg_z2 / TS_SAMPLES;
> + }
> +
> + ts->sample_cnt = 0;
> + ts->avg_x = 0;
> + ts->avg_y = 0;
> + ts->avg_z1 = 0;
> + ts->avg_z2 = 0;
> +
> + if (z1) {
> + pressure = x * (z2 - z1) / z1;
> + pressure = pressure * ts->x_plate_ohm / 4096;
> + } else
> + goto out;
> +
> + pressure_limit = ts->sample_sent? ts->p_max: ts->touch_pressure;
> + if (pressure > pressure_limit)
> + goto out;
> +
> + /* discard the event if it still is within the previous rect - unless
> + * if the pressure is harder, but then use previous x,y position */
> + inside_rect = (ts->sample_sent &&
> + x > (int)ts->x - TS_RECT_SIZE &&
> + x < (int)ts->x + TS_RECT_SIZE &&
> + y > (int)ts->y - TS_RECT_SIZE &&
> + y < (int)ts->y + TS_RECT_SIZE);
> + if (inside_rect)
> + x = ts->x, y = ts->y;
> +
> + if (!inside_rect || pressure < ts->p) {
> + tsc2005_ts_update_pen_state(ts, x, y, pressure);
> + ts->sample_sent = 1;
> + ts->x = x;
> + ts->y = y;
> + ts->p = pressure;
> + }
Minor nit: will this not break ts_calibrate from tslib? ts_calibrate
tries to read 5 samples for every touch, so the user will need to be
moving the pen. I hit this in emulators where noise needs to be added
artifically because userspace (tslib) relies on it.
Also curious why report if pressure becomes harder but not when softer.
Regards
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html