• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
rpi_ts_poll(struct input_polled_dev * dev)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 
rpi_ts_dma_cleanup(void * data)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 
rpi_ts_probe(struct platform_device * pdev)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 = devm_rpi_firmware_get(&pdev->dev, 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 	if (error || touchbuf != 0) {
168 		dev_warn(dev, "Failed to set touchbuf, %d\n", error);
169 		return error;
170 	}
171 
172 	poll_dev = devm_input_allocate_polled_device(dev);
173 	if (!poll_dev) {
174 		dev_err(dev, "Failed to allocate input device\n");
175 		return -ENOMEM;
176 	}
177 	ts->poll_dev = poll_dev;
178 	input = poll_dev->input;
179 
180 	input->name = "raspberrypi-ts";
181 	input->id.bustype = BUS_HOST;
182 	poll_dev->poll_interval = RPI_TS_POLL_INTERVAL;
183 	poll_dev->poll = rpi_ts_poll;
184 	poll_dev->private = ts;
185 
186 	input_set_abs_params(input, ABS_MT_POSITION_X, 0,
187 			     RPI_TS_DEFAULT_WIDTH, 0, 0);
188 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
189 			     RPI_TS_DEFAULT_HEIGHT, 0, 0);
190 	touchscreen_parse_properties(input, true, &ts->prop);
191 
192 	error = input_mt_init_slots(input, RPI_TS_MAX_SUPPORTED_POINTS,
193 				    INPUT_MT_DIRECT);
194 	if (error) {
195 		dev_err(dev, "could not init mt slots, %d\n", error);
196 		return error;
197 	}
198 
199 	error = input_register_polled_device(poll_dev);
200 	if (error) {
201 		dev_err(dev, "could not register input device, %d\n", error);
202 		return error;
203 	}
204 
205 	return 0;
206 }
207 
208 static const struct of_device_id rpi_ts_match[] = {
209 	{ .compatible = "raspberrypi,firmware-ts", },
210 	{},
211 };
212 MODULE_DEVICE_TABLE(of, rpi_ts_match);
213 
214 static struct platform_driver rpi_ts_driver = {
215 	.driver = {
216 		.name   = "raspberrypi-ts",
217 		.of_match_table = rpi_ts_match,
218 	},
219 	.probe          = rpi_ts_probe,
220 };
221 module_platform_driver(rpi_ts_driver);
222 
223 MODULE_AUTHOR("Gordon Hollingworth");
224 MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
225 MODULE_DESCRIPTION("Raspberry Pi firmware based touchscreen driver");
226 MODULE_LICENSE("GPL v2");
227