bcm2835_ft5406.c revision 307767
11558Srgrimes/*-
21558Srgrimes * Copyright (C) 2016 Oleksandr Tymoshenko <gonzo@freebsd.org>
31558Srgrimes * All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes *
141558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241558Srgrimes * SUCH DAMAGE.
251558Srgrimes *
261558Srgrimes */
271558Srgrimes
281558Srgrimes#include <sys/cdefs.h>
291558Srgrimes__FBSDID("$FreeBSD: stable/11/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c 307767 2016-10-22 02:37:59Z gonzo $");
301558Srgrimes
311558Srgrimes#include <sys/param.h>
321558Srgrimes#include <sys/systm.h>
331558Srgrimes#include <sys/bus.h>
341558Srgrimes#include <sys/cpu.h>
357590Sbde#include <sys/kernel.h>
361558Srgrimes#include <sys/lock.h>
371558Srgrimes#include <sys/malloc.h>
381558Srgrimes#include <sys/module.h>
391558Srgrimes#include <sys/mutex.h>
401558Srgrimes#include <sys/condvar.h>
4136626Scharnier#include <sys/sysctl.h>
427590Sbde#include <sys/selinfo.h>
4336626Scharnier#include <sys/poll.h>
4436626Scharnier#include <sys/uio.h>
4537233Sbde#include <sys/conf.h>
461558Srgrimes#include <sys/kthread.h>
471558Srgrimes
481558Srgrimes#include <vm/vm.h>
491558Srgrimes#include <vm/pmap.h>
501558Srgrimes
511558Srgrimes#include <dev/fdt/fdt_common.h>
521558Srgrimes#include <dev/ofw/ofw_bus.h>
531558Srgrimes#include <dev/ofw/ofw_bus_subr.h>
541558Srgrimes
551558Srgrimes#include <dev/evdev/input.h>
561558Srgrimes#include <dev/evdev/evdev.h>
571558Srgrimes
581558Srgrimes#include <machine/bus.h>
591558Srgrimes#include <machine/cpu.h>
601558Srgrimes#include <machine/intr.h>
611558Srgrimes
621558Srgrimes#include <arm/broadcom/bcm2835/bcm2835_mbox.h>
631558Srgrimes#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>
6436626Scharnier#include <arm/broadcom/bcm2835/bcm2835_vcbus.h>
6518485Sbde
661558Srgrimes#include "mbox_if.h"
671558Srgrimes
681558Srgrimes#ifdef DEBUG
691558Srgrimes#define DPRINTF(fmt, ...) do {			\
701558Srgrimes	printf("%s:%u: ", __func__, __LINE__);	\
711558Srgrimes	printf(fmt, ##__VA_ARGS__);		\
721558Srgrimes} while (0)
731558Srgrimes#else
741558Srgrimes#define DPRINTF(fmt, ...)
751558Srgrimes#endif
761558Srgrimes
771558Srgrimes#define	FT5406_LOCK(_sc)		\
781558Srgrimes	mtx_lock(&(_sc)->sc_mtx)
791558Srgrimes#define	FT5406_UNLOCK(_sc)		\
801558Srgrimes	mtx_unlock(&(_sc)->sc_mtx)
811558Srgrimes#define	FT5406_LOCK_INIT(_sc)	\
821558Srgrimes	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
831558Srgrimes	    "ft5406", MTX_DEF)
841558Srgrimes#define	FT5406_LOCK_DESTROY(_sc)	\
851558Srgrimes	mtx_destroy(&_sc->sc_mtx);
861558Srgrimes#define	FT5406_LOCK_ASSERT(_sc)	\
871558Srgrimes	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
881558Srgrimes
891558Srgrimes#define	FT5406_DEVICE_MODE	0
901558Srgrimes#define	FT5406_GESTURE_ID	1
911558Srgrimes#define	FT5406_NUM_POINTS	2
9236626Scharnier#define	FT5406_POINT_XH(n)	(0 + 3 + (n)*6)
9336626Scharnier#define	FT5406_POINT_XL(n)	(1 + 3 + (n)*6)
9436626Scharnier#define	FT5406_POINT_YH(n)	(2 + 3 + (n)*6)
9536626Scharnier#define	FT5406_POINT_YL(n)	(3 + 3 + (n)*6)
9636626Scharnier#define	FT5406_WINDOW_SIZE	64
9736626Scharnier
9836626Scharnier#define	GET_NUM_POINTS(buf)	(buf[FT5406_NUM_POINTS])
991558Srgrimes#define	GET_X(buf, n)		(((buf[FT5406_POINT_XH(n)] & 0xf) << 8) | \
1001558Srgrimes				    (buf[FT5406_POINT_XL(n)]))
1011558Srgrimes#define	GET_Y(buf, n)		(((buf[FT5406_POINT_YH(n)] & 0xf) << 8) | \
1021558Srgrimes				    (buf[FT5406_POINT_YL(n)]))
1031558Srgrimes#define	GET_TOUCH_ID(buf, n)	((buf[FT5406_POINT_YH(n)] >> 4) & 0xf)
1047589Sbde
1051558Srgrimes#define	NO_POINTS	99
1061558Srgrimes#define	SCREEN_WIDTH	800
10718485Sbde#define	SCREEN_HEIGHT	480
1081558Srgrimes
1097589Sbdestruct ft5406ts_softc {
1107589Sbde	device_t		sc_dev;
1111558Srgrimes	struct mtx		sc_mtx;
11236626Scharnier	struct proc		*sc_worker;
11336626Scharnier
11436626Scharnier	/* mbox buffer (mapped to KVA) */
11536626Scharnier	uint8_t			*touch_buf;
1161558Srgrimes
11736626Scharnier	/* initial hook for waiting mbox intr */
11836626Scharnier	struct intr_config_hook	sc_init_hook;
1197589Sbde
1201558Srgrimes	struct evdev_dev	*sc_evdev;
1217589Sbde	int			sc_detaching;
12236626Scharnier};
12336626Scharnier
1241558Srgrimesstatic void
1251558Srgrimesft5406ts_worker(void *data)
1261558Srgrimes{
1271558Srgrimes	struct ft5406ts_softc *sc = (struct ft5406ts_softc *)data;
1281558Srgrimes	int points;
1291558Srgrimes	int id, new_x, new_y, i, new_pen_down, updated;
1307590Sbde	int x, y, pen_down;
13137233Sbde	uint8_t window[FT5406_WINDOW_SIZE];
1321558Srgrimes	int tick;
1331558Srgrimes
1347589Sbde	/* 60Hz */
1357589Sbde	tick = hz*17/1000;
1367589Sbde	if (tick == 0)
1377589Sbde		tick = 1;
1387589Sbde
1397589Sbde	x = y = -1;
14036626Scharnier	pen_down = 0;
14136626Scharnier
1421558Srgrimes	FT5406_LOCK(sc);
1431558Srgrimes	while(1) {
1441558Srgrimes		msleep(sc, &sc->sc_mtx, PCATCH | PZERO, "ft5406ts", tick);
1451558Srgrimes
1467589Sbde		if (sc->sc_detaching)
1471558Srgrimes			break;
1481558Srgrimes
1497589Sbde		memcpy(window, sc->touch_buf, sizeof(window));
1507589Sbde		sc->touch_buf[FT5406_NUM_POINTS] = NO_POINTS;
1517589Sbde
1527589Sbde		points = GET_NUM_POINTS(window);
1537589Sbde		/*
1547589Sbde		 * No update from VC - do nothing
1557589Sbde		 */
1567589Sbde		if (points == NO_POINTS)
1577590Sbde			continue;
15837233Sbde
1597589Sbde		/* No points and pen is already up */
1607589Sbde		if ((points == 0) && !pen_down)
1617589Sbde			continue;
16236626Scharnier
1631558Srgrimes		new_pen_down = 0;
1641558Srgrimes		for (i = 0; i < points; i++) {
1651558Srgrimes			id = GET_TOUCH_ID(window, 0);
1661558Srgrimes			/* For now consider only touch 0 */
1671558Srgrimes			if (id != 0)
1681558Srgrimes				continue;
1691558Srgrimes			new_pen_down = 1;
1701558Srgrimes			new_x = GET_X(window, 0);
1711558Srgrimes			new_y = GET_Y(window, 0);
1721558Srgrimes		}
1731558Srgrimes
1741558Srgrimes		updated = 0;
1751558Srgrimes
1761558Srgrimes		if (new_x != x) {
1771558Srgrimes			x = new_x;
1781558Srgrimes			updated = 1;
1791558Srgrimes		}
18037233Sbde
1811558Srgrimes		if (new_y != y) {
1821558Srgrimes			y = new_y;
1831558Srgrimes			updated = 1;
1841558Srgrimes		}
1851558Srgrimes
1867590Sbde		if (new_pen_down != pen_down) {
18737233Sbde			pen_down = new_pen_down;
1881558Srgrimes			updated = 1;
1891558Srgrimes		}
1901558Srgrimes
1911558Srgrimes		if (updated) {
1927590Sbde			evdev_push_event(sc->sc_evdev, EV_ABS, ABS_X, x);
19337233Sbde			evdev_push_event(sc->sc_evdev, EV_ABS, ABS_Y, y);
1941558Srgrimes			evdev_push_event(sc->sc_evdev, EV_KEY, BTN_TOUCH, pen_down);
1951558Srgrimes			evdev_sync(sc->sc_evdev);
1961558Srgrimes		}
1971558Srgrimes	}
1981558Srgrimes	FT5406_UNLOCK(sc);
1991558Srgrimes
2001558Srgrimes	kproc_exit(0);
2011558Srgrimes}
2021558Srgrimes
2031558Srgrimesstatic void
2041558Srgrimesft5406ts_init(void *arg)
2051558Srgrimes{
20637233Sbde	struct ft5406ts_softc *sc = arg;
2071558Srgrimes	struct bcm2835_mbox_tag_touchbuf msg;
2081558Srgrimes	uint32_t touchbuf;
2091558Srgrimes	int err;
2101558Srgrimes
2111558Srgrimes	/* release this hook (continue boot) */
2121558Srgrimes	config_intrhook_disestablish(&sc->sc_init_hook);
2131558Srgrimes
2141558Srgrimes	memset(&msg, 0, sizeof(msg));
2151558Srgrimes	msg.hdr.buf_size = sizeof(msg);
2161558Srgrimes	msg.hdr.code = BCM2835_MBOX_CODE_REQ;
2171558Srgrimes	msg.tag_hdr.tag = BCM2835_MBOX_TAG_GET_TOUCHBUF;
2181558Srgrimes	msg.tag_hdr.val_buf_size = sizeof(msg.body);
2191558Srgrimes	msg.tag_hdr.val_len = sizeof(msg.body);
2201558Srgrimes	msg.end_tag = 0;
2211558Srgrimes
22237233Sbde	/* call mailbox property */
22336626Scharnier	err = bcm2835_mbox_property(&msg, sizeof(msg));
2241558Srgrimes	if (err) {
2251558Srgrimes		device_printf(sc->sc_dev, "failed to get touchbuf address\n");
2261558Srgrimes		return;
22737233Sbde	}
22836626Scharnier
2291558Srgrimes	if (msg.body.resp.address == 0) {
2301558Srgrimes		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);
338