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