Merge commit 'v2.6.38-rc3' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 1 Feb 2011 05:16:22 +0000 (21:16 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 1 Feb 2011 05:16:22 +0000 (21:16 -0800)
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ad7877.c
drivers/input/touchscreen/ad7879-spi.c
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/wm831x-ts.c [new file with mode: 0644]
include/linux/mfd/wm831x/pdata.h

index 61834ae..258e43e 100644 (file)
@@ -423,6 +423,16 @@ config TOUCHSCREEN_UCB1400
          To compile this driver as a module, choose M here: the
          module will be called ucb1400_ts.
 
+config TOUCHSCREEN_WM831X
+       tristate "Support for WM831x touchscreen controllers"
+       depends on MFD_WM831X
+       help
+         This enables support for the touchscreen controller on the WM831x
+         series of PMICs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called wm831x-ts.
+
 config TOUCHSCREEN_WM97XX
        tristate "Support for WM97xx AC97 touchscreen controllers"
        depends on AC97_BUS
index 718bcc8..e8dcfab 100644 (file)
@@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)    += touchwin.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2007)      += tsc2007.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)      += ucb1400_ts.o
 obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)  += wacom_w8001.o
+obj-$(CONFIG_TOUCHSCREEN_WM831X)       += wm831x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX)       += wm97xx-ts.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
index a1952fc..714d4e0 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
+#include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ad7877.h>
@@ -826,39 +827,37 @@ static int __devexit ad7877_remove(struct spi_device *spi)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int ad7877_suspend(struct spi_device *spi, pm_message_t message)
+#ifdef CONFIG_PM_SLEEP
+static int ad7877_suspend(struct device *dev)
 {
-       struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+       struct ad7877 *ts = dev_get_drvdata(dev);
 
        ad7877_disable(ts);
 
        return 0;
 }
 
-static int ad7877_resume(struct spi_device *spi)
+static int ad7877_resume(struct device *dev)
 {
-       struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+       struct ad7877 *ts = dev_get_drvdata(dev);
 
        ad7877_enable(ts);
 
        return 0;
 }
-#else
-#define ad7877_suspend NULL
-#define ad7877_resume  NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume);
+
 static struct spi_driver ad7877_driver = {
        .driver = {
                .name   = "ad7877",
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
+               .pm     = &ad7877_pm,
        },
        .probe          = ad7877_probe,
        .remove         = __devexit_p(ad7877_remove),
-       .suspend        = ad7877_suspend,
-       .resume         = ad7877_resume,
 };
 
 static int __init ad7877_init(void)
index 59c6e68..ddf732f 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/input.h>       /* BUS_SPI */
+#include <linux/pm.h>
 #include <linux/spi/spi.h>
 
 #include "ad7879.h"
 #define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
 #define AD7879_READCMD(reg)  (AD7879_CMD(reg) | AD7879_CMD_READ)
 
-#ifdef CONFIG_PM
-static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message)
+#ifdef CONFIG_PM_SLEEP
+static int ad7879_spi_suspend(struct device *dev)
 {
+       struct spi_device *spi = to_spi_device(dev);
        struct ad7879 *ts = spi_get_drvdata(spi);
 
        ad7879_suspend(ts);
@@ -30,19 +32,19 @@ static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message)
        return 0;
 }
 
-static int ad7879_spi_resume(struct spi_device *spi)
+static int ad7879_spi_resume(struct device *dev)
 {
+       struct spi_device *spi = to_spi_device(dev);
        struct ad7879 *ts = spi_get_drvdata(spi);
 
        ad7879_resume(ts);
 
        return 0;
 }
