bcm2835_ft5406.c revision 307775
1/*-
2 * Copyright (C) 2016 Oleksandr Tymoshenko <gonzo@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: stable/11/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c 307775 2016-10-22 15:26:32Z gonzo $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/bus.h>
34#include <sys/cpu.h>
35#include <sys/kernel.h>
36#include <sys/lock.h>
37#include <sys/malloc.h>
38#include <sys/module.h>
39#include <sys/mutex.h>
40#include <sys/condvar.h>
41#include <sys/sysctl.h>
42#include <sys/selinfo.h>
43#include <sys/poll.h>
44#include <sys/uio.h>
45#include <sys/conf.h>
46#include <sys/kthread.h>
47
48#include <vm/vm.h>
49#include <vm/pmap.h>
50
51#include <dev/fdt/fdt_common.h>
52#include <dev/ofw/ofw_bus.h>
53#include <dev/ofw/ofw_bus_subr.h>
54
55#include <dev/evdev/input.h>
56#include <dev/evdev/evdev.h>
57
58#include <machine/bus.h>
59#include <machine/cpu.h>
60#include <machine/intr.h>
61
62#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
63#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
64#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
65
66#include "mbox_if.h"
67
68#ifdef DEBUG
69#define DPRINTF(fmt, ...) do {			\
70	printf("%s:%u: ", __func__, __LINE__);	\
71	printf(fmt, ##__VA_ARGS__);		\
72} while (0)
73#else
74#define DPRINTF(fmt, ...)
75#endif
76
77#define	FT5406_LOCK(_sc)		\
78	mtx_lock(&(_sc)->sc_mtx)
79#define	FT5406_UNLOCK(_sc)		\
80	mtx_unlock(&(_sc)->sc_mtx)
81#define	FT5406_LOCK_INIT(_sc)	\
82	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
83	    "ft5406", MTX_DEF)
84#define	FT5406_LOCK_DESTROY(_sc)	\
85	mtx_destroy(&_sc->sc_mtx);
86#define	FT5406_LOCK_ASSERT(_sc)	\
87	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
88
89#define	FT5406_DEVICE_MODE	0
90#define	FT5406_GESTURE_ID	1
91#define	FT5406_NUM_POINTS	2
92#define	FT5406_POINT_XH(n)	(0 + 3 + (n)*6)
93#define	FT5406_POINT_XL(n)	(1 + 3 + (n)*6)
94#define	FT5406_POINT_YH(n)	(2 + 3 + (n)*6)
95#define	FT5406_POINT_YL(n)	(3 + 3 + (n)*6)
96#define	FT5406_WINDOW_SIZE	64
97
98#define	GET_NUM_POINTS(buf)	(buf[FT5406_NUM_POINTS])
99#define	GET_X(buf, n)		(((buf[FT5406_POINT_XH(n)] & 0xf) << 8) | \
100				    (buf[FT5406_POINT_XL(n)]))
101#define	GET_Y(buf, n)		(((buf[FT5406_POINT_YH(n)] & 0xf) << 8) | \
102				    (buf[FT5406_POINT_YL(n)]))
103#define	GET_TOUCH_ID(buf, n)	((buf[FT5406_POINT_YH(n)] >> 4) & 0xf)
104
105#define	NO_POINTS	99
106#define	SCREEN_WIDTH	800
107#define	SCREEN_HEIGHT	480
108
109struct ft5406ts_softc {
110	device_t		sc_dev;
111	struct mtx		sc_mtx;
112	struct proc		*sc_worker;
113
114	/* mbox buffer (mapped to KVA) */
115	uint8_t			*touch_buf;
116
117	/* initial hook for waiting mbox intr */
118	struct intr_config_hook	sc_init_hook;
119
120	struct evdev_dev	*sc_evdev;
121	int			sc_detaching;
122};
123
124static void
125ft5406ts_worker(void *data)
126{
127	struct ft5406ts_softc *sc = (struct ft5406ts_softc *)data;
128	int points;
129	int id, new_x, new_y, i, new_pen_down, updated;
130	int x, y, pen_down;
131	uint8_t window[FT5406_WINDOW_SIZE];
132	int tick;
133
134	/* 60Hz */
135	tick = hz*17/1000;
136	if (tick == 0)
137		tick = 1;
138
139	x = y = -1;
140	pen_down = 0;
141
142	FT5406_LOCK(sc);
143	while(1) {
144		msleep(sc, &sc->sc_mtx, PCATCH | PZERO, "ft5406ts", tick);
145
146		if (sc->sc_detaching)
147			break;
148
149		memcpy(window, sc->touch_buf, sizeof(window));
150		sc->touch_buf[FT5406_NUM_POINTS] = NO_POINTS;
151
152		points = GET_NUM_POINTS(window);
153		/*
154		 * No update from VC - do nothing
155		 */
156		if (points == NO_POINTS)
157			continue;
158
159		/* No points and pen is already up */
160		if ((points == 0) && !pen_down)
161			continue;
162
163		new_pen_down = 0;
164		for (i = 0; i < points; i++) {
165			id = GET_TOUCH_ID(window, 0);
166			/* For now consider only touch 0 */
167			if (id != 0)
168				continue;
169			new_pen_down = 1;
170			new_x = GET_X(window, 0);
171			new_y = GET_Y(window, 0);
172		}
173
174		updated = 0;
175
176		if (new_x != x) {
177			x = new_x;
178			updated = 1;
179		}
180
181		if (new_y != y) {
182			y = new_y;
183			updated = 1;
184		}
185
186		if (new_pen_down != pen_down) {
187			pen_down = new_pen_down;
188			updated = 1;
189		}
190
191		if (updated) {
192			evdev_push_event(sc->sc_evdev, EV_ABS, ABS_X, x);
193			evdev_push_event(sc->sc_evdev, EV_ABS, ABS_Y, y);
194			evdev_push_event(sc->sc_evdev, EV_KEY, BTN_TOUCH, pen_down);
195			evdev_sync(sc->sc_evdev);
196		}
197	}
198	FT5406_UNLOCK(sc);
199
200	kproc_exit(0);
201}
202
203static void
204ft5406ts_init(void *arg)
205{
206	struct ft5406ts_softc *sc = arg;
207	struct bcm2835_mbox_tag_touchbuf msg;
208	uint32_t touchbuf;
209	int err;
210
211	/* release this hook (continue boot) */
212	config_intrhook_disestablish(&sc->sc_init_hook);
213
214	memset(&msg, 0, sizeof(msg));
215	msg.hdr.buf_size = sizeof(msg);
216	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
217	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TOUCHBUF;
218	msg.tag_hdr.val_buf_size = sizeof(msg.body);
219	msg.tag_hdr.val_len = sizeof(msg.body);
220	msg.end_tag = 0;
221
222	/* call mailbox property */
223	err = bcm2835_mbox_property(&msg, sizeof(msg));
224	if (err) {
225		device_printf(sc->sc_dev, "failed to get touchbuf address\n");
226		return;
227	}
228
229	if (msg.body.resp.address == 0) {
230		device_printf(sc->sc_dev, "touchscreen not detected\n");
231		return;
232	}
233
234	touchbuf = VCBUS_TO_PHYS(msg.body.resp.address);
235	sc->touch_buf = (uint8_t*)pmap_mapdev(touchbuf, FT5406_WINDOW_SIZE);
236
237	sc->sc_evdev = evdev_alloc();
238	evdev_set_name(sc->sc_evdev, device_get_desc(sc->sc_dev));
239	evdev_set_phys(sc->sc_evdev, device_get_nameunit(sc->sc_dev));
240	evdev_set_id(sc->sc_evdev, BUS_VIRTUAL, 0, 0, 0);
241	evdev_support_prop(sc->sc_evdev, INPUT_PROP_DIRECT);
242	evdev_support_event(sc->sc_evdev, EV_SYN);
243	evdev_support_event(sc->sc_evdev, EV_ABS);
244	evdev_support_event(sc->sc_evdev, EV_KEY);
245
246	evdev_support_abs(sc->sc_evdev, ABS_X, 0, 0,
247	    SCREEN_WIDTH, 0, 0, 0);
248	evdev_support_abs(sc->sc_evdev, ABS_Y, 0, 0,
249	    SCREEN_HEIGHT, 0, 0, 0);
250
251	evdev_support_key(sc->sc_evdev, BTN_TOUCH);
252
253	err = evdev_register(sc->sc_evdev);
254	if (err) {
255		evdev_free(sc->sc_evdev);
256		return;
257	}
258
259	sc->touch_buf[FT5406_NUM_POINTS] = NO_POINTS;
260	if (kproc_create(ft5406ts_worker, (void*)sc, &sc->sc_worker, 0, 0,
261	    "ft5406ts_worker") != 0) {
262		printf("failed to create ft5406ts_worker\n");
263	}
264}
265
266static int
267ft5406ts_probe(device_t dev)
268{
269
270	if (!ofw_bus_is_compatible(dev, "rpi,rpi-ft5406"))
271		return (ENXIO);
272
273	device_set_desc(dev, "FT5406 touchscreen (VC memory interface)");
274
275	return (BUS_PROBE_DEFAULT);
276}
277
278static int
279ft5406ts_attach(device_t dev)
280{
281	struct ft5406ts_softc *sc;
282
283	/* set self dev */
284	sc = device_get_softc(dev);
285	sc->sc_dev = dev;
286
287	/* register callback for using mbox when interrupts are enabled */
288	sc->sc_init_hook.ich_func = ft5406ts_init;
289	sc->sc_init_hook.ich_arg = sc;
290
291	if (config_intrhook_establish(&sc->sc_init_hook) != 0) {
292		device_printf(dev, "config_intrhook_establish failed\n");
293		return (ENOMEM);
294	}
295
296	FT5406_LOCK_INIT(sc);
297
298	return (0);
299}
300
301static int
302ft5406ts_detach(device_t dev)
303{
304	struct ft5406ts_softc *sc;
305
306	sc = device_get_softc(dev);
307
308	FT5406_LOCK(sc);
309	if (sc->sc_worker)
310		sc->sc_detaching = 1;
311
312	if (sc->sc_evdev)
313		evdev_free(sc->sc_evdev);
314	FT5406_UNLOCK(sc);
315
316	FT5406_LOCK_DESTROY(sc);
317
318	return (0);
319}
320
321static device_method_t ft5406ts_methods[] = {
322	/* Device interface */
323	DEVMETHOD(device_probe,		ft5406ts_probe),
324	DEVMETHOD(device_attach,	ft5406ts_attach),
325	DEVMETHOD(device_detach,	ft5406ts_detach),
326
327	DEVMETHOD_END
328};
329
330static devclass_t ft5406ts_devclass;
331static driver_t ft5406ts_driver = {
332	"ft5406ts",
333	ft5406ts_methods,
334	sizeof(struct ft5406ts_softc),
335};
336
337DRIVER_MODULE(ft5406ts, ofwbus, ft5406ts_driver, ft5406ts_devclass, 0, 0);
338MODULE_DEPEND(ft5406ts, evdev, 1, 1, 1);
339