xref: /linux/drivers/input/touchscreen/raspberrypi-ts.c (revision dd5b2498d845f925904cb2afabb6ba11bfc317c5)
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 <nsaenzjulienne@suse.de>
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_alloc_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 <nsaenzjulienne@suse.de>");
226 MODULE_DESCRIPTION("Raspberry Pi firmware based touchscreen driver");
227 MODULE_LICENSE("GPL v2");
228