linux/drivers/input/touchscreen/ti_am335x_tsc.c
<<
>>
Prefs
   1/*
   2 * TI Touch Screen driver
   3 *
   4 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation version 2.
   9 *
  10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  11 * kind, whether express or implied; without even the implied warranty
  12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15
  16
  17#include <linux/init.h>
  18#include <linux/kernel.h>
  19#include <linux/err.h>
  20#include <linux/module.h>
  21#include <linux/input.h>
  22#include <linux/slab.h>
  23#include <linux/interrupt.h>
  24#include <linux/clk.h>
  25#include <linux/platform_device.h>
  26#include <linux/io.h>
  27#include <linux/input/ti_am335x_tsc.h>
  28#include <linux/delay.h>
  29
  30#include <linux/mfd/ti_am335x_tscadc.h>
  31
  32#define ADCFSM_STEPID           0x10
  33#define SEQ_SETTLE              275
  34#define MAX_12BIT               ((1 << 12) - 1)
  35
  36struct titsc {
  37        struct input_dev        *input;
  38        struct ti_tscadc_dev    *mfd_tscadc;
  39        unsigned int            irq;
  40        unsigned int            wires;
  41        unsigned int            x_plate_resistance;
  42        bool                    pen_down;
  43        int                     steps_to_configure;
  44};
  45
  46static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
  47{
  48        return readl(ts->mfd_tscadc->tscadc_base + reg);
  49}
  50
  51static void titsc_writel(struct titsc *tsc, unsigned int reg,
  52                                        unsigned int val)
  53{
  54        writel(val, tsc->mfd_tscadc->tscadc_base + reg);
  55}
  56
  57static void titsc_step_config(struct titsc *ts_dev)
  58{
  59        unsigned int    config;
  60        int i, total_steps;
  61
  62        /* Configure the Step registers */
  63        total_steps = 2 * ts_dev->steps_to_configure;
  64
  65        config = STEPCONFIG_MODE_HWSYNC |
  66                        STEPCONFIG_AVG_16 | STEPCONFIG_XPP;
  67        switch (ts_dev->wires) {
  68        case 4:
  69                config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
  70                break;
  71        case 5:
  72                config |= STEPCONFIG_YNN |
  73                                STEPCONFIG_INP_AN4 | STEPCONFIG_XNN |
  74                                STEPCONFIG_YPP;
  75                break;
  76        case 8:
  77                config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN;
  78                break;
  79        }
  80
  81        for (i = 1; i <= ts_dev->steps_to_configure; i++) {
  82                titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
  83                titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
  84        }
  85
  86        config = 0;
  87        config = STEPCONFIG_MODE_HWSYNC |
  88                        STEPCONFIG_AVG_16 | STEPCONFIG_YNN |
  89                        STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1;
  90        switch (ts_dev->wires) {
  91        case 4:
  92                config |= STEPCONFIG_YPP;
  93                break;
  94        case 5:
  95                config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 |
  96                                STEPCONFIG_XNP | STEPCONFIG_YPN;
  97                break;
  98        case 8:
  99                config |= STEPCONFIG_YPP;
 100                break;
 101        }
 102
 103        for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) {
 104                titsc_writel(ts_dev, REG_STEPCONFIG(i), config);
 105                titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
 106        }
 107
 108        config = 0;
 109        /* Charge step configuration */
 110        config = STEPCONFIG_XPP | STEPCONFIG_YNN |
 111                        STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR |
 112                        STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1;
 113
 114        titsc_writel(ts_dev, REG_CHARGECONFIG, config);
 115        titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY);
 116
 117        config = 0;
 118        /* Configure to calculate pressure */
 119        config = STEPCONFIG_MODE_HWSYNC |
 120                        STEPCONFIG_AVG_16 | STEPCONFIG_YPP |
 121                        STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM;
 122        titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config);
 123        titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1),
 124                        STEPCONFIG_OPENDLY);
 125
 126        config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1;
 127        titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config);
 128        titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2),
 129                        STEPCONFIG_OPENDLY);
 130
 131        titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC);
 132}
 133
 134static void titsc_read_coordinates(struct titsc *ts_dev,
 135                                    unsigned int *x, unsigned int *y)
 136{
 137        unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT);
 138        unsigned int prev_val_x = ~0, prev_val_y = ~0;
 139        unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
 140        unsigned int read, diff;
 141        unsigned int i, channel;
 142
 143        /*
 144         * Delta filter is used to remove large variations in sampled
 145         * values from ADC. The filter tries to predict where the next
 146         * coordinate could be. This is done by taking a previous
 147         * coordinate and subtracting it form current one. Further the
 148         * algorithm compares the difference with that of a present value,
 149         * if true the value is reported to the sub system.
 150         */
 151        for (i = 0; i < fifocount - 1; i++) {
 152                read = titsc_readl(ts_dev, REG_FIFO0);
 153                channel = read & 0xf0000;
 154                channel = channel >> 0x10;
 155                if ((channel >= 0) && (channel < ts_dev->steps_to_configure)) {
 156                        read &= 0xfff;
 157                        diff = abs(read - prev_val_x);
 158                        if (diff < prev_diff_x) {
 159                                prev_diff_x = diff;
 160                                *x = read;
 161                        }
 162                        prev_val_x = read;
 163                }
 164
 165                read = titsc_readl(ts_dev, REG_FIFO1);
 166                channel = read & 0xf0000;
 167                channel = channel >> 0x10;
 168                if ((channel >= ts_dev->steps_to_configure) &&
 169                        (channel < (2 * ts_dev->steps_to_configure - 1))) {
 170                        read &= 0xfff;
 171                        diff = abs(read - prev_val_y);
 172                        if (diff < prev_diff_y) {
 173                                prev_diff_y = diff;
 174                                *y = read;
 175                        }
 176                        prev_val_y = read;
 177                }
 178        }
 179}
 180
 181static irqreturn_t titsc_irq(int irq, void *dev)
 182{
 183        struct titsc *ts_dev = dev;
 184        struct input_dev *input_dev = ts_dev->input;
 185        unsigned int status, irqclr = 0;
 186        unsigned int x = 0, y = 0;
 187        unsigned int z1, z2, z;
 188        unsigned int fsm;
 189        unsigned int fifo1count, fifo0count;
 190        int i;
 191
 192        status = titsc_readl(ts_dev, REG_IRQSTATUS);
 193        if (status & IRQENB_FIFO0THRES) {
 194                titsc_read_coordinates(ts_dev, &x, &y);
 195
 196                z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff;
 197                z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff;
 198
 199                fifo1count = titsc_readl(ts_dev, REG_FIFO1CNT);
 200                for (i = 0; i < fifo1count; i++)
 201                        titsc_readl(ts_dev, REG_FIFO1);
 202
 203                fifo0count = titsc_readl(ts_dev, REG_FIFO0CNT);
 204                for (i = 0; i < fifo0count; i++)
 205                        titsc_readl(ts_dev, REG_FIFO0);
 206
 207                if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
 208                        /*
 209                         * Calculate pressure using formula
 210                         * Resistance(touch) = x plate resistance *
 211                         * x postion/4096 * ((z2 / z1) - 1)
 212                         */
 213                        z = z2 - z1;
 214                        z *= x;
 215                        z *= ts_dev->x_plate_resistance;
 216                        z /= z1;
 217                        z = (z + 2047) >> 12;
 218
 219                        if (z <= MAX_12BIT) {
 220                                input_report_abs(input_dev, ABS_X, x);
 221                                input_report_abs(input_dev, ABS_Y, y);
 222                                input_report_abs(input_dev, ABS_PRESSURE, z);
 223                                input_report_key(input_dev, BTN_TOUCH, 1);
 224                                input_sync(input_dev);
 225                        }
 226                }
 227                irqclr |= IRQENB_FIFO0THRES;
 228        }
 229
 230        /*
 231         * Time for sequencer to settle, to read
 232         * correct state of the sequencer.
 233         */
 234        udelay(SEQ_SETTLE);
 235
 236        status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
 237        if (status & IRQENB_PENUP) {
 238                /* Pen up event */
 239                fsm = titsc_readl(ts_dev, REG_ADCFSM);
 240                if (fsm == ADCFSM_STEPID) {
 241                        ts_dev->pen_down = false;
 242                        input_report_key(input_dev, BTN_TOUCH, 0);
 243                        input_report_abs(input_dev, ABS_PRESSURE, 0);
 244                        input_sync(input_dev);
 245                } else {
 246                        ts_dev->pen_down = true;
 247                }
 248                irqclr |= IRQENB_PENUP;
 249        }
 250
 251        titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
 252
 253        titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC);
 254        return IRQ_HANDLED;
 255}
 256
 257/*
 258 * The functions for inserting/removing driver as a module.
 259 */
 260
 261static int titsc_probe(struct platform_device *pdev)
 262{
 263        struct titsc *ts_dev;
 264        struct input_dev *input_dev;
 265        struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
 266        struct mfd_tscadc_board *pdata;
 267        int err;
 268
 269        pdata = tscadc_dev->dev->platform_data;
 270
 271        if (!pdata) {
 272                dev_err(&pdev->dev, "Could not find platform data\n");
 273                return -EINVAL;
 274        }
 275
 276        /* Allocate memory for device */
 277        ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL);
 278        input_dev = input_allocate_device();
 279        if (!ts_dev || !input_dev) {
 280                dev_err(&pdev->dev, "failed to allocate memory.\n");
 281                err = -ENOMEM;
 282                goto err_free_mem;
 283        }
 284
 285        tscadc_dev->tsc = ts_dev;
 286        ts_dev->mfd_tscadc = tscadc_dev;
 287        ts_dev->input = input_dev;
 288        ts_dev->irq = tscadc_dev->irq;
 289        ts_dev->wires = pdata->tsc_init->wires;
 290        ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance;
 291        ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure;
 292
 293        err = request_irq(ts_dev->irq, titsc_irq,
 294                          0, pdev->dev.driver->name, ts_dev);
 295        if (err) {
 296                dev_err(&pdev->dev, "failed to allocate irq.\n");
 297                goto err_free_mem;
 298        }
 299
 300        titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
 301        titsc_step_config(ts_dev);
 302        titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure);
 303
 304        input_dev->name = "ti-tsc";
 305        input_dev->dev.parent = &pdev->dev;
 306
 307        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 308        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
 309
 310        input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
 311        input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
 312        input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
 313
 314        /* register to the input system */
 315        err = input_register_device(input_dev);
 316        if (err)
 317                goto err_free_irq;
 318
 319        platform_set_drvdata(pdev, ts_dev);
 320        return 0;
 321
 322err_free_irq:
 323        free_irq(ts_dev->irq, ts_dev);
 324err_free_mem:
 325        input_free_device(input_dev);
 326        kfree(ts_dev);
 327        return err;
 328}
 329
 330static int titsc_remove(struct platform_device *pdev)
 331{
 332        struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
 333        struct titsc *ts_dev = tscadc_dev->tsc;
 334
 335        free_irq(ts_dev->irq, ts_dev);
 336
 337        input_unregister_device(ts_dev->input);
 338
 339        platform_set_drvdata(pdev, NULL);
 340        kfree(ts_dev);
 341        return 0;
 342}
 343
 344#ifdef CONFIG_PM
 345static int titsc_suspend(struct device *dev)
 346{
 347        struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
 348        struct titsc *ts_dev = tscadc_dev->tsc;
 349        unsigned int idle;
 350
 351        if (device_may_wakeup(tscadc_dev->dev)) {
 352                idle = titsc_readl(ts_dev, REG_IRQENABLE);
 353                titsc_writel(ts_dev, REG_IRQENABLE,
 354                                (idle | IRQENB_HW_PEN));
 355                titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
 356        }
 357        return 0;
 358}
 359
 360static int titsc_resume(struct device *dev)
 361{
 362        struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
 363        struct titsc *ts_dev = tscadc_dev->tsc;
 364
 365        if (device_may_wakeup(tscadc_dev->dev)) {
 366                titsc_writel(ts_dev, REG_IRQWAKEUP,
 367                                0x00);
 368                titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
 369        }
 370        titsc_step_config(ts_dev);
 371        titsc_writel(ts_dev, REG_FIFO0THR,
 372                        ts_dev->steps_to_configure);
 373        return 0;
 374}
 375
 376static const struct dev_pm_ops titsc_pm_ops = {
 377        .suspend = titsc_suspend,
 378        .resume  = titsc_resume,
 379};
 380#define TITSC_PM_OPS (&titsc_pm_ops)
 381#else
 382#define TITSC_PM_OPS NULL
 383#endif
 384
 385static struct platform_driver ti_tsc_driver = {
 386        .probe  = titsc_probe,
 387        .remove = titsc_remove,
 388        .driver = {
 389                .name   = "tsc",
 390                .owner  = THIS_MODULE,
 391                .pm     = TITSC_PM_OPS,
 392        },
 393};
 394module_platform_driver(ti_tsc_driver);
 395
 396MODULE_DESCRIPTION("TI touchscreen controller driver");
 397MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
 398MODULE_LICENSE("GPL");
 399
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.