Skip to content

Commit 0b9f28f

Browse files
Nicolas Saenz Juliennedtor
authored andcommitted
Input: add official Raspberry Pi's touchscreen driver
Add's support to Raspberry Pi's 7" Touch device. Instead of using a conventional bus all information is copied into a memory mapped area by RPi's firmware. Based on the driver found in RPi's kernel repository. Signed-off-by: Nicolas Saenz Julienne <[email protected]> Reviewed-by: Rob Herring <[email protected]> Acked-by: Eric Anholt <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]>
1 parent 4d8f727 commit 0b9f28f

File tree

4 files changed

+266
-0
lines changed

4 files changed

+266
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Raspberry Pi firmware based 7" touchscreen
2+
=====================================
3+
4+
Required properties:
5+
- compatible: "raspberrypi,firmware-ts"
6+
7+
Optional properties:
8+
- firmware: Reference to RPi's firmware device node
9+
- touchscreen-size-x: See touchscreen.txt
10+
- touchscreen-size-y: See touchscreen.txt
11+
- touchscreen-inverted-x: See touchscreen.txt
12+
- touchscreen-inverted-y: See touchscreen.txt
13+
- touchscreen-swapped-x-y: See touchscreen.txt
14+
15+
Example:
16+
17+
firmware: firmware-rpi {
18+
compatible = "raspberrypi,bcm2835-firmware";
19+
mboxes = <&mailbox>;
20+
21+
ts: touchscreen {
22+
compatible = "raspberrypi,firmware-ts";
23+
touchscreen-size-x = <800>;
24+
touchscreen-size-y = <480>;
25+
};
26+
};

drivers/input/touchscreen/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,18 @@ config TOUCHSCREEN_EDT_FT5X06
683683
To compile this driver as a module, choose M here: the
684684
module will be called edt-ft5x06.
685685

686+
config TOUCHSCREEN_RASPBERRYPI_FW
687+
tristate "Raspberry Pi's firmware base touch screen support"
688+
depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST
689+
help
690+
Say Y here if you have the official Raspberry Pi 7 inch screen on
691+
your system.
692+
693+
If unsure, say N.
694+
695+
To compile this driver as a module, choose M here: the
696+
module will be called raspberrypi-ts.
697+
686698
config TOUCHSCREEN_MIGOR
687699
tristate "Renesas MIGO-R touchscreen"
688700
depends on (SH_MIGOR || COMPILE_TEST) && I2C

drivers/input/touchscreen/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,4 @@ obj-$(CONFIG_TOUCHSCREEN_ZET6223) += zet6223.o
108108
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
109109
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
110110
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
111+
obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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 <[email protected]>
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+
35+
struct 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+
46+
struct 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+
60+
static 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+
114+
static 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+
122+
static 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_zalloc_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+
209+
static const struct of_device_id rpi_ts_match[] = {
210+
{ .compatible = "raspberrypi,firmware-ts", },
211+
{},
212+
};
213+
MODULE_DEVICE_TABLE(of, rpi_ts_match);
214+
215+
static 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+
};
222+
module_platform_driver(rpi_ts_driver);
223+
224+
MODULE_AUTHOR("Gordon Hollingworth");
225+
MODULE_AUTHOR("Nicolas Saenz Julienne <[email protected]>");
226+
MODULE_DESCRIPTION("Raspberry Pi firmware based touchscreen driver");
227+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)