Input: ads7846 - add regulator support
[safe/jmp/linux-2.6] / drivers / input / touchscreen / ads7846.c
index d0004dc..8b05d8e 100644 (file)
 #include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
+#include <linux/regulator/consumer.h>
 #include <asm/irq.h>
 
-
 /*
  * This code has been heavily tested on a Nokia 770, and lightly
- * tested on other ads7846 devices (OSK/Mistral, Lubbock).
+ * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz).
  * TSC2046 is just newer ads7846 silicon.
  * Support for ads7843 tested on Atmel at91sam926x-EK.
  * Support for ads7845 has only been stubbed in.
@@ -43,7 +43,7 @@
  * have to maintain our own SW IRQ disabled status. This should be
  * removed as soon as the affected platform's IRQ handling is fixed.
  *
- * app note sbaa036 talks in more detail about accurate sampling...
+ * App note sbaa036 talks in more detail about accurate sampling...
  * that ought to help in situations like LCDs inducing noise (which
  * can also be helped by using synch signals) and more generally.
  * This driver tries to utilize the measures described in the app
@@ -83,8 +83,10 @@ struct ads7846_packet {
 struct ads7846 {
        struct input_dev        *input;
        char                    phys[32];
+       char                    name[32];
 
        struct spi_device       *spi;
+       struct regulator        *reg;
 
 #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
        struct attribute_group  *attr_group;
@@ -97,6 +99,8 @@ struct ads7846 {
        u16                     x_plate_ohms;
        u16                     pressure_max;
 
+       bool                    swap_xy;
+
        struct ads7846_packet   *packet;
 
        struct spi_transfer     xfer[18];
@@ -127,6 +131,8 @@ struct ads7846 {
        void                    (*filter_cleanup)(void *data);
        int                     (*get_pendown_state)(void);
        int                     gpio_pendown;
+
+       void                    (*wait_for_sync)(void);
 };
 
 /* leave chip selected when we're done, for quicker re-select? */
@@ -295,7 +301,7 @@ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
 static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
 
 
-/* Sysfs conventions report temperatures in millidegrees Celcius.
+/* Sysfs conventions report temperatures in millidegrees Celsius.
  * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high
  * accuracy scheme without calibration data.  For now we won't try either;
  * userspace sees raw sensor values, and must scale/calibrate appropriately.
@@ -511,6 +517,10 @@ static int get_pendown_state(struct ads7846 *ts)
        return !gpio_get_value(ts->gpio_pendown);
 }
 
+static void null_wait_for_sync(void)
+{
+}
+
 /*
  * PENIRQ only kicks the timer.  The timer only reissues the SPI transfer,
  * to retrieve touchscreen status.
@@ -557,10 +567,8 @@ static void ads7846_rx(void *ads)
         * once more the measurement
         */
        if (packet->tc.ignore || Rt > ts->pressure_max) {
-#ifdef VERBOSE
-               pr_debug("%s: ignored %d pressure %d\n",
-                       dev_name(&ts->spi->dev), packet->tc.ignore, Rt);
-#endif
+               dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
+                        packet->tc.ignore, Rt);
                hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
                              HRTIMER_MODE_REL);
                return;
@@ -589,18 +597,18 @@ static void ads7846_rx(void *ads)
                if (!ts->pendown) {
                        input_report_key(input, BTN_TOUCH, 1);
                        ts->pendown = 1;
-#ifdef VERBOSE
-                       dev_dbg(&ts->spi->dev, "DOWN\n");
-#endif
+                       dev_vdbg(&ts->spi->dev, "DOWN\n");
                }
+
+               if (ts->swap_xy)
+                       swap(x, y);
+
                input_report_abs(input, ABS_X, x);
                input_report_abs(input, ABS_Y, y);
-               input_report_abs(input, ABS_PRESSURE, Rt);
+               input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
 
                input_sync(input);
-#ifdef VERBOSE
-               dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
-#endif
+               dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
        }
 
        hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
@@ -686,6 +694,7 @@ static void ads7846_rx_val(void *ads)
        default:
                BUG();
        }
