From 9bac591e8848831ed44d73ebdc1b29d9d5a30af6 Mon Sep 17 00:00:00 2001 From: James Hughes Date: Thu, 16 Nov 2017 15:56:17 +0000 Subject: [PATCH] Tidy up of the ft5406 driver to use DT (#2189) Driver was using a fixed resolution, this commit adds touchscreen size, and coordinate flip and swap features via device tree overlays. Adds overrides so the VC4 can adjust the DT parameters appropriately; there is a newer version of the VC4 side driver that can now set up the appropriate DT values if required. Signed-off-by: James Hughes --- arch/arm/boot/dts/overlays/README | 8 +- .../boot/dts/overlays/rpi-ft5406-overlay.dts | 13 ++ drivers/input/touchscreen/rpi-ft5406.c | 218 ++++++++++++------ 3 files changed, 164 insertions(+), 75 deletions(-) diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 708d4e4baa8ed..608b68fb7dad2 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -1299,8 +1299,12 @@ Params: speed Display SPI bus speed Name: rpi-ft5406 Info: Official Raspberry Pi display touchscreen -Load: dtoverlay=rpi-ft5406 -Params: +Load: dtoverlay=rpi-ft5406,= +Params: touchscreen-size-x Touchscreen X resolution (default 800) + touchscreen-size-y Touchscreen Y resolution (default 600); + touchscreen-inverted-x Invert touchscreen X coordinates (default 0); + touchscreen-inverted-y Invert touchscreen Y coordinates (default 0); + touchscreen-swapped-x-y Swap X and Y cordinates (default 0); Name: rpi-proto diff --git a/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts b/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts index 2e53a17491706..d4607b9d1cbfc 100644 --- a/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts +++ b/arch/arm/boot/dts/overlays/rpi-ft5406-overlay.dts @@ -11,7 +11,20 @@ compatible = "rpi,rpi-ft5406"; firmware = <&firmware>; status = "okay"; + touchscreen-size-x = <800>; + touchscreen-size-y = <600>; + touchscreen-inverted-x = <0>; + touchscreen-inverted-y = <0>; + touchscreen-swapped-x-y = <0>; }; }; }; + + __overrides__ { + touchscreen-size-x = <&rpi_ft5406>,"touchscreen-size-x:0"; + touchscreen-size-y = <&rpi_ft5406>,"touchscreen-size-y:0"; + touchscreen-inverted-x = <&rpi_ft5406>,"touchscreen-inverted-x:0"; + touchscreen-inverted-y = <&rpi_ft5406>,"touchscreen-inverted-y:0"; + touchscreen-swapped-x-y = <&rpi_ft5406>,"touchscreen-swapped-x-y:0"; + }; }; diff --git a/drivers/input/touchscreen/rpi-ft5406.c b/drivers/input/touchscreen/rpi-ft5406.c index 9d7d05482355d..40bbde9ce1bc4 100644 --- a/drivers/input/touchscreen/rpi-ft5406.c +++ b/drivers/input/touchscreen/rpi-ft5406.c @@ -1,7 +1,7 @@ /* * Driver for memory based ft5406 touchscreen * - * Copyright (C) 2015 Raspberry Pi + * Copyright (C) 2015, 2017 Raspberry Pi * * * This program is free software; you can redistribute it and/or modify @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ - #include #include #include @@ -21,11 +20,15 @@ #include #include #include -#include +#include #include #include #define MAXIMUM_SUPPORTED_POINTS 10 +#define FTS_TOUCH_DOWN 0 +#define FTS_TOUCH_UP 1 +#define FTS_TOUCH_CONTACT 2 + struct ft5406_regs { uint8_t device_mode; uint8_t gesture_id; @@ -35,85 +38,125 @@ struct ft5406_regs { uint8_t xl; uint8_t yh; uint8_t yl; - uint8_t res1; - uint8_t res2; + uint8_t pressure; /* Not supported */ + uint8_t area; /* Not supported */ } point[MAXIMUM_SUPPORTED_POINTS]; }; -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 480 +/* These are defaults if the DT entries are missing */ +#define DEFAULT_SCREEN_WIDTH 800 +#define DEFAULT_SCREEN_HEIGHT 480 struct ft5406 { - struct platform_device * pdev; - struct input_dev * input_dev; - void __iomem * ts_base; - dma_addr_t bus_addr; - struct task_struct * thread; + struct platform_device *pdev; + struct input_dev *input_dev; + void __iomem *ts_base; + dma_addr_t bus_addr; + struct task_struct *thread; + + uint16_t max_x; + uint16_t max_y; + uint8_t hflip; + uint8_t vflip; + uint8_t xyswap; }; /* Thread to poll for touchscreen events - * + * * This thread polls the memory based register copy of the ft5406 registers * using the number of points register to know whether the copy has been - * updated (we write 99 to the memory copy, the GPU will write between + * updated (we write 99 to the memory copy, the GPU will write between * 0 - 10 points) */ +#define ID_TO_BIT(a) (1 << a) + static int ft5406_thread(void *arg) { struct ft5406 *ts = (struct ft5406 *) arg; struct ft5406_regs regs; int known_ids = 0; - - while(!kthread_should_stop()) - { - // 60fps polling + + while (!kthread_should_stop()) { + /* 60fps polling */ msleep_interruptible(17); memcpy_fromio(®s, ts->ts_base, sizeof(struct ft5406_regs)); - iowrite8(99, ts->ts_base + offsetof(struct ft5406_regs, num_points)); - // Do not output if theres no new information (num_points is 99) - // or we have no touch points and don't need to release any - if(!(regs.num_points == 99 || (regs.num_points == 0 && known_ids == 0))) - { + iowrite8(99, + ts->ts_base + + offsetof(struct ft5406_regs, num_points)); + + /* + * Do not output if theres no new information (num_points is 99) + * or we have no touch points and don't need to release any + */ + if (!(regs.num_points == 99 || + (regs.num_points == 0 && known_ids == 0))) { int i; int modified_ids = 0, released_ids; - for(i = 0; i < regs.num_points; i++) - { - int x = (((int) regs.point[i].xh & 0xf) << 8) + regs.point[i].xl; - int y = (((int) regs.point[i].yh & 0xf) << 8) + regs.point[i].yl; - int touchid = (regs.point[i].yh >> 4) & 0xf; - - modified_ids |= 1 << touchid; - - if(!((1 << touchid) & known_ids)) - dev_dbg(&ts->pdev->dev, "x = %d, y = %d, touchid = %d\n", x, y, touchid); - - input_mt_slot(ts->input_dev, touchid); - input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); - - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x); - input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y); + for (i = 0; i < regs.num_points; i++) { + int x = (((int) regs.point[i].xh & 0xf) << 8) + + regs.point[i].xl; + int y = (((int) regs.point[i].yh & 0xf) << 8) + + regs.point[i].yl; + int touchid = (regs.point[i].yh >> 4) & 0xf; + int event_type = (regs.point[i].xh >> 6) & 0x03; + + modified_ids |= ID_TO_BIT(touchid); + + if (event_type == FTS_TOUCH_DOWN || + event_type == FTS_TOUCH_CONTACT) { + if (ts->hflip) + x = ts->max_x - 1 - x; + + if (ts->vflip) + y = ts->max_y - 1 - y; + + if (ts->xyswap) + swap(x, y); + + if (!((ID_TO_BIT(touchid)) & known_ids)) + dev_dbg(&ts->pdev->dev, + "x = %d, y = %d, press = %d, touchid = %d\n", + x, y, + regs.point[i].pressure, + touchid); + + input_mt_slot(ts->input_dev, touchid); + input_mt_report_slot_state( + ts->input_dev, + MT_TOOL_FINGER, + 1); + + input_report_abs(ts->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(ts->input_dev, + ABS_MT_POSITION_Y, y); + } } released_ids = known_ids & ~modified_ids; - for(i = 0; released_ids && i < MAXIMUM_SUPPORTED_POINTS; i++) - { - if(released_ids & (1<pdev->dev, "Released %d, known = %x modified = %x\n", i, known_ids, modified_ids); + for (i = 0; + released_ids && i < MAXIMUM_SUPPORTED_POINTS; + i++) { + if (released_ids & (1<pdev->dev, + "Released %d, known = %x, modified = %x\n", + i, known_ids, modified_ids); input_mt_slot(ts->input_dev, i); - input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); - modified_ids &= ~(1 << i); + input_mt_report_slot_state( + ts->input_dev, + MT_TOOL_FINGER, + 0); + modified_ids &= ~(ID_TO_BIT(i)); } } known_ids = modified_ids; - + input_mt_report_pointer_emulation(ts->input_dev, true); input_sync(ts->input_dev); } - } - + return 0; } @@ -122,13 +165,14 @@ static int ft5406_probe(struct platform_device *pdev) int err = 0; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct ft5406 * ts; + struct ft5406 *ts; struct device_node *fw_node; struct rpi_firmware *fw; u32 touchbuf; - + u32 val; + dev_info(dev, "Probing device\n"); - + fw_node = of_parse_phandle(np, "firmware", 0); if (!fw_node) { dev_err(dev, "Missing firmware node\n"); @@ -151,7 +195,8 @@ static int ft5406_probe(struct platform_device *pdev) return -ENOMEM; } - ts->ts_base = dma_zalloc_coherent(dev, PAGE_SIZE, &ts->bus_addr, GFP_KERNEL); + ts->ts_base = dma_zalloc_coherent(dev, PAGE_SIZE, &ts->bus_addr, + GFP_KERNEL); if (!ts->ts_base) { pr_err("[%s]: failed to dma_alloc_coherent(%ld)\n", __func__, PAGE_SIZE); @@ -164,17 +209,22 @@ static int ft5406_probe(struct platform_device *pdev) &touchbuf, sizeof(touchbuf)); if (err || touchbuf != 0) { - dev_warn(dev, "Failed to set touchbuf, trying to get err:%x\n", err); + dev_warn(dev, "Failed to set touchbuf, trying to get err:%x\n", + err); dma_free_coherent(dev, PAGE_SIZE, ts->ts_base, ts->bus_addr); ts->ts_base = 0; ts->bus_addr = 0; } if (!ts->ts_base) { - dev_warn(dev, "set failed, trying get (err:%d touchbuf:%x virt:%p bus:%x)\n", err, touchbuf, ts->ts_base, ts->bus_addr); - - err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF, - &touchbuf, sizeof(touchbuf)); + dev_warn(dev, + "set failed, trying get (err:%d touchbuf:%x virt:%p bus:%x)\n", + err, touchbuf, ts->ts_base, ts->bus_addr); + + err = rpi_firmware_property( + fw, + RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF, + &touchbuf, sizeof(touchbuf)); if (err) { dev_err(dev, "Failed to get touch buffer\n"); goto out; @@ -188,11 +238,10 @@ static int ft5406_probe(struct platform_device *pdev) dev_dbg(dev, "Got TS buffer 0x%x\n", touchbuf); - // mmap the physical memory + /* mmap the physical memory */ touchbuf &= ~0xc0000000; ts->ts_base = ioremap(touchbuf, sizeof(struct ft5406_regs)); - if (ts->ts_base == NULL) - { + if (ts->ts_base == NULL) { dev_err(dev, "Failed to map physical address\n"); err = -ENOMEM; goto out; @@ -200,22 +249,46 @@ static int ft5406_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, ts); ts->pdev = pdev; - + ts->input_dev->name = "FT5406 memory based driver"; - + + if (of_property_read_u32(np, "touchscreen-size-x", &val) >= 0) + ts->max_x = val; + else + ts->max_x = DEFAULT_SCREEN_WIDTH; + + if (of_property_read_u32(np, "touchscreen-size-y", &val) >= 0) + ts->max_y = val; + else + ts->max_y = DEFAULT_SCREEN_HEIGHT; + + if (of_property_read_u32(np, "touchscreen-inverted-x", &val) >= 0) + ts->hflip = val; + + if (of_property_read_u32(np, "touchscreen-inverted-y", &val) >= 0) + ts->vflip = val; + + if (of_property_read_u32(np, "touchscreen-swapped-x-y", &val) >= 0) + ts->xyswap = val; + + dev_dbg(dev, + "Touchscreen parameters (%d,%d), hflip=%d, vflip=%d, xyswap=%d", + ts->max_x, ts->max_y, ts->hflip, ts->vflip, ts->xyswap); + __set_bit(EV_KEY, ts->input_dev->evbit); __set_bit(EV_SYN, ts->input_dev->evbit); __set_bit(EV_ABS, ts->input_dev->evbit); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, - SCREEN_WIDTH, 0, 0); + ts->xyswap ? ts->max_y : ts->max_x, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, - SCREEN_HEIGHT, 0, 0); + ts->xyswap ? ts->max_x : ts->max_y, 0, 0); - input_mt_init_slots(ts->input_dev, MAXIMUM_SUPPORTED_POINTS, INPUT_MT_DIRECT); + input_mt_init_slots(ts->input_dev, + MAXIMUM_SUPPORTED_POINTS, INPUT_MT_DIRECT); input_set_drvdata(ts->input_dev, ts); - + err = input_register_device(ts->input_dev); if (err) { dev_err(dev, "could not register input device, %d\n", @@ -223,10 +296,9 @@ static int ft5406_probe(struct platform_device *pdev) goto out; } - // create thread to poll the touch events + /* create thread that polls the touch events */ ts->thread = kthread_run(ft5406_thread, ts, "ft5406"); - if(ts->thread == NULL) - { + if (ts->thread == NULL) { dev_err(dev, "Failed to create kernel thread"); err = -ENOMEM; goto out; @@ -254,9 +326,9 @@ static int ft5406_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ft5406 *ts = (struct ft5406 *) platform_get_drvdata(pdev); - + dev_info(dev, "Removing rpi-ft5406\n"); - + kthread_stop(ts->thread); if (ts->bus_addr) @@ -265,7 +337,7 @@ static int ft5406_remove(struct platform_device *pdev) iounmap(ts->ts_base); if (ts->input_dev) input_unregister_device(ts->input_dev); - + return 0; }