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