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-polldev.h>
20 #include <linux/input/touchscreen.h>
21 #include <soc/bcm2835/raspberrypi-firmware.h>
23 #define RPI_TS_DEFAULT_WIDTH 800
24 #define RPI_TS_DEFAULT_HEIGHT 480
26 #define RPI_TS_MAX_SUPPORTED_POINTS 10
28 #define RPI_TS_FTS_TOUCH_DOWN 0
29 #define RPI_TS_FTS_TOUCH_CONTACT 2
31 #define RPI_TS_POLL_INTERVAL 17 /* 60fps */
33 #define RPI_TS_NPOINTS_REG_INVALIDATE 99
36 struct platform_device
*pdev
;
37 struct input_polled_dev
*poll_dev
;
38 struct touchscreen_properties prop
;
40 void __iomem
*fw_regs_va
;
41 dma_addr_t fw_regs_phys
;
55 u8 pressure
; /* Not supported */
56 u8 area
; /* Not supported */
57 } point
[RPI_TS_MAX_SUPPORTED_POINTS
];
60 static void rpi_ts_poll(struct input_polled_dev
*dev
)
62 struct input_dev
*input
= dev
->input
;
63 struct rpi_ts
*ts
= dev
->private;
64 struct rpi_ts_regs regs
;
72 memcpy_fromio(®s
, ts
->fw_regs_va
, sizeof(regs
));
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
79 iowrite8(RPI_TS_NPOINTS_REG_INVALIDATE
,
80 ts
->fw_regs_va
+ offsetof(struct rpi_ts_regs
, num_points
));
82 if (regs
.num_points
== RPI_TS_NPOINTS_REG_INVALIDATE
||
83 (regs
.num_points
== 0 && ts
->known_ids
== 0))
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;
92 modified_ids
|= BIT(touchid
);
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);
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
));
108 ts
->known_ids
= modified_ids
;
110 input_mt_sync_frame(input
);
114 static void rpi_ts_dma_cleanup(void *data
)
116 struct rpi_ts
*ts
= data
;
117 struct device
*dev
= &ts
->pdev
->dev
;
119 dma_free_coherent(dev
, PAGE_SIZE
, ts
->fw_regs_va
, ts
->fw_regs_phys
);
122 static int rpi_ts_probe(struct platform_device
*pdev
)
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
;
134 fw_node
= of_get_parent(np
);
136 dev_err(dev
, "Missing firmware node\n");
140 fw
= rpi_firmware_get(fw_node
);
141 of_node_put(fw_node
);
143 return -EPROBE_DEFER
;
145 ts
= devm_kzalloc(dev
, sizeof(*ts
), GFP_KERNEL
);
150 ts
->fw_regs_va
= dma_alloc_coherent(dev
, PAGE_SIZE
, &ts
->fw_regs_phys
,
152 if (!ts
->fw_regs_va
) {
153 dev_err(dev
, "failed to dma_alloc_coherent\n");
157 error
= devm_add_action_or_reset(dev
, rpi_ts_dma_cleanup
, ts
);
159 dev_err(dev
, "failed to devm_add_action_or_reset, %d\n", error
);
164 touchbuf
= (u32
)ts
->fw_regs_phys
;
165 error
= rpi_firmware_property(fw
, RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF
,
166 &touchbuf
, sizeof(touchbuf
));
168 if (error
|| touchbuf
!= 0) {
169 dev_warn(dev
, "Failed to set touchbuf, %d\n", error
);
173 poll_dev
= devm_input_allocate_polled_device(dev
);
175 dev_err(dev
, "Failed to allocate input device\n");
178 ts
->poll_dev
= poll_dev
;
179 input
= poll_dev
->input
;
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
;
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
);
193 error
= input_mt_init_slots(input
, RPI_TS_MAX_SUPPORTED_POINTS
,
196 dev_err(dev
, "could not init mt slots, %d\n", error
);
200 error
= input_register_polled_device(poll_dev
);
202 dev_err(dev
, "could not register input device, %d\n", error
);
209 static const struct of_device_id rpi_ts_match
[] = {
210 { .compatible
= "raspberrypi,firmware-ts", },
213 MODULE_DEVICE_TABLE(of
, rpi_ts_match
);
215 static struct platform_driver rpi_ts_driver
= {
217 .name
= "raspberrypi-ts",
218 .of_match_table
= rpi_ts_match
,
220 .probe
= rpi_ts_probe
,
222 module_platform_driver(rpi_ts_driver
);
224 MODULE_AUTHOR("Gordon Hollingworth");
225 MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
226 MODULE_DESCRIPTION("Raspberry Pi firmware based touchscreen driver");
227 MODULE_LICENSE("GPL v2");