linux/drivers/input/touchscreen/wm831x-ts.c
<<
>>
Prefs
   1/*
   2 * Touchscreen driver for WM831x PMICs
   3 *
   4 * Copyright 2011 Wolfson Microelectronics plc.
   5 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   6 *
   7 * This program is free software; you can redistribute  it and/or modify it
   8 * under  the terms of  the GNU General  Public License as published by the
   9 * Free Software Foundation;  either version 2 of the  License, or (at your
  10 * option) any later version.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/moduleparam.h>
  15#include <linux/kernel.h>
  16#include <linux/init.h>
  17#include <linux/string.h>
  18#include <linux/pm.h>
  19#include <linux/input.h>
  20#include <linux/interrupt.h>
  21#include <linux/io.h>
  22#include <linux/mfd/wm831x/core.h>
  23#include <linux/mfd/wm831x/irq.h>
  24#include <linux/mfd/wm831x/pdata.h>
  25#include <linux/platform_device.h>
  26#include <linux/slab.h>
  27#include <linux/types.h>
  28
  29/*
  30 * R16424 (0x4028) - Touch Control 1
  31 */
  32#define WM831X_TCH_ENA                          0x8000  /* TCH_ENA */
  33#define WM831X_TCH_CVT_ENA                      0x4000  /* TCH_CVT_ENA */
  34#define WM831X_TCH_SLPENA                       0x1000  /* TCH_SLPENA */
  35#define WM831X_TCH_Z_ENA                        0x0400  /* TCH_Z_ENA */
  36#define WM831X_TCH_Y_ENA                        0x0200  /* TCH_Y_ENA */
  37#define WM831X_TCH_X_ENA                        0x0100  /* TCH_X_ENA */
  38#define WM831X_TCH_DELAY_MASK                   0x00E0  /* TCH_DELAY - [7:5] */
  39#define WM831X_TCH_DELAY_SHIFT                       5  /* TCH_DELAY - [7:5] */
  40#define WM831X_TCH_DELAY_WIDTH                       3  /* TCH_DELAY - [7:5] */
  41#define WM831X_TCH_RATE_MASK                    0x001F  /* TCH_RATE - [4:0] */
  42#define WM831X_TCH_RATE_SHIFT                        0  /* TCH_RATE - [4:0] */
  43#define WM831X_TCH_RATE_WIDTH                        5  /* TCH_RATE - [4:0] */
  44
  45/*
  46 * R16425 (0x4029) - Touch Control 2
  47 */
  48#define WM831X_TCH_PD_WK                        0x2000  /* TCH_PD_WK */
  49#define WM831X_TCH_5WIRE                        0x1000  /* TCH_5WIRE */
  50#define WM831X_TCH_PDONLY                       0x0800  /* TCH_PDONLY */
  51#define WM831X_TCH_ISEL                         0x0100  /* TCH_ISEL */
  52#define WM831X_TCH_RPU_MASK                     0x000F  /* TCH_RPU - [3:0] */
  53#define WM831X_TCH_RPU_SHIFT                         0  /* TCH_RPU - [3:0] */
  54#define WM831X_TCH_RPU_WIDTH                         4  /* TCH_RPU - [3:0] */
  55
  56/*
  57 * R16426-8 (0x402A-C) - Touch Data X/Y/X
  58 */
  59#define WM831X_TCH_PD                           0x8000  /* TCH_PD1 */
  60#define WM831X_TCH_DATA_MASK                    0x0FFF  /* TCH_DATA - [11:0] */
  61#define WM831X_TCH_DATA_SHIFT                        0  /* TCH_DATA - [11:0] */
  62#define WM831X_TCH_DATA_WIDTH                       12  /* TCH_DATA - [11:0] */
  63
  64struct wm831x_ts {
  65        struct input_dev *input_dev;
  66        struct wm831x *wm831x;
  67        unsigned int data_irq;
  68        unsigned int pd_irq;
  69        bool pressure;
  70        bool pen_down;
  71        struct work_struct pd_data_work;
  72};
  73
  74static void wm831x_pd_data_work(struct work_struct *work)
  75{
  76        struct wm831x_ts *wm831x_ts =
  77                container_of(work, struct wm831x_ts, pd_data_work);
  78
  79        if (wm831x_ts->pen_down) {
  80                enable_irq(wm831x_ts->data_irq);
  81                dev_dbg(wm831x_ts->wm831x->dev, "IRQ PD->DATA done\n");
  82        } else {
  83                enable_irq(wm831x_ts->pd_irq);
  84                dev_dbg(wm831x_ts->wm831x->dev, "IRQ DATA->PD done\n");
  85        }
  86}
  87
  88static irqreturn_t wm831x_ts_data_irq(int irq, void *irq_data)
  89{
  90        struct wm831x_ts *wm831x_ts = irq_data;
  91        struct wm831x *wm831x = wm831x_ts->wm831x;
  92        static int data_types[] = { ABS_X, ABS_Y, ABS_PRESSURE };
  93        u16 data[3];
  94        int count;
  95        int i, ret;
  96
  97        if (wm831x_ts->pressure)
  98                count = 3;
  99        else
 100                count = 2;
 101
 102        wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
 103                        WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
 104
 105        ret = wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count,
 106                               data);
 107        if (ret != 0) {
 108                dev_err(wm831x->dev, "Failed to read touch data: %d\n",
 109                        ret);
 110                return IRQ_NONE;
 111        }
 112
 113        /*
 114         * We get a pen down reading on every reading, report pen up if any
 115         * individual reading does so.
 116         */
 117        wm831x_ts->pen_down = true;
 118        for (i = 0; i < count; i++) {
 119                if (!(data[i] & WM831X_TCH_PD)) {
 120                        wm831x_ts->pen_down = false;
 121                        continue;
 122                }
 123                input_report_abs(wm831x_ts->input_dev, data_types[i],
 124                                 data[i] & WM831X_TCH_DATA_MASK);
 125        }
 126
 127        if (!wm831x_ts->pen_down) {
 128                /* Switch from data to pen down */
 129                dev_dbg(wm831x->dev, "IRQ DATA->PD\n");
 130
 131                disable_irq_nosync(wm831x_ts->data_irq);
 132
 133                /* Don't need data any more */
 134                wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
 135                                WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
 136                                WM831X_TCH_Z_ENA, 0);
 137
 138                /* Flush any final samples that arrived while reading */
 139                wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
 140                                WM831X_TCHDATA_EINT, WM831X_TCHDATA_EINT);
 141
 142                wm831x_bulk_read(wm831x, WM831X_TOUCH_DATA_X, count, data);
 143
 144                if (wm831x_ts->pressure)
 145                        input_report_abs(wm831x_ts->input_dev,
 146                                         ABS_PRESSURE, 0);
 147
 148                input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 0);
 149
 150                schedule_work(&wm831x_ts->pd_data_work);
 151        } else {
 152                input_report_key(wm831x_ts->input_dev, BTN_TOUCH, 1);
 153        }
 154
 155        input_sync(wm831x_ts->input_dev);
 156
 157        return IRQ_HANDLED;
 158}
 159
 160static irqreturn_t wm831x_ts_pen_down_irq(int irq, void *irq_data)
 161{
 162        struct wm831x_ts *wm831x_ts = irq_data;
 163        struct wm831x *wm831x = wm831x_ts->wm831x;
 164        int ena = 0;
 165
 166        if (wm831x_ts->pen_down)
 167                return IRQ_HANDLED;
 168
 169        disable_irq_nosync(wm831x_ts->pd_irq);
 170
 171        /* Start collecting data */
 172        if (wm831x_ts->pressure)
 173                ena |= WM831X_TCH_Z_ENA;
 174
 175        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
 176                        WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA,
 177                        WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA | ena);
 178
 179        wm831x_set_bits(wm831x, WM831X_INTERRUPT_STATUS_1,
 180                        WM831X_TCHPD_EINT, WM831X_TCHPD_EINT);
 181
 182        wm831x_ts->pen_down = true;
 183
 184        /* Switch from pen down to data */
 185        dev_dbg(wm831x->dev, "IRQ PD->DATA\n");
 186        schedule_work(&wm831x_ts->pd_data_work);
 187
 188        return IRQ_HANDLED;
 189}
 190
 191static int wm831x_ts_input_open(struct input_dev *idev)
 192{
 193        struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
 194        struct wm831x *wm831x = wm831x_ts->wm831x;
 195
 196        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
 197                        WM831X_TCH_ENA | WM831X_TCH_CVT_ENA |
 198                        WM831X_TCH_X_ENA | WM831X_TCH_Y_ENA |
 199                        WM831X_TCH_Z_ENA, WM831X_TCH_ENA);
 200
 201        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
 202                        WM831X_TCH_CVT_ENA, WM831X_TCH_CVT_ENA);
 203
 204        return 0;
 205}
 206
 207static void wm831x_ts_input_close(struct input_dev *idev)
 208{
 209        struct wm831x_ts *wm831x_ts = input_get_drvdata(idev);
 210        struct wm831x *wm831x = wm831x_ts->wm831x;
 211
 212        /* Shut the controller down, disabling all other functionality too */
 213        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
 214                        WM831X_TCH_ENA | WM831X_TCH_X_ENA |
 215                        WM831X_TCH_Y_ENA | WM831X_TCH_Z_ENA, 0);
 216
 217        /* Make sure any pending IRQs are done, the above will prevent
 218         * new ones firing.
 219         */
 220        synchronize_irq(wm831x_ts->data_irq);
 221        synchronize_irq(wm831x_ts->pd_irq);
 222
 223        /* Make sure the IRQ completion work is quiesced */
 224        flush_work(&wm831x_ts->pd_data_work);
 225
 226        /* If we ended up with the pen down then make sure we revert back
 227         * to pen detection state for the next time we start up.
 228         */
 229        if (wm831x_ts->pen_down) {
 230                disable_irq(wm831x_ts->data_irq);
 231                enable_irq(wm831x_ts->pd_irq);
 232                wm831x_ts->pen_down = false;
 233        }
 234}
 235
 236static int wm831x_ts_probe(struct platform_device *pdev)
 237{
 238        struct wm831x_ts *wm831x_ts;
 239        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
 240        struct wm831x_pdata *core_pdata = dev_get_platdata(pdev->dev.parent);
 241        struct wm831x_touch_pdata *pdata = NULL;
 242        struct input_dev *input_dev;
 243        int error, irqf;
 244
 245        if (core_pdata)
 246                pdata = core_pdata->touch;
 247
 248        wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
 249                                 GFP_KERNEL);
 250        input_dev = devm_input_allocate_device(&pdev->dev);
 251        if (!wm831x_ts || !input_dev) {
 252                error = -ENOMEM;
 253                goto err_alloc;
 254        }
 255
 256        wm831x_ts->wm831x = wm831x;
 257        wm831x_ts->input_dev = input_dev;
 258        INIT_WORK(&wm831x_ts->pd_data_work, wm831x_pd_data_work);
 259
 260        /*
 261         * If we have a direct IRQ use it, otherwise use the interrupt
 262         * from the WM831x IRQ controller.
 263         */
 264        wm831x_ts->data_irq = wm831x_irq(wm831x,
 265                                         platform_get_irq_byname(pdev,
 266                                                                 "TCHDATA"));
 267        if (pdata && pdata->data_irq)
 268                wm831x_ts->data_irq = pdata->data_irq;
 269
 270        wm831x_ts->pd_irq = wm831x_irq(wm831x,
 271                                       platform_get_irq_byname(pdev, "TCHPD"));
 272        if (pdata && pdata->pd_irq)
 273                wm831x_ts->pd_irq = pdata->pd_irq;
 274
 275        if (pdata)
 276                wm831x_ts->pressure = pdata->pressure;
 277        else
 278                wm831x_ts->pressure = true;
 279
 280        /* Five wire touchscreens can't report pressure */
 281        if (pdata && pdata->fivewire) {
 282                wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
 283                                WM831X_TCH_5WIRE, WM831X_TCH_5WIRE);
 284
 285                /* Pressure measurements are not possible for five wire mode */
 286                WARN_ON(pdata->pressure && pdata->fivewire);
 287                wm831x_ts->pressure = false;
 288        } else {
 289                wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
 290                                WM831X_TCH_5WIRE, 0);
 291        }
 292
 293        if (pdata) {
 294                switch (pdata->isel) {
 295                default:
 296                        dev_err(&pdev->dev, "Unsupported ISEL setting: %d\n",
 297                                pdata->isel);
 298                        /* Fall through */
 299                case 200:
 300                case 0:
 301                        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
 302                                        WM831X_TCH_ISEL, 0);
 303                        break;
 304                case 400:
 305                        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
 306                                        WM831X_TCH_ISEL, WM831X_TCH_ISEL);
 307                        break;
 308                }
 309        }
 310
 311        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_2,
 312                        WM831X_TCH_PDONLY, 0);
 313
 314        /* Default to 96 samples/sec */
 315        wm831x_set_bits(wm831x, WM831X_TOUCH_CONTROL_1,
 316                        WM831X_TCH_RATE_MASK, 6);
 317
 318        if (pdata && pdata->data_irqf)
 319                irqf = pdata->data_irqf;
 320        else
 321                irqf = IRQF_TRIGGER_HIGH;
 322
 323        error = request_threaded_irq(wm831x_ts->data_irq,
 324                                     NULL, wm831x_ts_data_irq,
 325                                     irqf | IRQF_ONESHOT,
 326                                     "Touchscreen data", wm831x_ts);
 327        if (error) {
 328                dev_err(&pdev->dev, "Failed to request data IRQ %d: %d\n",
 329                        wm831x_ts->data_irq, error);
 330                goto err_alloc;
 331        }
 332        disable_irq(wm831x_ts->data_irq);
 333
 334        if (pdata && pdata->pd_irqf)
 335                irqf = pdata->pd_irqf;
 336        else
 337                irqf = IRQF_TRIGGER_HIGH;
 338
 339        error = request_threaded_irq(wm831x_ts->pd_irq,
 340                                     NULL, wm831x_ts_pen_down_irq,
 341                                     irqf | IRQF_ONESHOT,
 342                                     "Touchscreen pen down", wm831x_ts);
 343        if (error) {
 344                dev_err(&pdev->dev, "Failed to request pen down IRQ %d: %d\n",
 345                        wm831x_ts->pd_irq, error);
 346                goto err_data_irq;
 347        }
 348
 349        /* set up touch configuration */
 350        input_dev->name = "WM831x touchscreen";
 351        input_dev->phys = "wm831x";
 352        input_dev->open = wm831x_ts_input_open;
 353        input_dev->close = wm831x_ts_input_close;
 354
 355        __set_bit(EV_ABS, input_dev->evbit);
 356        __set_bit(EV_KEY, input_dev->evbit);
 357        __set_bit(BTN_TOUCH, input_dev->keybit);
 358
 359        input_set_abs_params(input_dev, ABS_X, 0, 4095, 5, 0);
 360        input_set_abs_params(input_dev, ABS_Y, 0, 4095, 5, 0);
 361        if (wm831x_ts->pressure)
 362                input_set_abs_params(input_dev, ABS_PRESSURE, 0, 4095, 5, 0);
 363
 364        input_set_drvdata(input_dev, wm831x_ts);
 365        input_dev->dev.parent = &pdev->dev;
 366
 367        error = input_register_device(input_dev);
 368        if (error)
 369                goto err_pd_irq;
 370
 371        platform_set_drvdata(pdev, wm831x_ts);
 372        return 0;
 373
 374err_pd_irq:
 375        free_irq(wm831x_ts->pd_irq, wm831x_ts);
 376err_data_irq:
 377        free_irq(wm831x_ts->data_irq, wm831x_ts);
 378err_alloc:
 379
 380        return error;
 381}
 382
 383static int wm831x_ts_remove(struct platform_device *pdev)
 384{
 385        struct wm831x_ts *wm831x_ts = platform_get_drvdata(pdev);
 386
 387        free_irq(wm831x_ts->pd_irq, wm831x_ts);
 388        free_irq(wm831x_ts->data_irq, wm831x_ts);
 389
 390        return 0;
 391}
 392
 393static struct platform_driver wm831x_ts_driver = {
 394        .driver = {
 395                .name = "wm831x-touch",
 396                .owner = THIS_MODULE,
 397        },
 398        .probe = wm831x_ts_probe,
 399        .remove = wm831x_ts_remove,
 400};
 401module_platform_driver(wm831x_ts_driver);
 402
 403/* Module information */
 404MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 405MODULE_DESCRIPTION("WM831x PMIC touchscreen driver");
 406MODULE_LICENSE("GPL");
 407MODULE_ALIAS("platform:wm831x-touch");
 408
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.