linux/drivers/input/touchscreen/raspberrypi-ts.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Raspberry Pi firmware based touchscreen driver
   4 *
   5 * Copyright (C) 2015, 2017 Raspberry Pi
   6 * Copyright (C) 2018 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
   7 */
   8
   9#include <linux/io.h>
  10#include <linux/of.h>
  11#include <linux/slab.h>
  12#include <linux/device.h>
  13#include <linux/module.h>
  14#include <linux/bitops.h>
  15#include <linux/dma-mapping.h>
  16#include <linux/platform_device.h>
  17#include <linux/input.h>
  18#include <linux/input/mt.h>
  19#include <linux/input-polldev.h>
  20#include <linux/input/touchscreen.h>
  21#include <soc/bcm2835/raspberrypi-firmware.h>
  22
  23#define RPI_TS_DEFAULT_WIDTH    800
  24#define RPI_TS_DEFAULT_HEIGHT   480
  25
  26#define RPI_TS_MAX_SUPPORTED_POINTS     10
  27
  28#define RPI_TS_FTS_TOUCH_DOWN           0
  29#define RPI_TS_FTS_TOUCH_CONTACT        2
  30
  31#define RPI_TS_POLL_INTERVAL            17      /* 60fps */
  32
  33#define RPI_TS_NPOINTS_REG_INVALIDATE   99
  34
  35struct rpi_ts {
  36        struct platform_device *pdev;
  37        struct input_polled_dev *poll_dev;
  38        struct touchscreen_properties prop;
  39
  40        void __iomem *fw_regs_va;
  41        dma_addr_t fw_regs_phys;
  42
  43        int known_ids;
  44};
  45
  46struct rpi_ts_regs {
  47        u8 device_mode;
  48        u8 gesture_id;
  49        u8 num_points;
  50        struct rpi_ts_touch {
  51                u8 xh;
  52                u8 xl;
  53                u8 yh;
  54                u8 yl;
  55                u8 pressure; /* Not supported */
  56                u8 area;     /* Not supported */
  57        } point[RPI_TS_MAX_SUPPORTED_POINTS];
  58};
  59
  60static void rpi_ts_poll(struct input_polled_dev *dev)
  61{
  62        struct input_dev *input = dev->input;
  63        struct rpi_ts *ts = dev->private;
  64        struct rpi_ts_regs regs;
  65        int modified_ids = 0;
  66        long released_ids;
  67        int event_type;
  68        int touchid;
  69        int x, y;
  70        int i;
  71
  72        memcpy_fromio(&regs, ts->fw_regs_va, sizeof(regs));
  73        /*
  74         * We poll the memory based register copy of the touchscreen chip using
  75         * the number of points register to know whether the copy has been
  76         * updated (we write 99 to the memory copy, the GPU will write between
  77         * 0 - 10 points)
  78         */
  79        iowrite8(RPI_TS_NPOINTS_REG_INVALIDATE,
  80                 ts->fw_regs_va + offsetof(struct rpi_ts_regs, num_points));
  81
  82        if (regs.num_points == RPI_TS_NPOINTS_REG_INVALIDATE ||
  83            (regs.num_points == 0 && ts->known_ids == 0))
  84                return;
  85
  86        for (i = 0; i < regs.num_points; i++) {
  87                x = (((int)regs.point[i].xh & 0xf) << 8) + regs.point[i].xl;
  88                y = (((int)regs.point[i].yh & 0xf) << 8) + regs.point[i].yl;
  89                touchid = (regs.point[i].yh >> 4) & 0xf;
  90                event_type = (regs.point[i].xh >> 6) & 0x03;
  91
  92                modified_ids |= BIT(touchid);
  93
  94                if (event_type == RPI_TS_FTS_TOUCH_DOWN ||
  95                    event_type == RPI_TS_FTS_TOUCH_CONTACT) {
  96                        input_mt_slot(input, touchid);
  97                        input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
  98                        touchscreen_report_pos(input, &ts->prop, x, y, true);
  99                }
 100        }
 101
 102        released_ids = ts->known_ids & ~modified_ids;
 103        for_each_set_bit(i, &released_ids, RPI_TS_MAX_SUPPORTED_POINTS) {
 104                input_mt_slot(input, i);
 105                input_mt_report_slot_state(input, MT_TOOL_FINGER, 0);
 106                modified_ids &= ~(BIT(i));
 107        }
 108        ts->known_ids = modified_ids;
 109
 110        input_mt_sync_frame(input);
 111        input_sync(input);
 112}
 113
 114static void rpi_ts_dma_cleanup(void *data)
 115{
 116        struct rpi_ts *ts = data;
 117        struct device *dev = &ts->pdev->dev;
 118
 119        dma_free_coherent(dev, PAGE_SIZE, ts->fw_regs_va, ts->fw_regs_phys);
 120}
 121
 122static int rpi_ts_probe(struct platform_device *pdev)
 123{
 124        struct device *dev = &pdev->dev;
 125        struct device_node *np = dev->of_node;
 126        struct input_polled_dev *poll_dev;
 127        struct device_node *fw_node;
 128        struct rpi_firmware *fw;
 129        struct input_dev *input;
 130        struct rpi_ts *ts;
 131        u32 touchbuf;
 132        int error;
 133
 134        fw_node = of_get_parent(np);
 135        if (!fw_node) {
 136                dev_err(dev, "Missing firmware node\n");
 137                return -ENOENT;
 138        }
 139
 140        fw = rpi_firmware_get(fw_node);
 141        of_node_put(fw_node);
 142        if (!fw)
 143                return -EPROBE_DEFER;
 144
 145        ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
 146        if (!ts)
 147                return -ENOMEM;
 148        ts->pdev = pdev;
 149
 150        ts->fw_regs_va = dma_alloc_coherent(dev, PAGE_SIZE, &ts->fw_regs_phys,
 151                                            GFP_KERNEL);
 152        if (!ts->fw_regs_va) {
 153                dev_err(dev, "failed to dma_alloc_coherent\n");
 154                return -ENOMEM;
 155        }
 156
 157        error = devm_add_action_or_reset(dev, rpi_ts_dma_cleanup, ts);
 158        if (error) {
 159                dev_err(dev, "failed to devm_add_action_or_reset, %d\n", error);
 160                return error;
 161        }
 162
 163
 164        touchbuf = (u32)ts->fw_regs_phys;
 165        error = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF,
 166                                      &touchbuf, sizeof(touchbuf));
 167
 168        if (error || touchbuf != 0) {
 169                dev_warn(dev, "Failed to set touchbuf, %d\n", error);
 170                return error;
 171        }
 172
 173        poll_dev = devm_input_allocate_polled_device(dev);
 174        if (!poll_dev) {
 175                dev_err(dev, "Failed to allocate input device\n");
 176                return -ENOMEM;
 177        }
 178        ts->poll_dev = poll_dev;
 179        input = poll_dev->input;
 180
 181        input->name = "raspberrypi-ts";
 182        input->id.bustype = BUS_HOST;
 183        poll_dev->poll_interval = RPI_TS_POLL_INTERVAL;
 184        poll_dev->poll = rpi_ts_poll;
 185        poll_dev->private = ts;
 186
 187        input_set_abs_params(input, ABS_MT_POSITION_X, 0,
 188                             RPI_TS_DEFAULT_WIDTH, 0, 0);
 189        input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
 190                             RPI_TS_DEFAULT_HEIGHT, 0, 0);
 191        touchscreen_parse_properties(input, true, &ts->prop);
 192
 193        error = input_mt_init_slots(input, RPI_TS_MAX_SUPPORTED_POINTS,
 194                                    INPUT_MT_DIRECT);
 195        if (error) {
 196                dev_err(dev, "could not init mt slots, %d\n", error);
 197                return error;
 198        }
 199
 200        error = input_register_polled_device(poll_dev);
 201        if (error) {
 202                dev_err(dev, "could not register input device, %d\n", error);
 203                return error;
 204        }
 205
 206        return 0;
 207}
 208
 209static const struct of_device_id rpi_ts_match[] = {
 210        { .compatible = "raspberrypi,firmware-ts", },
 211        {},
 212};
 213MODULE_DEVICE_TABLE(of, rpi_ts_match);
 214
 215static struct platform_driver rpi_ts_driver = {
 216        .driver = {
 217                .name   = "raspberrypi-ts",
 218                .of_match_table = rpi_ts_match,
 219        },
 220        .probe          = rpi_ts_probe,
 221};
 222module_platform_driver(rpi_ts_driver);
 223
 224MODULE_AUTHOR("Gordon Hollingworth");
 225MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
 226MODULE_DESCRIPTION("Raspberry Pi firmware based touchscreen driver");
 227MODULE_LICENSE("GPL v2");
 228