linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Cory Maccarrone <darkstar6262@gmail.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: linux-input@vger.kernel.org, spi-devel-general@lists.sourceforge.net
Subject: Re: [PATCH] Input: Add tsc2046 touchscreen driver
Date: Sun, 30 May 2010 13:21:01 -0700	[thread overview]
Message-ID: <AANLkTike4Hu6xJ52fcC9TcKaeD4hLeWxHwUvA90tc3_Q@mail.gmail.com> (raw)
In-Reply-To: <20100417082841.GB19866@core.coreip.homeip.net>

On Sat, Apr 17, 2010 at 1:28 AM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> Hi Cory,
>
> Sorry for not reviewing the driver earlier.
>
> On Sun, Dec 13, 2009 at 12:39:14PM -0800, Cory Maccarrone wrote:
>> This change implements a new driver for the TSC2046-series
>> touchscreen found on many HTC OMAP850-based smartphones.
>> This driver differs from the ADS7846 in that it uses 16-bit
>> commands instead of 8-bit, and does not include temerature
>> or voltage sensing capabilities.
>>
>> This driver was submitted in May of 2007 to the linux-omap
>> list at:
>>
>> http://linux.omap.com/pipermail/linux-omap-open-source/2007-May/009745.html
>>
>> This is a resubmit of this driver, with additions that allows
>> the injection of pointercal parameters from tslib through
>> /sys.  This was done for the Wing Linux project primarily to
>> allow this touchscreen to work with Android, but it's useful for
>> any userspace that doesn't support tslib calibration.  Pointercal
>> use is optional -- it produces raw parameters unless the use_pointercal
>> /sys file is set to 1.
>
> The current poiicy is simply use tslib and push the filtering and
> scaling out of the kernel. I do not see the reason for this driver be an
> exception.
>
>>
>> Though this driver was previously submitted to linux-omap, there
>> is nothing omap-specific in this driver.
>>
>
> Overall I would like to see this being not a standalone driver but
> instead tsc2046 support being plugged into existing tsc2007, extending
> it. There are a few of TSC drivers floating around, all of them very
> similar, with the difference of the transport (i2c vs spi) and register
> sets but all having very similar core interrupt handling, suspend and
> resume algorithms... This is a waste.
>
>> Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com>
>> ---
>>  drivers/input/touchscreen/Kconfig   |   14 +
>>  drivers/input/touchscreen/Makefile  |    1 +
>>  drivers/input/touchscreen/tsc2046.c |  740 +++++++++++++++++++++++++++++++++++
>>  include/linux/input/tsc2046.h       |   26 ++
>>  4 files changed, 781 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/input/touchscreen/tsc2046.c
>>  create mode 100644 include/linux/input/tsc2046.h
>>
>> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
>> index 8cc453c..1f3273b 100644
>> --- a/drivers/input/touchscreen/Kconfig
>> +++ b/drivers/input/touchscreen/Kconfig
>> @@ -501,6 +501,20 @@ config TOUCHSCREEN_TOUCHIT213
>>         To compile this driver as a module, choose M here: the
>>         module will be called touchit213.
>>
>> +config TOUCHSCREEN_TSC2046
>> +     tristate "TSC2046 based touchscreens"
>> +     depends on SPI_MASTER
>> +     help
>> +       Say Y here if you have a touchscreen interface using the
>> +       TI TSC 2046 controller, and your board-specific initialization
>> +       code includes that in its table of SPI devices.  Also make
>> +       sure the proper SPI controller is selected.
>> +
>> +       If unsure, say N (but it's safe to say "Y").
>> +
>> +       To compile this driver as a module, choose M here: the
>> +       module will be called tsc2046.
>> +
>>  config TOUCHSCREEN_TSC2007
>>       tristate "TSC2007 based touchscreens"
>>       depends on I2C
>> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
>> index 15fa62c..3c413f7 100644
>> --- a/drivers/input/touchscreen/Makefile
>> +++ b/drivers/input/touchscreen/Makefile
>> @@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)        += touchit213.o
>>  obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
>>  obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)   += touchwin.o
>>  obj-$(CONFIG_TOUCHSCREEN_TSC2007)    += tsc2007.o
>> +obj-$(CONFIG_TOUCHSCREEN_TSC2046)    += tsc2046.o
>>  obj-$(CONFIG_TOUCHSCREEN_UCB1400)    += ucb1400_ts.o
>>  obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)        += wacom_w8001.o
>>  obj-$(CONFIG_TOUCHSCREEN_WM97XX)     += wm97xx-ts.o
>> diff --git a/drivers/input/touchscreen/tsc2046.c b/drivers/input/touchscreen/tsc2046.c
>> new file mode 100644
>> index 0000000..e45d251
>> --- /dev/null
>> +++ b/drivers/input/touchscreen/tsc2046.c
>> @@ -0,0 +1,740 @@
>> +/*
>> + * TSC2046 Touchscreen driver
>> + *
>> + * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
>> + *
>> + * Communication details from original TI driver
>> + * Copyright (C) 2004-2005 Texas Instruments, Inc.
>> + *
>> + * Structure based heavily on TSC2301 driver
>> + * Copyright (C) 2005-2006 Nokia Corporation
>> + *
>> + * 2007 (c) MontaVista Software, Inc. This file is licensed under
>> + * the terms of the GNU General Public License version 2. This program
>> + * is licensed "as is" without any warranty of any kind, whether express
>> + * or implied.
>> + *
>> + */
>> +
>> +#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>
>> +#include <linux/gpio.h>
>> +
>> +#include <linux/input/tsc2046.h>
>> +
>> +/* TSC2046 commands */
>> +#define START_BYTE           0x8000
>> +#define X_CMDWD                      0xd300
>> +#define Y_CMDWD                      0x9300
>> +#define Z1_CMDWD             0xb300
>> +#define Z2_CMDWD             0xc300
>> +
>> +#define TSC2046_TS_SCAN_TIME 1
>> +#define MAX_12BIT            ((1 << 12) - 1)
>> +
>> +#define MIN_ABS_X    528
>> +#define MAX_ABS_X    3760
>> +#define MIN_ABS_Y    624
>> +#define MAX_ABS_Y    3760
>> +
>> +/* The TSC2046 operates at a maximum speed of 2MHz */
>> +#define TSC2046_HZ   2000000
>> +
>> +#define CONV_DATA(d1, d2) \
>> +  (((d1 & 0x7f) << 5) | ((d2 >> 11) & 0x1f))
>> +
>> +/* Convenience macros for pointercal calibration calculation */
>> +#define PA ts->pointercal[0]
>> +#define PB ts->pointercal[1]
>> +#define PC ts->pointercal[2]
>> +#define PD ts->pointercal[3]
>> +#define PE ts->pointercal[4]
>> +#define PF ts->pointercal[5]
>> +#define PS ts->pointercal[6]
>> +
>> +struct tsc2046_ts {
>> +     struct input_dev        *idev;
>> +     char                    phys[32];
>> +     struct timer_list       timer;
>> +     spinlock_t              lock;
>> +
>> +     struct spi_transfer     read_xfer[3];
>> +     struct spi_message      read_msg;
>> +     struct spi_message      enable_msg;
>> +     u16                     data[5];
>> +
>> +     u16                     x;
>> +     u16                     y;
>> +     u16                     p;
>> +     u16                     old_x;
>> +     u16                     old_y;
>> +     u16                     old_pressure;
>> +     int                     sample_cnt;
>> +
>> +     int                     ignore_last : 1;
>> +     u16                     x_plate_ohm;
>> +     int                     max_pressure;
>> +     int                     touch_pressure;
>> +     int                     pressure_limit;
>> +
>> +     int                     pointercal[7];
>> +     u16                     x_res;
>> +     u16                     y_res;
>> +
>> +     u16                     irq_enabled:1;
>> +     u16                     pen_down:1;
>> +     u16                     disabled:1;
>> +     u16                     pending:1;
>> +     u16                     use_pointercal:1;
>> +
>> +     s16                     dav_gpio;
>> +     int                     (*get_pendown_state)(void);
>> +     int                     irq;
>> +};
>> +
>> +struct tsc2046 {
>> +     struct spi_device       *spi;
>> +     struct tsc2046_ts       *ts;
>> +};
>
> Why is such a split?
>
>> +
>> +static const u16 tsc2046_ts_cmd_data[] = {
>> +     START_BYTE, X_CMDWD, Y_CMDWD, Z1_CMDWD, Z2_CMDWD,
>> +};
>> +
>> +static int tsc2046_device_suspended(struct device *dev)
>> +{
>> +     struct tsc2046 *tsc = dev_get_drvdata(dev);
>> +     return dev->power.power_state.event != PM_EVENT_ON || tsc->ts->disabled;
>> +}
>> +
>> +static void tsc2046_update_pen_state(struct tsc2046 *tsc, int x, int y, int pressure)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     if (x <= ts->old_x - 4 || x >= ts->old_x + 4 ||
>> +         y <= ts->old_y - 4 || y >= ts->old_y + 4 ||
>> +         pressure <= ts->old_pressure - 4 || pressure >= ts->old_pressure + 4)
>> +     {
>> +             if (pressure) {
>> +                     if (!ts->use_pointercal || (ts->use_pointercal &&
>> +                         x >= 0 &&
>> +                         x <= ts->x_res - 1 &&
>> +                         y >= 0 &&
>> +                         y <= ts->y_res - 1))
>> +                     {
>> +                             input_report_abs(ts->idev, ABS_X, x);
>> +                             input_report_abs(ts->idev, ABS_Y, y);
>> +                             input_report_abs(ts->idev, ABS_PRESSURE, 1);
>
> Why don't you report true pressure reading instead of clamping to [0,1]
> range?
>
>> +                             input_report_key(ts->idev, BTN_TOUCH, 1);
>> +                             ts->pen_down = 1;
>> +                     } else {
>> +                             input_report_abs(ts->idev, ABS_PRESSURE, 0);
>> +                             input_report_key(ts->idev, BTN_TOUCH, 0);
>> +                             ts->pen_down = 0;
>> +                     }
>> +             } else {
>> +                     input_report_abs(ts->idev, ABS_PRESSURE, 0);
>> +                     input_report_key(ts->idev, BTN_TOUCH, 0);
>> +                     ts->pen_down = 0;
>> +             }
>> +
>> +             ts->old_x = x;
>> +             ts->old_y = y;
>> +             ts->old_pressure = pressure;
>> +
>> +             input_sync(ts->idev);
>> +
>> +#ifdef VERBOSE
>> +             dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure);
>> +#endif
>
> dev_vdbg().
>
>
>> +     }
>> +}
>> +
>> +/*
>> + * This procedure is called by the SPI framework after the coordinates
>> + * have been read from TSC2046
>> + */
>> +static void tsc2046_ts_rx(void *arg)
>> +{
>> +     struct tsc2046 *tsc = arg;
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned int x, y, z1, z2, pressure;
>> +     int xN, yN;
>> +
>> +     x  = CONV_DATA(ts->data[2], ts->data[3]);
>> +     y  = CONV_DATA(ts->data[1], ts->data[2]);
>> +     z1 = CONV_DATA(ts->data[3], ts->data[4]);
>> +     z2 = CONV_DATA(ts->data[4], 0);
>> +
>> +     if (z1) {
>> +             pressure = ts->x_plate_ohm * x;
>> +             pressure /= 4096;
>> +             pressure *= z2 - z1;
>> +             pressure /= z1;
>> +     } else
>> +             pressure = 0;
>> +
>> +     if (ts->use_pointercal) {
>> +             /* Transform pen state according to calibration parameters.  This
>> +              * algorithm is the same used by tslib and Qt to do 7-value
>> +              * screen transforms.  See:
>> +              *
>> +              * http://doc.trolltech.com/4.3/qwscalibratedmousehandler.html#transform
>> +              */
>> +             xN = ((PA * x) + (PB * y) + PC) / PS;
>> +             yN = ((PD * x) + (PE * y) + PF) / PS;
>> +
>> +             x = (xN < 0) ? 0 : (xN > ts->x_res ? ts->x_res - 1 : (unsigned int)xN);
>> +             y = (yN < 0) ? 0 : (yN > ts->y_res ? ts->y_res - 1 : (unsigned int)yN);
>> +     }
>> +
>> +     /* If pressure value is above a preset limit (pen is barely
>> +      * touching the screen) we can't trust the coordinate values.
>> +      */
>> +     if (pressure < ts->pressure_limit && x < MAX_12BIT && y < MAX_12BIT) {
>> +             ts->pressure_limit = ts->max_pressure;
>> +             if (ts->ignore_last) {
>> +                     if (ts->sample_cnt)
>> +                             tsc2046_update_pen_state(tsc, ts->x, ts->y, ts->p);
>> +                     ts->x = x;
>> +                     ts->y = y;
>> +                     ts->p = pressure;
>> +             } else
>> +                     tsc2046_update_pen_state(tsc, x, y, pressure);
>> +             ts->sample_cnt++;
>> +     }
>> +
>> +     mod_timer(&ts->timer,
>> +               jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
>> +}
>> +
>> +static int tsc2046_is_pen_down(struct tsc2046_ts *ts)
>> +{
>> +     return ts->pen_down;
>> +}
>> +
>> +static int tsc2046_get_pendown_state(struct tsc2046_ts *ts)
>> +{
>> +     if (ts->get_pendown_state)
>> +             return ts->get_pendown_state();
>> +
>> +     return !gpio_get_value(ts->dav_gpio);
>> +}
>> +
>> +/*
>> + * Timer is called every TSC2046_TS_SCAN_TIME when the pen is down
>> + */
>> +static void tsc2046_ts_timer(unsigned long arg)
>> +{
>> +     struct tsc2046 *tsc = (void *) arg;
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned long flags;
>> +     int ndav;
>> +     int r;
>> +
>> +     spin_lock_irqsave(&ts->lock, flags);
>> +     ndav = tsc2046_get_pendown_state(ts);
>> +     if (!ndav || tsc2046_device_suspended(&tsc->spi->dev)) {
>> +             /* Pen has been lifted */
>> +             if (!tsc2046_device_suspended(&tsc->spi->dev) &&
>> +                 !ts->irq_enabled)
>> +             {
>> +                     ts->irq_enabled = 1;
>> +                     enable_irq(ts->irq);
>> +             }
>> +             tsc2046_update_pen_state(tsc, 0, 0, 0);
>> +             ts->pending = 0;
>> +             spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +     } else {
>> +             ts->pen_down = 1;
>> +             spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +             r = spi_async(tsc->spi, &ts->read_msg);
>> +             if (r)
>> +                     dev_err(&tsc->spi->dev, "ts: spi_async() failed");
>> +     }
>> +}
>> +
>> +/*
>> + * This interrupt is called when pen is down and first coordinates are
>> + * available. That is indicated by a falling edge on DEV line.  IRQ is
>> + * disabled here because while the pen is down the coordinates are
>> + * read by a timer.
>> + */
>> +static irqreturn_t tsc2046_ts_irq_handler(int irq, void *dev_id)
>> +{
>> +     struct tsc2046 *tsc = dev_id;
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned long flags;
>> +
>> +     spin_lock_irqsave(&ts->lock, flags);
>> +     if (ts->irq_enabled) {
>> +             ts->irq_enabled = 0;
>> +             disable_irq_nosync(ts->irq);
>> +             ts->pending = 1;
>> +             ts->pressure_limit = ts->touch_pressure;
>> +             ts->sample_cnt = 0;
>> +             mod_timer(&ts->timer,
>> +                       jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME));
>> +     }
>> +     spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +/* Must be called with ts->lock held */
>> +static void tsc2046_ts_disable(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     if (ts->disabled)
>> +             return;
>> +
>> +     ts->disabled = 1;
>> +     if (!ts->pending) {
>> +             ts->irq_enabled = 0;
>> +             disable_irq(ts->irq);
>> +     } else {
>> +             while (ts->pending) {
>> +                     spin_unlock_irq(&ts->lock);
>> +                     msleep(1);
>> +                     spin_lock_irq(&ts->lock);
>> +             }
>> +     }
>> +}
>> +
>> +static void tsc2046_ts_enable(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     if (!ts->disabled)
>> +             return;
>> +
>> +     ts->disabled = 0;
>> +     ts->irq_enabled = 1;
>> +     enable_irq(ts->irq);
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +int tsc2046_ts_suspend(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +     tsc2046_ts_disable(tsc);
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return 0;
>> +}
>> +
>> +void tsc2046_ts_resume(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +     tsc2046_ts_enable(tsc);
>> +     spin_unlock_irq(&ts->lock);
>> +}
>> +#endif
>> +
>> +static void tsc2046_ts_setup_spi_xfer(struct tsc2046 *tsc)
>> +{
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     struct spi_message *m = &ts->read_msg;
>> +     struct spi_transfer *x = &ts->read_xfer[1];
>> +
>> +     spi_message_init(m);
>> +
>> +     /* read and write data in one transaction */
>> +     x->tx_buf = &tsc2046_ts_cmd_data;
>> +     x->rx_buf = &ts->data;
>> +     x->len    = 10;
>> +     spi_message_add_tail(x, m);
>> +
>> +     /* send another START_BYTE to (re)enable pen interrupts */
>> +     x++;
>> +     x->tx_buf = &tsc2046_ts_cmd_data[0];
>> +     x->rx_buf = &ts->data;
>> +     x->len = 2;
>> +     spi_message_add_tail(x, m);
>> +
>> +     m->complete = tsc2046_ts_rx;
>> +     m->context = tsc;
>> +}
>> +
>> +/* Attribute functions */
>> +
>> +static ssize_t tsc2046_ts_use_pointercal_show(struct device *dev,
>> +                                    struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%u\n", ts->use_pointercal);
>> +}
>> +
>> +static ssize_t tsc2046_ts_use_pointercal_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     int i;
>> +
>> +     i = simple_strtoul(buf, &endp, 10);
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     if (i)
>> +             ts->use_pointercal = 1;
>> +     else
>> +             ts->use_pointercal = 0;
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(use_pointercal, 0644, tsc2046_ts_use_pointercal_show, tsc2046_ts_use_pointercal_store);
>> +
>> +static ssize_t tsc2046_ts_pointercal_show(struct device *dev,
>> +                struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%d %d %d %d %d %d %d\n", PA, PB, PC, PD, PE, PF, PS);
>> +}
>> +
>> +static ssize_t tsc2046_ts_pointercal_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     int i;
>> +     unsigned int index = 0, offset = 0;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     for (index = 0; index < 7; index++) {
>> +             /* Grab the numbers */
>> +             i = simple_strtol(buf + offset, &endp, 10);
>> +             ts->pointercal[index] = i;
>> +
>> +             /* Skip the space */
>> +             offset = endp - buf + 1;
>> +     }
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(pointercal, 0644, tsc2046_ts_pointercal_show, tsc2046_ts_pointercal_store);
>> +
>> +static ssize_t tsc2046_ts_screen_res_show(struct device *dev,
>> +                struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%u %u\n", ts->x_res, ts->y_res);
>> +}
>> +
>> +static ssize_t tsc2046_ts_screen_res_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     unsigned int i, offset = 0;
>> +
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     /* Grab the x res */
>> +     i = simple_strtoul(buf, &endp, 10);
>> +     ts->x_res = i;
>> +
>> +     /* Skip the space */
>> +     offset = endp - buf + 1;
>> +
>> +     /* Grab the y res */
>> +     i = simple_strtoul(buf + offset, &endp, 10);
>> +     ts->y_res = i;
>> +
>> +     /* Update the device parameters */
>> +     input_set_abs_params(ts->idev, ABS_X, 0, ts->x_res - 1, 0, 0);
>> +     input_set_abs_params(ts->idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(screen_res, 0644, tsc2046_ts_screen_res_show, tsc2046_ts_screen_res_store);
>> +
>> +static ssize_t tsc2046_ts_pen_down_show(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     char *buf)
>> +{
>> +     struct tsc2046 *tsc = dev_get_drvdata(dev);
>> +
>> +     return sprintf(buf, "%u\n", tsc2046_is_pen_down(tsc->ts));
>> +}
>> +
>> +static DEVICE_ATTR(pen_down, S_IRUGO, tsc2046_ts_pen_down_show, NULL);
>> +
>> +static ssize_t tsc2046_ts_disable_show(struct device *dev,
>> +                                    struct device_attribute *attr, char *buf)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +
>> +     return sprintf(buf, "%u\n", ts->disabled);
>> +}
>> +
>> +static ssize_t tsc2046_ts_disable_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +     struct tsc2046          *tsc = dev_get_drvdata(dev);
>> +     struct tsc2046_ts       *ts = tsc->ts;
>> +     char *endp;
>> +     int i;
>> +
>> +     i = simple_strtoul(buf, &endp, 10);
>> +     spin_lock_irq(&ts->lock);
>> +
>> +     if (i)
>> +             tsc2046_ts_disable(tsc);
>> +     else
>> +             tsc2046_ts_enable(tsc);
>> +
>> +     spin_unlock_irq(&ts->lock);
>> +
>> +     return count;
>> +}
>> +
>> +static DEVICE_ATTR(disable_ts, 0664, tsc2046_ts_disable_show,
>> +                tsc2046_ts_disable_store);
>> +
>> +/* Init functions */
>> +
>> +static int __devinit tsc2046_probe(struct spi_device *spi)
>> +{
>> +     struct tsc2046                  *tsc;
>> +     struct tsc2046_ts               *ts;
>> +     struct tsc2046_platform_data    *pdata = spi->dev.platform_data;
>> +     struct input_dev                *idev;
>> +     int r = -ENODEV;
>> +
>> +     dev_dbg(&spi->dev, "%s\n", __FUNCTION__);
>> +
>> +     if (!pdata) {
>> +             dev_dbg(&spi->dev, "no platform data?\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
>> +     if (tsc == NULL)
>> +             return -ENOMEM;
>> +
>> +     dev_set_drvdata(&spi->dev, tsc);
>> +     tsc->spi = spi;
>> +     spi->dev.power.power_state = PMSG_ON;
>> +
>> +     spi->mode = SPI_MODE_1;
>> +     spi->bits_per_word = 16;
>> +
>> +     /* The max speed might've been defined by the board-specific
>> +      * struct */
>> +     if (!spi->max_speed_hz)
>> +             spi->max_speed_hz = TSC2046_HZ;
>> +     spi_setup(spi);
>> +
>> +     if (spi->irq < 0) {
>> +             dev_err(&tsc->spi->dev, "need DAV IRQ/GPIO");
>> +             return -EINVAL;
>> +     }
>> +
>> +     if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) {
>> +             dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     ts = kzalloc(sizeof(*ts), GFP_KERNEL);
>> +     if (ts == NULL)
>> +             return -ENOMEM;
>> +     tsc->ts = ts;
>> +
>> +     if (pdata->get_pendown_state)
>> +             ts->get_pendown_state = pdata->get_pendown_state;
>> +     else {
>> +             ts->dav_gpio = pdata->gpio_pendown;
>> +             r = gpio_request(ts->dav_gpio, dev_name(&tsc->spi->dev));
>> +             if (r < 0) {
>> +                     dev_err(&tsc->spi->dev, "unable to get DAV GPIO");
>> +                     goto err1;
>> +             }
>> +             gpio_direction_input(ts->dav_gpio);
>> +     }
>> +
>> +     ts->irq = spi->irq;
>> +
>> +     init_timer(&ts->timer);
>> +     ts->timer.data = (unsigned long) tsc;
>> +     ts->timer.function = tsc2046_ts_timer;
>> +
>> +     spin_lock_init(&ts->lock);
>> +
>> +     ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
>> +     ts->max_pressure = pdata->ts_max_pressure ? : MAX_12BIT;
>> +     ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure;
>> +     ts->ignore_last = pdata->ts_ignore_last;
>> +
>> +     idev = input_allocate_device();
>> +     if (idev == NULL) {
>> +             r = -ENOMEM;
>> +             goto err2;
>> +     }
>> +     idev->name = "TSC2046 touchscreen";
>> +     snprintf(ts->phys, sizeof(ts->phys),
>> +              "%s/input-ts", dev_name(&tsc->spi->dev));
>> +     idev->phys = ts->phys;
>> +
>> +     idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
>> +     idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
>> +     ts->idev = idev;
>> +
>> +     tsc2046_ts_setup_spi_xfer(tsc);
>> +
>> +     /* X and Y res default to 240x320 */
>> +     ts->x_res = pdata->ts_x_res ? : 240;
>> +     ts->y_res = pdata->ts_y_res ? : 320;
>> +
>> +     input_set_abs_params(idev, ABS_X, 0, ts->x_res - 1, 0, 0);
>> +     input_set_abs_params(idev, ABS_Y, 0, ts->y_res - 1, 0, 0);
>> +     input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
>> +
>> +     set_bit(BTN_TOUCH, idev->keybit);
>> +
>> +     r = request_irq(ts->irq, tsc2046_ts_irq_handler,
>> +                     IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
>> +                     "tsc2046-ts", tsc);
>> +     if (r < 0) {
>> +             dev_err(&tsc->spi->dev, "unable to get DAV IRQ");
>> +             goto err3;
>> +     }
>> +     set_irq_wake(ts->irq, 1);
>> +     ts->irq_enabled = 1;
>> +
>> +     /* Create the various driver attributes */
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0)
>> +             goto err4;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0)
>> +             goto err5;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_use_pointercal) < 0)
>> +             goto err6;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_pointercal) < 0)
>> +             goto err7;
>> +     if (device_create_file(&tsc->spi->dev, &dev_attr_screen_res) < 0)
>> +             goto err8;
>
> If there are still more than 1 attriute left then anonymous attribute
> group should be used. HOwever if you remove pointercal support that
> basically leaves disable methid and I am not too keen on having it
> either - I believe we need a generic solution for shutting off an
> arbitrary device and not somethign that has to be reimplemented in every
> driver.
>
>> +
>> +     r = input_register_device(idev);
>> +     if (r < 0) {
>> +             dev_err(&tsc->spi->dev, "can't register touchscreen device\n");
>> +             goto err9;
>> +     }
>> +
>> +     /* kick off a transaction to enable pen interrupts */
>> +     spi_async(tsc->spi, &ts->read_msg);
>
> Should be done in open() method.
>
>> +
>> +     return 0;
>> +err9:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
>> +err8:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
>> +err7:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
>> +err6:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
>> +err5:
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
>> +err4:
>> +     free_irq(ts->irq, tsc);
>> +err3:
>> +     input_free_device(idev);
>> +err2:
>> +     if (ts->dav_gpio)
>> +             gpio_free(ts->dav_gpio);
>> +err1:
>> +     kfree(ts);
>> +     return r;
>> +}
>> +
>> +static int __devexit tsc2046_remove(struct spi_device *spi)
>> +{
>> +     struct tsc2046 *tsc = dev_get_drvdata(&spi->dev);
>> +     struct tsc2046_ts *ts = tsc->ts;
>> +     unsigned long flags;
>> +
>> +     dev_dbg(&tsc->spi->dev, "%s\n", __FUNCTION__);
>> +
>> +     spin_lock_irqsave(&ts->lock, flags);
>> +     tsc2046_ts_disable(tsc);
>> +     spin_unlock_irqrestore(&ts->lock, flags);
>> +
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_use_pointercal);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_pointercal);
>> +     device_remove_file(&tsc->spi->dev, &dev_attr_screen_res);
>> +
>> +     free_irq(ts->irq, tsc);
>> +     input_unregister_device(ts->idev);
>> +
>> +     if (ts->dav_gpio)
>> +             gpio_free(ts->dav_gpio);
>> +
>> +     kfree(ts);
>> +     kfree(tsc);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct spi_driver tsc2046_driver = {
>> +     .driver = {
>> +                .name = "tsc2046",
>> +                .bus = &spi_bus_type,
>> +                .owner = THIS_MODULE,
>> +     },
>> +     .probe = tsc2046_probe,
>> +     .remove = __devexit_p(tsc2046_remove),
>> +};
>> +
>> +static int __init tsc2046_init(void)
>> +{
>> +     printk(KERN_INFO "TSC2046 driver initializing\n");
>> +
>> +     return spi_register_driver(&tsc2046_driver);
>> +}
>> +
>> +static void __exit tsc2046_exit(void)
>> +{
>> +     spi_unregister_driver(&tsc2046_driver);
>> +}
>> +
>> +module_init(tsc2046_init);
>> +module_exit(tsc2046_exit);
>> +
>> +MODULE_AUTHOR("Kevin Hilman <khilman@mvista.com>");
>> +MODULE_LICENSE("GPL");
>> +
>> diff --git a/include/linux/input/tsc2046.h b/include/linux/input/tsc2046.h
>> new file mode 100644
>> index 0000000..acb8fed
>> --- /dev/null
>> +++ b/include/linux/input/tsc2046.h
>> @@ -0,0 +1,26 @@
>> +#ifndef _LINUX_INPUT_TSC2046_H
>> +#define _LINUX_INPUT_TSC2046_H
>> +
>> +#include <linux/types.h>
>> +#include <linux/timer.h>
>> +
>> +struct tsc2046_platform_data {
>> +     int     (*get_pendown_state)(void);
>> +     int     gpio_pendown;
>> +     s16     gpio_debounce;
>> +     u16     ts_x_plate_ohm;
>> +     u32     ts_max_pressure;  /* Samples with bigger pressure value will
>> +                                  be ignored, since the corresponding X, Y
>> +                                  values are unreliable */
>> +     u32     ts_touch_pressure;/* Pressure limit until we report a
>> +                                  touch event. After that we switch
>> +                                  to ts_max_pressure. */
>> +     u32     ts_x_res;
>> +     u32     ts_y_res;
>> +     unsigned ts_ignore_last : 1;
>> +
>> +};
>> +
>> +#endif
>> +
>> +
>
> Thanks.
>
> --
> Dmitry
>

Turns out the ads7846 driver works fine for this hardware -- there was
a bug in the SPI100k driver we were using that caused a problem with
it.  With that fixed, there's no more need for this driver.

Thanks for reviewing it.
- Cory
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

  reply	other threads:[~2010-05-30 20:21 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-12-13 20:39 [PATCH] Input: Add tsc2046 touchscreen driver Cory Maccarrone
     [not found] ` <1260736754-374-1-git-send-email-darkstar6262-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2010-01-10 23:07   ` Cory Maccarrone
2010-03-07 17:54     ` Cory Maccarrone
2010-04-17  8:28 ` Dmitry Torokhov
2010-05-30 20:21   ` Cory Maccarrone [this message]
2010-05-30 21:08     ` [spi-devel-general] " David Brownell
2010-05-30 21:15       ` Cory Maccarrone

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=AANLkTike4Hu6xJ52fcC9TcKaeD4hLeWxHwUvA90tc3_Q@mail.gmail.com \
    --to=darkstar6262@gmail.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=spi-devel-general@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).