1 // SPDX-License-Identifier: GPL-2.0
3 * Raspberry Pi firmware based touchscreen driver
5 * Copyright (C) 2015, 2017 Raspberry Pi
6 * Copyright (C) 2018 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
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/touchscreen.h>
20 #include <soc/bcm2835/raspberrypi-firmware.h>
22 #define RPI_TS_DEFAULT_WIDTH 800
23 #define RPI_TS_DEFAULT_HEIGHT 480
25 #define RPI_TS_MAX_SUPPORTED_POINTS 10
27 #define RPI_TS_FTS_TOUCH_DOWN 0
28 #define RPI_TS_FTS_TOUCH_CONTACT 2
30 #define RPI_TS_POLL_INTERVAL 17 /* 60fps */
32 #define RPI_TS_NPOINTS_REG_INVALIDATE 99
35 struct platform_device
*pdev
;
36 struct input_dev
*input
;
37 struct touchscreen_properties prop
;
39 void __iomem
*fw_regs_va
;
40 dma_addr_t fw_regs_phys
;
54 u8 pressure
; /* Not supported */
55 u8 area
; /* Not supported */
56 } point
[RPI_TS_MAX_SUPPORTED_POINTS
];
59 static void rpi_ts_poll(struct input_dev
*input
)
61 struct rpi_ts
*ts
= input_get_drvdata(input
);
62 struct rpi_ts_regs regs
;
70 memcpy_fromio(®s
, ts
->fw_regs_va
, sizeof(regs
));
72 * We poll the memory based register copy of the touchscreen chip using
73 * the number of points register to know whether the copy has been
74 * updated (we write 99 to the memory copy, the GPU will write between
77 iowrite8(RPI_TS_NPOINTS_REG_INVALIDATE
,
78 ts
->fw_regs_va
+ offsetof(struct rpi_ts_regs
, num_points
));
80 if (regs
.num_points
== RPI_TS_NPOINTS_REG_INVALIDATE
||
81 (regs
.num_points
== 0 && ts
->known_ids
== 0))
84 for (i
= 0; i
< regs
.num_points
; i
++) {
85 x
= (((int)regs
.point
[i
].xh
& 0xf) << 8) + regs
.point
[i
].xl
;
86 y
= (((int)regs
.point
[i
].yh
& 0xf) << 8) + regs
.point
[i
].yl
;
87 touchid
= (regs
.point
[i
].yh
>> 4) & 0xf;
88 event_type
= (regs
.point
[i
].xh
>> 6) & 0x03;
90 modified_ids
|= BIT(touchid
);
92 if (event_type
== RPI_TS_FTS_TOUCH_DOWN
||
93 event_type
== RPI_TS_FTS_TOUCH_CONTACT
) {
94 input_mt_slot(input
, touchid
);
95 input_mt_report_slot_state(input
, MT_TOOL_FINGER
, 1);
96 touchscreen_report_pos(input
, &ts
->prop
, x
, y
, true);
100 released_ids
= ts
->known_ids
& ~modified_ids
;
101 for_each_set_bit(i
, &released_ids
, RPI_TS_MAX_SUPPORTED_POINTS
) {
102 input_mt_slot(input
, i
);
103 input_mt_report_slot_inactive(input
);
104 modified_ids
&= ~(BIT(i
));
106 ts
->known_ids
= modified_ids
;
108 input_mt_sync_frame(input
);
112 static void rpi_ts_dma_cleanup(void *data
)
114 struct rpi_ts
*ts
= data
;
115 struct device
*dev
= &ts
->pdev
->dev
;
117 dma_free_coherent(dev
, PAGE_SIZE
, ts
->fw_regs_va
, ts
->fw_regs_phys
);
120 static int rpi_ts_probe(struct platform_device
*pdev
)
122 struct device
*dev
= &pdev
->dev
;
123 struct device_node
*np
= dev
->of_node
;
124 struct input_dev
*input
;
125 struct device_node
*fw_node
;
126 struct rpi_firmware
*fw
;
131 fw_node
= of_get_parent(np
);
133 dev_err(dev
, "Missing firmware node\n");
137 fw
= rpi_firmware_get(fw_node
);
138 of_node_put(fw_node
);
140 return -EPROBE_DEFER
;
142 ts
= devm_kzalloc(dev
, sizeof(*ts
), GFP_KERNEL
);
147 ts
->fw_regs_va
= dma_alloc_coherent(dev
, PAGE_SIZE
, &ts
->fw_regs_phys
,
149 if (!ts
->fw_regs_va
) {
150 dev_err(dev
, "failed to dma_alloc_coherent\n");
154 error
= devm_add_action_or_reset(dev
, rpi_ts_dma_cleanup
, ts
);
156 dev_err(dev
, "failed to devm_add_action_or_reset, %d\n", error
);
160 touchbuf
= (u32
)ts
->fw_regs_phys
;
161 error
= rpi_firmware_property(fw
, RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF
,
162 &touchbuf
, sizeof(touchbuf
));
164 if (error
|| touchbuf
!= 0) {
165 dev_warn(dev
, "Failed to set touchbuf, %d\n", error
);
169 input
= devm_input_allocate_device(dev
);
171 dev_err(dev
, "Failed to allocate input device\n");
176 input_set_drvdata(input
, ts
);
178 input
->name
= "raspberrypi-ts";
179 input
->id
.bustype
= BUS_HOST
;
181 input_set_abs_params(input
, ABS_MT_POSITION_X
, 0,
182 RPI_TS_DEFAULT_WIDTH
, 0, 0);
183 input_set_abs_params(input
, ABS_MT_POSITION_Y
, 0,
184 RPI_TS_DEFAULT_HEIGHT
, 0, 0);
185 touchscreen_parse_properties(input
, true, &ts
->prop
);
187 error
= input_mt_init_slots(input
, RPI_TS_MAX_SUPPORTED_POINTS
,
190 dev_err(dev
, "could not init mt slots, %d\n", error
);
194 error
= input_setup_polling(input
, rpi_ts_poll
);
196 dev_err(dev
, "could not set up polling mode, %d\n", error
);
200 input_set_poll_interval(input
, RPI_TS_POLL_INTERVAL
);
202 error
= input_register_device(input
);
204 dev_err(dev
, "could not register input device, %d\n", error
);
211 static const struct of_device_id rpi_ts_match
[] = {
212 { .compatible
= "raspberrypi,firmware-ts", },
215 MODULE_DEVICE_TABLE(of
, rpi_ts_match
);
217 static struct platform_driver rpi_ts_driver
= {
219 .name
= "raspberrypi-ts",
220 .of_match_table
= rpi_ts_match
,
222 .probe
= rpi_ts_probe
,
224 module_platform_driver(rpi_ts_driver
);
226 MODULE_AUTHOR("Gordon Hollingworth");
227 MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
228 MODULE_DESCRIPTION("Raspberry Pi firmware based touchscreen driver");
229 MODULE_LICENSE("GPL v2");