+       ts->wait_for_sync();
        status = spi_async(ts->spi, m);
        if (status)
                dev_err(&ts->spi->dev, "spi_async --> %d\n",
@@ -697,7 +706,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
        struct ads7846  *ts = container_of(handle, struct ads7846, timer);
        int             status = 0;
 
-       spin_lock_irq(&ts->lock);
+       spin_lock(&ts->lock);
 
        if (unlikely(!get_pendown_state(ts) ||
                     device_suspended(&ts->spi->dev))) {
@@ -709,9 +718,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
                        input_sync(input);
 
                        ts->pendown = 0;
-#ifdef VERBOSE
-                       dev_dbg(&ts->spi->dev, "UP\n");
-#endif
+                       dev_vdbg(&ts->spi->dev, "UP\n");
                }
 
                /* measurement cycle ended */
@@ -723,12 +730,13 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
        } else {
                /* pen is still down, continue with the measurement */
                ts->msg_idx = 0;
+               ts->wait_for_sync();
                status = spi_async(ts->spi, &ts->msg[0]);
                if (status)
                        dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
        }
 
-       spin_unlock_irq(&ts->lock);
+       spin_unlock(&ts->lock);
        return HRTIMER_NORESTART;
 }
 
@@ -746,7 +754,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
                         * that here.  (The "generic irq" framework may help...)
                         */
                        ts->irq_disabled = 1;
-                       disable_irq(ts->spi->irq);
+                       disable_irq_nosync(ts->spi->irq);
                        ts->pending = 1;
                        hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
                                        HRTIMER_MODE_REL);
@@ -782,6 +790,8 @@ static void ads7846_disable(struct ads7846 *ts)
                }
        }
 
+       regulator_disable(ts->reg);
+
        /* we know the chip's in lowpower mode since we always
         * leave it that way after every request
         */
@@ -793,6 +803,8 @@ static void ads7846_enable(struct ads7846 *ts)
        if (!ts->disabled)
                return;
 
+       regulator_enable(ts->reg);
+
        ts->disabled = 0;
        ts->irq_disabled = 0;
        enable_irq(ts->spi->irq);
@@ -909,6 +921,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        ts->spi = spi;
        ts->input = input_dev;
        ts->vref_mv = pdata->vref_mv;
+       ts->swap_xy = pdata->swap_xy;
 
        hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        ts->timer.function = ads7846_timer;
@@ -947,9 +960,12 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                ts->penirq_recheck_delay_usecs =
                                pdata->penirq_recheck_delay_usecs;
 
+       ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
+
        snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+       snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
 
-       input_dev->name = "ADS784x Touchscreen";
+       input_dev->name = ts->name;
        input_dev->phys = ts->phys;
        input_dev->dev.parent = &spi->dev;
 
@@ -1129,11 +1145,30 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 
        ts->last_msg = m;
 
+       ts->reg = regulator_get(&spi->dev, "vcc");
+       if (IS_ERR(ts->reg)) {
+               dev_err(&spi->dev, "unable to get regulator: %ld\n",
+                       PTR_ERR(ts->reg));
+               goto err_free_gpio;
+       }
+
+       err = regulator_enable(ts->reg);
+       if (err) {
+               dev_err(&spi->dev, "unable to enable regulator: %d\n", err);
+               goto err_put_regulator;
+       }
+
        if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING,
                        spi->dev.driver->name, ts)) {
-               dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
-               err = -EBUSY;
-               goto err_free_gpio;
+               dev_info(&spi->dev,
+                       "trying pin change workaround on irq %d\n", spi->irq);
+               err = request_irq(spi->irq, ads7846_irq,
+                                 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                                 spi->dev.driver->name, ts);
+               if (err) {
+                       dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+                       goto err_disable_regulator;
+               }
        }
 
        err = ads784x_hwmon_register(spi, ts);
@@ -1164,6 +1199,10 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        ads784x_hwmon_unregister(spi, ts);
  err_free_irq:
        free_irq(spi->irq, ts);
+ err_disable_regulator:
+       regulator_disable(ts->reg);
+ err_put_regulator:
+       regulator_put(ts->reg);
  err_free_gpio:
        if (ts->gpio_pendown != -1)
                gpio_free(ts->gpio_pendown);
@@ -1192,6 +1231,9 @@ static int __devexit ads7846_remove(struct spi_device *spi)
        /* suspend left the IRQ disabled */
        enable_irq(ts->spi->irq);
 
+       regulator_disable(ts->reg);
+       regulator_put(ts->reg);
+
        if (ts->gpio_pendown != -1)
                gpio_free(ts->gpio_pendown);
 
@@ -1231,3 +1273,4 @@ module_exit(ads7846_exit);
 
 MODULE_DESCRIPTION("ADS7846 TouchScreen Driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ads7846");