linux/drivers/input/touchscreen/da9052_tsi.c
<<
>>
Prefs
   1/*
   2 * TSI driver for Dialog DA9052
   3 *
   4 * Copyright(c) 2012 Dialog Semiconductor Ltd.
   5 *
   6 * Author: David Dajun Chen <dchen@diasemi.com>
   7 *
   8 *  This program is free software; you can redistribute  it and/or modify it
   9 *  under  the terms of  the GNU General  Public License as published by the
  10 *  Free Software Foundation;  either version 2 of the  License, or (at your
  11 *  option) any later version.
  12 *
  13 */
  14#include <linux/module.h>
  15#include <linux/input.h>
  16#include <linux/delay.h>
  17#include <linux/platform_device.h>
  18#include <linux/interrupt.h>
  19
  20#include <linux/mfd/da9052/reg.h>
  21#include <linux/mfd/da9052/da9052.h>
  22
  23#define TSI_PEN_DOWN_STATUS 0x40
  24
  25struct da9052_tsi {
  26        struct da9052 *da9052;
  27        struct input_dev *dev;
  28        struct delayed_work ts_pen_work;
  29        bool stopped;
  30        bool adc_on;
  31};
  32
  33static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on)
  34{
  35        da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on);
  36        tsi->adc_on = on;
  37}
  38
  39static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
  40{
  41        struct da9052_tsi *tsi = data;
  42
  43        if (!tsi->stopped) {
  44                /* Mask PEN_DOWN event and unmask TSI_READY event */
  45                da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN);
  46                da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
  47
  48                da9052_ts_adc_toggle(tsi, true);
  49
  50                schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
  51        }
  52
  53        return IRQ_HANDLED;
  54}
  55
  56static void da9052_ts_read(struct da9052_tsi *tsi)
  57{
  58        struct input_dev *input = tsi->dev;
  59        int ret;
  60        u16 x, y, z;
  61        u8 v;
  62
  63        ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG);
  64        if (ret < 0)
  65                return;
  66
  67        x = (u16) ret;
  68
  69        ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG);
  70        if (ret < 0)
  71                return;
  72
  73        y = (u16) ret;
  74
  75        ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG);
  76        if (ret < 0)
  77                return;
  78
  79        z = (u16) ret;
  80
  81        ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
  82        if (ret < 0)
  83                return;
  84
  85        v = (u8) ret;
  86
  87        x = ((x << 2) & 0x3fc) | (v & 0x3);
  88        y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2);
  89        z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4);
  90
  91        input_report_key(input, BTN_TOUCH, 1);
  92        input_report_abs(input, ABS_X, x);
  93        input_report_abs(input, ABS_Y, y);
  94        input_report_abs(input, ABS_PRESSURE, z);
  95        input_sync(input);
  96}
  97
  98static irqreturn_t da9052_ts_datardy_irq(int irq, void *data)
  99{
 100        struct da9052_tsi *tsi = data;
 101
 102        da9052_ts_read(tsi);
 103
 104        return IRQ_HANDLED;
 105}
 106
 107static void da9052_ts_pen_work(struct work_struct *work)
 108{
 109        struct da9052_tsi *tsi = container_of(work, struct da9052_tsi,
 110                                              ts_pen_work.work);
 111        if (!tsi->stopped) {
 112                int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
 113                if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) {
 114                        /* Pen is still DOWN (or read error) */
 115                        schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
 116                } else {
 117                        struct input_dev *input = tsi->dev;
 118
 119                        /* Pen UP */
 120                        da9052_ts_adc_toggle(tsi, false);
 121
 122                        /* Report Pen UP */
 123                        input_report_key(input, BTN_TOUCH, 0);
 124                        input_report_abs(input, ABS_PRESSURE, 0);
 125                        input_sync(input);
 126
 127                        /*
 128                         * FIXME: Fixes the unhandled irq issue when quick
 129                         * pen down and pen up events occurs
 130                         */
 131                        ret = da9052_reg_update(tsi->da9052,
 132                                                DA9052_EVENT_B_REG, 0xC0, 0xC0);
 133                        if (ret < 0)
 134                                return;
 135
 136                        /* Mask TSI_READY event and unmask PEN_DOWN event */
 137                        da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
 138                        da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
 139                }
 140        }
 141}
 142
 143static int da9052_ts_configure_gpio(struct da9052 *da9052)
 144{
 145        int error;
 146
 147        error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0);
 148        if (error < 0)
 149                return error;
 150
 151        error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0);
 152        if (error < 0)
 153                return error;
 154
 155        error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0);
 156        if (error < 0)
 157                return error;
 158
 159        return 0;
 160}
 161
 162static int da9052_configure_tsi(struct da9052_tsi *tsi)
 163{
 164        int error;
 165
 166        error = da9052_ts_configure_gpio(tsi->da9052);
 167        if (error)
 168                return error;
 169
 170        /* Measure TSI sample every 1ms */
 171        error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG,
 172                                  1 << 6, 1 << 6);
 173        if (error < 0)
 174                return error;
 175
 176        /* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */
 177        error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0);
 178        if (error < 0)
 179                return error;
 180
 181        /* Supply TSIRef through LD09 */
 182        error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59);
 183        if (error < 0)
 184                return error;
 185
 186        return 0;
 187}
 188
 189static int da9052_ts_input_open(struct input_dev *input_dev)
 190{
 191        struct da9052_tsi *tsi = input_get_drvdata(input_dev);
 192
 193        tsi->stopped = false;
 194        mb();
 195
 196        /* Unmask PEN_DOWN event */
 197        da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
 198
 199        /* Enable Pen Detect Circuit */
 200        return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
 201                                 1 << 1, 1 << 1);
 202}
 203
 204static void da9052_ts_input_close(struct input_dev *input_dev)
 205{
 206        struct da9052_tsi *tsi = input_get_drvdata(input_dev);
 207
 208        tsi->stopped = true;
 209        mb();
 210        da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
 211        cancel_delayed_work_sync(&tsi->ts_pen_work);
 212
 213        if (tsi->adc_on) {
 214                da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
 215                da9052_ts_adc_toggle(tsi, false);
 216
 217                /*
 218                 * If ADC was on that means that pendwn IRQ was disabled
 219                 * twice and we need to enable it to keep enable/disable
 220                 * counter balanced. IRQ is still off though.
 221                 */
 222                da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
 223        }
 224
 225        /* Disable Pen Detect Circuit */
 226        da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
 227}
 228
 229static int da9052_ts_probe(struct platform_device *pdev)
 230{
 231        struct da9052 *da9052;
 232        struct da9052_tsi *tsi;
 233        struct input_dev *input_dev;
 234        int error;
 235
 236        da9052 = dev_get_drvdata(pdev->dev.parent);
 237        if (!da9052)
 238                return -EINVAL;
 239
 240        tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
 241        input_dev = input_allocate_device();
 242        if (!tsi || !input_dev) {
 243                error = -ENOMEM;
 244                goto err_free_mem;
 245        }
 246
 247        tsi->da9052 = da9052;
 248        tsi->dev = input_dev;
 249        tsi->stopped = true;
 250        INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
 251
 252        input_dev->id.version = 0x0101;
 253        input_dev->id.vendor = 0x15B6;
 254        input_dev->id.product = 0x9052;
 255        input_dev->name = "Dialog DA9052 TouchScreen Driver";
 256        input_dev->dev.parent = &pdev->dev;
 257        input_dev->open = da9052_ts_input_open;
 258        input_dev->close = da9052_ts_input_close;
 259
 260        __set_bit(EV_ABS, input_dev->evbit);
 261        __set_bit(EV_KEY, input_dev->evbit);
 262        __set_bit(BTN_TOUCH, input_dev->keybit);
 263
 264        input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
 265        input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
 266        input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0);
 267
 268        input_set_drvdata(input_dev, tsi);
 269
 270        /* Disable Pen Detect Circuit */
 271        da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
 272
 273        /* Disable ADC */
 274        da9052_ts_adc_toggle(tsi, false);
 275
 276        error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN,
 277                                "pendown-irq", da9052_ts_pendwn_irq, tsi);
 278        if (error) {
 279                dev_err(tsi->da9052->dev,
 280                        "Failed to register PENDWN IRQ: %d\n", error);
 281                goto err_free_mem;
 282        }
 283
 284        error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY,
 285                                "tsiready-irq", da9052_ts_datardy_irq, tsi);
 286        if (error) {
 287                dev_err(tsi->da9052->dev,
 288                        "Failed to register TSIRDY IRQ :%d\n", error);
 289                goto err_free_pendwn_irq;
 290        }
 291
 292        /* Mask PEN_DOWN and TSI_READY events */
 293        da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN);
 294        da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY);
 295
 296        error = da9052_configure_tsi(tsi);
 297        if (error)
 298                goto err_free_datardy_irq;
 299
 300        error = input_register_device(tsi->dev);
 301        if (error)
 302                goto err_free_datardy_irq;
 303
 304        platform_set_drvdata(pdev, tsi);
 305
 306        return 0;
 307
 308err_free_datardy_irq:
 309        da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
 310err_free_pendwn_irq:
 311        da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
 312err_free_mem:
 313        kfree(tsi);
 314        input_free_device(input_dev);
 315
 316        return error;
 317}
 318
 319static int  da9052_ts_remove(struct platform_device *pdev)
 320{
 321        struct da9052_tsi *tsi = platform_get_drvdata(pdev);
 322
 323        da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
 324
 325        da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi);
 326        da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi);
 327
 328        input_unregister_device(tsi->dev);
 329        kfree(tsi);
 330
 331        return 0;
 332}
 333
 334static struct platform_driver da9052_tsi_driver = {
 335        .probe  = da9052_ts_probe,
 336        .remove = da9052_ts_remove,
 337        .driver = {
 338                .name   = "da9052-tsi",
 339        },
 340};
 341
 342module_platform_driver(da9052_tsi_driver);
 343
 344MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
 345MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
 346MODULE_LICENSE("GPL");
 347MODULE_ALIAS("platform:da9052-tsi");
 348