Input: ads7846 - extend the driver for ads7845 controller support
authorAnatolij Gustschin <agust@denx.de>
Thu, 1 Jul 2010 16:01:56 +0000 (09:01 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 3 Jul 2010 20:13:22 +0000 (13:13 -0700)
ADS7845 is a controller for 5-wire touch screens and somewhat
different from 7846. It requires three serial communications to
accomplish one complete conversion. Unlike 7846 it doesn't allow
Z1-/Z2- position measurement.

The patch extends the ads7846 driver to also support ads7845.
The packet struct is extended to contain needed command and
conversion buffers. ads7846_rx() and ads7846_rx_val() now
differentiate between 7845 and 7846 case. ads7846_probe() is
modified to setup ads7845 specific command and conversion
messages and to switch ads7845 into power-down mode, since
this is needed to be prepared to respond to pendown interrupts.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/touchscreen/ads7846.c

index a377160..1603193 100644 (file)
@@ -68,6 +68,8 @@ struct ts_event {
        u16     y;
        u16     z1, z2;
        int     ignore;
+       u8      x_buf[3];
+       u8      y_buf[3];
 };
 
 /*
@@ -79,6 +81,8 @@ struct ads7846_packet {
        u8                      read_x, read_y, read_z1, read_z2, pwrdown;
        u16                     dummy;          /* for the pwrdown read */
        struct ts_event         tc;
+       /* for ads7845 with mpc5121 psc spi we use 3-byte buffers */
+       u8                      read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3];
 };
 
 struct ads7846 {
@@ -207,6 +211,14 @@ struct ser_req {
        struct spi_transfer     xfer[6];
 };
 
+struct ads7845_ser_req {
+       u8                      command[3];
+       u8                      pwrdown[3];
+       u8                      sample[3];
+       struct spi_message      msg;
+       struct spi_transfer     xfer[2];
+};
+
 static void ads7846_enable(struct ads7846 *ts);
 static void ads7846_disable(struct ads7846 *ts);
 
@@ -287,6 +299,41 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
        return status;
 }
 
+static int ads7845_read12_ser(struct device *dev, unsigned command)
+{
+       struct spi_device       *spi = to_spi_device(dev);
+       struct ads7846          *ts = dev_get_drvdata(dev);
+       struct ads7845_ser_req  *req = kzalloc(sizeof *req, GFP_KERNEL);
+       int                     status;
+
+       if (!req)
+               return -ENOMEM;
+
+       spi_message_init(&req->msg);
+
+       req->command[0] = (u8) command;
+       req->xfer[0].tx_buf = req->command;
+       req->xfer[0].rx_buf = req->sample;
+       req->xfer[0].len = 3;
+       spi_message_add_tail(&req->xfer[0], &req->msg);
+
+       ts->irq_disabled = 1;
+       disable_irq(spi->irq);
+       status = spi_sync(spi, &req->msg);
+       ts->irq_disabled = 0;
+       enable_irq(spi->irq);
+
+       if (status == 0) {
+               /* BE12 value, then padding */
+               status = be16_to_cpu(*((u16 *)&req->sample[1]));
+               status = status >> 3;
+               status &= 0x0fff;
+       }
+
+       kfree(req);
+       return status;
+}
+
 #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
 
 #define SHOW(name, var, adjust) static ssize_t \
@@ -540,10 +587,17 @@ static void ads7846_rx(void *ads)
        /* ads7846_rx_val() did in-place conversion (including byteswap) from
         * on-the-wire format as part of debouncing to get stable readings.
         */
-       x = packet->tc.x;
-       y = packet->tc.y;
-       z1 = packet->tc.z1;
-       z2 = packet->tc.z2;
+       if (ts->model == 7845) {
+               x = *(u16 *)packet->tc.x_buf;
+               y = *(u16 *)packet->tc.y_buf;
+               z1 = 0;
+               z2 = 0;
+       } else {
+               x = packet->tc.x;
+               y = packet->tc.y;
+               z1 = packet->tc.z1;
+               z2 = packet->tc.z2;
+       }
 
        /* range filtering */
        if (x == MAX_12BIT)