-#else
-# define ad7879_spi_suspend NULL
-# define ad7879_spi_resume  NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(ad7879_spi_pm, ad7879_spi_suspend, ad7879_spi_resume);
+
 /*
  * ad7879_read/write are only used for initial setup and for sysfs controls.
  * The main traffic is done in ad7879_collect().
@@ -173,11 +175,10 @@ static struct spi_driver ad7879_spi_driver = {
                .name   = "ad7879",
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
+               .pm     = &ad7879_spi_pm,
        },
        .probe          = ad7879_spi_probe,
        .remove         = __devexit_p(ad7879_spi_remove),
-       .suspend        = ad7879_spi_suspend,
-       .resume         = ad7879_spi_resume,
 };
 
 static int __init ad7879_spi_init(void)
index 14ea54b..b31e90f 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/pm.h>
 #include <linux/gpio.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
@@ -892,9 +893,10 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
        return IRQ_HANDLED;
 }
 
-static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
+#ifdef CONFIG_PM_SLEEP
+static int ads7846_suspend(struct device *dev)
 {
-       struct ads7846 *ts = dev_get_drvdata(&spi->dev);
+       struct ads7846 *ts = dev_get_drvdata(dev);
 
        mutex_lock(&ts->lock);
 
@@ -914,9 +916,9 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
        return 0;
 }
 
-static int ads7846_resume(struct spi_device *spi)
+static int ads7846_resume(struct device *dev)
 {
-       struct ads7846 *ts = dev_get_drvdata(&spi->dev);
+       struct ads7846 *ts = dev_get_drvdata(dev);
 
        mutex_lock(&ts->lock);
 
@@ -935,6 +937,9 @@ static int ads7846_resume(struct spi_device *spi)
 
        return 0;
 }
+#endif
+
+static SIMPLE_DEV_PM_OPS(ads7846_pm, ads7846_suspend, ads7846_resume);
 
 static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts)
 {
@@ -1402,11 +1407,10 @@ static struct spi_driver ads7846_driver = {
                .name   = "ads7846",
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
+               .pm     = &ads7846_pm,
        },
        .probe          = ads7846_probe,
        .remove         = __devexit_p(ads7846_remove),
-       .suspend        = ads7846_suspend,
-       .resume         = ads7846_resume,
 };
 
 static int __init ads7846_init(void)
diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c
new file mode 100644 (file)
index 0000000..1022f71
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Touchscreen driver for WM831x PMICs
+ *
+ * Copyright 2011 Wolfson Microelectronics plc.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/irq.h>
+#include <linux/mfd/wm831x/pdata.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * R16424 (0x4028) - Touch Control 1
+ */
+#define WM831X_TCH_ENA                          0x8000  /* TCH_ENA */
+#define WM831X_TCH_CVT_ENA                      0x4000  /* TCH_CVT_ENA */
+#define WM831X_TCH_SLPENA                       0x1000  /* TCH_SLPENA */
+#define WM831X_TCH_Z_ENA                        0x0400  /* TCH_Z_ENA */
+#define WM831X_TCH_Y_ENA                        0x0200  /* TCH_Y_ENA */
+#define WM831X_TCH_X_ENA                        0x0100  /* TCH_X_ENA */
+#define WM831X_TCH_DELAY_MASK                   0x00E0  /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_SHIFT                       5  /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_DELAY_WIDTH                       3  /* TCH_DELAY - [7:5] */
+#define WM831X_TCH_RATE_MASK                    0x001F  /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_SHIFT                        0  /* TCH_RATE - [4:0] */
+#define WM831X_TCH_RATE_WIDTH                        5  /* TCH_RATE - [4:0] */
+
+/*
+ * R16425 (0x4029) - Touch Control 2
+ */
+#define WM831X_TCH_PD_WK                        0x2000  /* TCH_PD_WK */
+#define WM831X_TCH_5WIRE                        0x1000  /* TCH_5WIRE */
+#define WM831X_TCH_PDONLY                       0x0800  /* TCH_PDONLY */
+#define WM831X_TCH_ISEL                         0x0100  /* TCH_ISEL */
+#define WM831X_TCH_RPU_MASK                     0x000F  /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_SHIFT                         0  /* TCH_RPU - [3:0] */
+#define WM831X_TCH_RPU_WIDTH                         4  /* TCH_RPU - [3:0] */
+
+/*
+ * R16426-8 (0x402A-C) - Touch Data X/Y/X
+ */
+#define WM831X_TCH_PD                           0x8000  /* TCH_PD1 */
+#define WM831X_TCH_DATA_MASK                    0x0FFF  /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_SHIFT                        0  /* TCH_DATA - [11:0] */
+#define WM831X_TCH_DATA_WIDTH                       12  /* TCH_DATA - [11:0] */
+
+struct wm831x_ts {
+       struct input_dev *input_dev;
+       struct wm831x *wm831x;
+       unsigned int data_irq;
+       unsigned int pd_irq;
+       bool pressure;
+       bool pen_down;
+};
+
+static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
+{
+       struct wm831x_ts *wm831x_ts = irq_data;
+       struct wm831x *wm831x = wm831x_ts->wm831x;
+       static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
+       u16 data[3];
+       int count;
+       int i, ret;
+
+       if (wm831x_ts->pressure)
+               count = 3;
+       else
+               count = 2;
+
+       wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+                       WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+       ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
+                              data);
+       if (ret != 0) {
+               dev_err(wm831x->dev, "Failed to read touch data: %d\n",
+                       ret);
+               return IRQ_NONE;
+       }
+
+       /*
+        * We get a pen down reading on every reading, report pen up if any
+        * individual reading does so.
+        */
+       wm831x_ts->pen_down = true;
+       for (i = 0; i < count; i++) {
+               if (!(data[i] & WM831X_TCH_PD)) {
+                       wm831x_ts->pen_down = false;
+                       continue;
+               }
+               input_report_abs(wm831x_ts->input_dev, data_types[i],
+                                data[i] & WM831X_TCH_DATA_MASK);
+       }
+
+       if (!wm831x_ts->pen_down) {
+               disable_irq_nosync(wm831x_ts->data_irq);
+
+               /* Don't need data any more */
+               wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+                               WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+                               WM831X_TCH_Z_ENA, 0);
+
+               /* Flush any final samples that arrived while reading */
+               wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+                               WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
+
+               wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
+
+               if (wm831x_ts->pressure)
+                       input_report_abs(wm831x_ts->input_dev,
+                                        ABS_PRESSURE, 0);
+
+               input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
+       }
+
+       input_sync(wm831x_ts->input_dev);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
+{
+       struct wm831x_ts *wm831x_ts = irq_data;
+       struct wm831x *wm831x = wm831x_ts->wm831x;
+       int ena = 0;
+
+       /* Start collecting data */
+       if (wm831x_ts->pressure)
+               ena |= WM831X_TCH_Z_ENA;
+
+       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+                       WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
+                       WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
+
+       input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
+       input_sync(wm831x_ts->input_dev);
+
+       wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
+                       WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
+
+       wm831x_ts->pen_down = true;
+       enable_irq(wm831x_ts->data_irq);
+
+       return IRQ_HANDLED;
+}
+
+static int wm831x_ts_input_open(struct input_dev *idev)
+{
+       struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+       struct wm831x *wm831x = wm831x_ts->wm831x;
+
+       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+                       WM831X_TCH_ENA, WM831X_TCH_ENA);
+
+       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+                       WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
+
+       return 0;
+}
+
+static void wm831x_ts_input_close(struct input_dev *idev)
+{
+       struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
+       struct wm831x *wm831x = wm831x_ts->wm831x;
+
+       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+                       WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
+                       WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
+                       WM831X_TCH_Z_ENA, 0);
+
+       if (wm831x_ts->pen_down)
+               disable_irq(wm831x_ts->data_irq);
+}
+
+static __devinit int wm831x_ts_probe(struct platform_device *pdev)
+{
+       struct wm831x_ts *wm831x_ts;
+       struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+       struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
+       struct wm831x_touch_pdata *pdata = NULL;
+       struct input_dev *input_dev;
+       int error;
+
+       if (core_pdata)
+               pdata = core_pdata->touch;
+
+       wm831x_ts = kzalloc(sizeof(struct wm831x_ts), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!wm831x_ts || !input_dev) {
+               error = -ENOMEM;
+               goto err_alloc;
+       }
+
+       wm831x_ts->wm831x = wm831x;
+       wm831x_ts->input_dev = input_dev;
+
+       /*
+        * If we have a direct IRQ use it, otherwise use the interrupt
+        * from the WM831x IRQ controller.
+        */
+       if (pdata && pdata->data_irq)
+               wm831x_ts->data_irq = pdata->data_irq;
+       else
+               wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA");
+
+       if (pdata && pdata->pd_irq)
+               wm831x_ts->pd_irq = pdata->pd_irq;
+       else
+               wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD");
+
+       wm831x_ts->pressure = pdata && pdata->pressure;
+
+       /* Five wire touchscreens can't report pressure */
+       if (pdata && pdata->fivewire) {
+               wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+                               WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
+
+               /* Pressure measurements are not possible for five wire mode */
+               WARN_ON(pdata->pressure && pdata->fivewire);
+               wm831x_ts->pressure = false;
+       } else {
+               wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+                               WM831X_TCH_5WIRE, 0);
+       }
+
+       if (pdata) {
+               switch (pdata->isel) {
+               default:
+                       dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
+                               pdata->isel);
+                       /* Fall through */
+               case 200:
+               case 0:
+                       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+                                       WM831X_TCH_ISEL, 0);
+                       break;
+               case 400:
+                       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+                                       WM831X_TCH_ISEL, WM831X_TCH_ISEL);
+                       break;
+               }
+       }
+
+       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
+                       WM831X_TCH_PDONLY, 0);
+
+       /* Default to 96 samples/sec */
+       wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
+                       WM831X_TCH_RATE_MASK, 6);
+
+       error = request_threaded_irq(wm831x_ts->data_irq,
+                                    NULL, wm831x_ts_data_irq,
+                                    IRQF_ONESHOT,
+                                    "Touchscreen data", wm831x_ts);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
+                       wm831x_ts->data_irq, error);
+               goto err_alloc;
+       }
+       disable_irq(wm831x_ts->data_irq);
+
+       error = request_threaded_irq(wm831x_ts->pd_irq,
+                                    NULL, wm831x_ts_pen_down_irq,
+                                    IRQF_ONESHOT,
+                                    "Touchscreen pen down", wm831x_ts);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
+                       wm831x_ts->pd_irq, error);
+               goto err_data_irq;
+       }
+
+       /* set up touch configuration */
+       input_dev->name = "WM831x touchscreen";
+       input_dev->phys = "wm831x";
+       input_dev->open = wm831x_ts_input_open;
+       input_dev->close = wm831x_ts_input_close;
+
+       __set_bit(EV_ABS, input_dev->evbit);
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(BTN_TOUCH, input_dev->keybit);
+
+       input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
+       if (wm831x_ts->pressure)
+               input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
+
+       input_set_drvdata(input_dev, wm831x_ts);
+       input_dev->dev.parent = &pdev->dev;
+
+       error = input_register_device(input_dev);
+       if (error)
+               goto err_pd_irq;
+
+       platform_set_drvdata(pdev, wm831x_ts);
+       return 0;
+
+err_pd_irq:
+       free_irq(wm831x_ts->pd_irq, wm831x_ts);
+err_data_irq:
+       free_irq(wm831x_ts->data_irq, wm831x_ts);
+err_alloc:
+       input_free_device(input_dev);
+       kfree(wm831x_ts);
+
+       return error;
+}
+
+static __devexit int wm831x_ts_remove(struct platform_device *pdev)
+{
+       struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
+
+       free_irq(wm831x_ts->pd_irq, wm831x_ts);
+       free_irq(wm831x_ts->data_irq, wm831x_ts);
+       input_unregister_device(wm831x_ts->input_dev);
+       kfree(wm831x_ts);
+
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct platform_driver wm831x_ts_driver = {
+       .driver = {
+               .name = "wm831x-touch",
+               .owner = THIS_MODULE,
+       },
+       .probe = wm831x_ts_probe,
+       .remove = __devexit_p(wm831x_ts_remove),
+};
+
+static int __init wm831x_ts_init(void)
+{
+       return platform_driver_register(&wm831x_ts_driver);
+}
+module_init(wm831x_ts_init);
+
+static void __exit wm831x_ts_exit(void)
+{
+       platform_driver_unregister(&wm831x_ts_driver);
+}
+module_exit(wm831x_ts_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-touch");
index fd322ac..173086d 100644 (file)
@@ -80,7 +80,8 @@ struct wm831x_touch_pdata {
        int isel;              /** Current for pen down (uA) */
        int rpu;               /** Pen down sensitivity resistor divider */
        int pressure;          /** Report pressure (boolean) */
-       int data_irq;          /** Touch data ready IRQ */
+       unsigned int data_irq; /** Touch data ready IRQ */
+       unsigned int pd_irq;   /** Touch pendown detect IRQ */
 };
 
 enum wm831x_watchdog_action {