linux/drivers/input/touchscreen/ts4800-ts.c
<<
>>
Prefs
   1/*
   2 * Touchscreen driver for the TS-4800 board
   3 *
   4 * Copyright (c) 2015 - Savoir-faire Linux
   5 *
   6 * This file is licensed under the terms of the GNU General Public
   7 * License version 2. This program is licensed "as is" without any
   8 * warranty of any kind, whether express or implied.
   9 */
  10
  11#include <linux/bitops.h>
  12#include <linux/input.h>
  13#include <linux/io.h>
  14#include <linux/kernel.h>
  15#include <linux/mfd/syscon.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/platform_device.h>
  19#include <linux/regmap.h>
  20
  21/* polling interval in ms */
  22#define POLL_INTERVAL           3
  23
  24#define DEBOUNCE_COUNT          1
  25
  26/* sensor values are 12-bit wide */
  27#define MAX_12BIT               ((1 << 12) - 1)
  28
  29#define PENDOWN_MASK            0x1
  30
  31#define X_OFFSET                0x0
  32#define Y_OFFSET                0x2
  33
  34struct ts4800_ts {
  35        struct input_dev        *input;
  36        struct device           *dev;
  37        char                    phys[32];
  38
  39        void __iomem            *base;
  40        struct regmap           *regmap;
  41        unsigned int            reg;
  42        unsigned int            bit;
  43
  44        bool                    pendown;
  45        int                     debounce;
  46};
  47
  48static int ts4800_ts_open(struct input_dev *input_dev)
  49{
  50        struct ts4800_ts *ts = input_get_drvdata(input_dev);
  51        int error;
  52
  53        ts->pendown = false;
  54        ts->debounce = DEBOUNCE_COUNT;
  55
  56        error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
  57        if (error) {
  58                dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error);
  59                return error;
  60        }
  61
  62        return 0;
  63}
  64
  65static void ts4800_ts_close(struct input_dev *input_dev)
  66{
  67        struct ts4800_ts *ts = input_get_drvdata(input_dev);
  68        int ret;
  69
  70        ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
  71        if (ret)
  72                dev_warn(ts->dev, "Failed to disable touchscreen\n");
  73
  74}
  75
  76static void ts4800_ts_poll(struct input_dev *input_dev)
  77{
  78        struct ts4800_ts *ts = input_get_drvdata(input_dev);
  79        u16 last_x = readw(ts->base + X_OFFSET);
  80        u16 last_y = readw(ts->base + Y_OFFSET);
  81        bool pendown = last_x & PENDOWN_MASK;
  82
  83        if (pendown) {
  84                if (ts->debounce) {
  85                        ts->debounce--;
  86                        return;
  87                }
  88
  89                if (!ts->pendown) {
  90                        input_report_key(input_dev, BTN_TOUCH, 1);
  91                        ts->pendown = true;
  92                }
  93
  94                last_x = ((~last_x) >> 4) & MAX_12BIT;
  95                last_y = ((~last_y) >> 4) & MAX_12BIT;
  96
  97                input_report_abs(input_dev, ABS_X, last_x);
  98                input_report_abs(input_dev, ABS_Y, last_y);
  99                input_sync(input_dev);
 100        } else if (ts->pendown) {
 101                ts->pendown = false;
 102                ts->debounce = DEBOUNCE_COUNT;
 103                input_report_key(input_dev, BTN_TOUCH, 0);
 104                input_sync(input_dev);
 105        }
 106}
 107
 108static int ts4800_parse_dt(struct platform_device *pdev,
 109                           struct ts4800_ts *ts)
 110{
 111        struct device *dev = &pdev->dev;
 112        struct device_node *np = dev->of_node;
 113        struct device_node *syscon_np;
 114        u32 reg, bit;
 115        int error;
 116
 117        syscon_np = of_parse_phandle(np, "syscon", 0);
 118        if (!syscon_np) {
 119                dev_err(dev, "no syscon property\n");
 120                return -ENODEV;
 121        }
 122
 123        ts->regmap = syscon_node_to_regmap(syscon_np);
 124        of_node_put(syscon_np);
 125        if (IS_ERR(ts->regmap)) {
 126                dev_err(dev, "cannot get parent's regmap\n");
 127                return PTR_ERR(ts->regmap);
 128        }
 129
 130        error = of_property_read_u32_index(np, "syscon", 1, &reg);
 131        if (error < 0) {
 132                dev_err(dev, "no offset in syscon\n");
 133                return error;
 134        }
 135
 136        ts->reg = reg;
 137
 138        error = of_property_read_u32_index(np, "syscon", 2, &bit);
 139        if (error < 0) {
 140                dev_err(dev, "no bit in syscon\n");
 141                return error;
 142        }
 143
 144        ts->bit = BIT(bit);
 145
 146        return 0;
 147}
 148
 149static int ts4800_ts_probe(struct platform_device *pdev)
 150{
 151        struct input_dev *input_dev;
 152        struct ts4800_ts *ts;
 153        int error;
 154
 155        ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
 156        if (!ts)
 157                return -ENOMEM;
 158
 159        error = ts4800_parse_dt(pdev, ts);
 160        if (error)
 161                return error;
 162
 163        ts->base = devm_platform_ioremap_resource(pdev, 0);
 164        if (IS_ERR(ts->base))
 165                return PTR_ERR(ts->base);
 166
 167        input_dev = devm_input_allocate_device(&pdev->dev);
 168        if (!input_dev)
 169                return -ENOMEM;
 170
 171        snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
 172        ts->input = input_dev;
 173        ts->dev = &pdev->dev;
 174
 175        input_set_drvdata(input_dev, ts);
 176
 177        input_dev->name = "TS-4800 Touchscreen";
 178        input_dev->phys = ts->phys;
 179
 180        input_dev->open = ts4800_ts_open;
 181        input_dev->close = ts4800_ts_close;
 182
 183        input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
 184        input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
 185        input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
 186
 187        error = input_setup_polling(input_dev, ts4800_ts_poll);
 188        if (error) {
 189                dev_err(&pdev->dev, "Unable to set up polling: %d\n", error);
 190                return error;
 191        }
 192
 193        input_set_poll_interval(input_dev, POLL_INTERVAL);
 194
 195        error = input_register_device(input_dev);
 196        if (error) {
 197                dev_err(&pdev->dev,
 198                        "Unable to register input device: %d\n", error);
 199                return error;
 200        }
 201
 202        return 0;
 203}
 204
 205static const struct of_device_id ts4800_ts_of_match[] = {
 206        { .compatible = "technologic,ts4800-ts", },
 207        { },
 208};
 209MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
 210
 211static struct platform_driver ts4800_ts_driver = {
 212        .driver = {
 213                .name = "ts4800-ts",
 214                .of_match_table = ts4800_ts_of_match,
 215        },
 216        .probe = ts4800_ts_probe,
 217};
 218module_platform_driver(ts4800_ts_driver);
 219
 220MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
 221MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
 222MODULE_LICENSE("GPL v2");
 223MODULE_ALIAS("platform:ts4800_ts");
 224