@@ -551,6 +605,12 @@ static void ads7846_rx(void *ads)
 
        if (ts->model == 7843) {
                Rt = ts->pressure_max / 2;
+       } else if (ts->model == 7845) {
+               if (get_pendown_state(ts))
+                       Rt = ts->pressure_max / 2;
+               else
+                       Rt = 0;
+               dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt);
        } else if (likely(x && z1)) {
                /* compute touch pressure resistance using equation #2 */
                Rt = z2;
@@ -671,10 +731,14 @@ static void ads7846_rx_val(void *ads)
        m = &ts->msg[ts->msg_idx];
        t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
 
-       /* adjust:  on-wire is a must-ignore bit, a BE12 value, then padding;
-        * built from two 8 bit values written msb-first.
-        */
-       val = be16_to_cpup((__be16 *)t->rx_buf) >> 3;
+       if (ts->model == 7845) {
+               val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
+       } else {
+               /* adjust:  on-wire is a must-ignore bit, a BE12 value, then
+                * padding; built from two 8 bit values written msb-first.
+                */
+               val = be16_to_cpup((__be16 *)t->rx_buf) >> 3;
+       }
 
        action = ts->filter(ts->filter_data, ts->msg_idx, &val);
        switch (action) {
@@ -1009,16 +1073,26 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 
        spi_message_init(m);
 
-       /* y- still on; turn on only y+ (and ADC) */
-       packet->read_y = READ_Y(vref);
-       x->tx_buf = &packet->read_y;
-       x->len = 1;
-       spi_message_add_tail(x, m);
+       if (ts->model == 7845) {
+               packet->read_y_cmd[0] = READ_Y(vref);
+               packet->read_y_cmd[1] = 0;
+               packet->read_y_cmd[2] = 0;
+               x->tx_buf = &packet->read_y_cmd[0];
+               x->rx_buf = &packet->tc.y_buf[0];
+               x->len = 3;
+               spi_message_add_tail(x, m);
+       } else {
+               /* y- still on; turn on only y+ (and ADC) */
+               packet->read_y = READ_Y(vref);
+               x->tx_buf = &packet->read_y;
+               x->len = 1;
+               spi_message_add_tail(x, m);
 
-       x++;
-       x->rx_buf = &packet->tc.y;
-       x->len = 2;
-       spi_message_add_tail(x, m);
+               x++;
+               x->rx_buf = &packet->tc.y;
+               x->len = 2;
+               spi_message_add_tail(x, m);
+       }
 
        /* the first sample after switching drivers can be low quality;
         * optionally discard it, using a second one after the signals
@@ -1044,17 +1118,28 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        m++;
        spi_message_init(m);
 
-       /* turn y- off, x+ on, then leave in lowpower */
-       x++;
-       packet->read_x = READ_X(vref);
-       x->tx_buf = &packet->read_x;
-       x->len = 1;
-       spi_message_add_tail(x, m);
+       if (ts->model == 7845) {
+               x++;
+               packet->read_x_cmd[0] = READ_X(vref);
+               packet->read_x_cmd[1] = 0;
+               packet->read_x_cmd[2] = 0;
+               x->tx_buf = &packet->read_x_cmd[0];
+               x->rx_buf = &packet->tc.x_buf[0];
+               x->len = 3;
+               spi_message_add_tail(x, m);
+       } else {
+               /* turn y- off, x+ on, then leave in lowpower */
+               x++;
+               packet->read_x = READ_X(vref);
+               x->tx_buf = &packet->read_x;
+               x->len = 1;
+               spi_message_add_tail(x, m);
 
-       x++;
-       x->rx_buf = &packet->tc.x;
-       x->len = 2;
-       spi_message_add_tail(x, m);
+               x++;
+               x->rx_buf = &packet->tc.x;
+               x->len = 2;
+               spi_message_add_tail(x, m);
+       }
 
        /* ... maybe discard first sample ... */
        if (pdata->settle_delay_usecs) {
@@ -1145,15 +1230,25 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        m++;
        spi_message_init(m);
 
-       x++;
-       packet->pwrdown = PWRDOWN;
-       x->tx_buf = &packet->pwrdown;
-       x->len = 1;
-       spi_message_add_tail(x, m);
+       if (ts->model == 7845) {
+               x++;
+               packet->pwrdown_cmd[0] = PWRDOWN;
+               packet->pwrdown_cmd[1] = 0;
+               packet->pwrdown_cmd[2] = 0;
+               x->tx_buf = &packet->pwrdown_cmd[0];
+               x->len = 3;
+       } else {
+               x++;
+               packet->pwrdown = PWRDOWN;
+               x->tx_buf = &packet->pwrdown;
+               x->len = 1;
+               spi_message_add_tail(x, m);
+
+               x++;
+               x->rx_buf = &packet->dummy;
+               x->len = 2;
+       }
 
-       x++;
-       x->rx_buf = &packet->dummy;
-       x->len = 2;
        CS_CHANGE(*x);
        spi_message_add_tail(x, m);
 
@@ -1202,8 +1297,11 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        /* take a first sample, leaving nPENIRQ active and vREF off; avoid
         * the touchscreen, in case it's not connected.
         */
-       (void) ads7846_read12_ser(&spi->dev,
-                         READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
+       if (ts->model == 7845)
+               ads7845_read12_ser(&spi->dev, PWRDOWN);
+       else
+               (void) ads7846_read12_ser(&spi->dev,
+                               READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
 
        err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
        if (err)