summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-05-02 22:01:22 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2016-06-01 23:08:17 +0200
commitb2afd931e2d83ce346811a68a34ee56c48be6d35 (patch)
tree58a32e570889406d0805d70054b97c99b257c8e8 /firmware
parent4d42e3685c55a6de9d05003f8482f7fb1be022fd (diff)
downloadrockbox-b2afd931e2d83ce346811a68a34ee56c48be6d35.tar.gz
rockbox-b2afd931e2d83ce346811a68a34ee56c48be6d35.zip
fuze+: rewrite touchpad driver
The old driver was bad in many respect, it had some race conditions, it was using a thread to serialize transfers because of the legacy i2c interface. It also had huge latency (typically 50ms but delays up to 300ms can happen), thus some presses were missed. The new driver takes advantage of the new i2c driver to do everything asynchronously. It also does not need a thread anymore because queueing ensures proper serialization. It provides much better and reliable latency (typically ~2ms). Also fix the debug screen which was horribly broken. The new screen also displays the deadzones. Change-Id: I69b7f99b75053e6b1d3d56beb4453c004fd2076e
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/drivers/synaptics-rmi.c104
-rw-r--r--firmware/export/synaptics-rmi.h22
-rw-r--r--firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c720
4 files changed, 496 insertions, 351 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 2cace81c8b..34d2db39b2 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1465,7 +1465,6 @@ target/arm/as3525/lcd-as-e200v2-fuze-fuzev2.S
1465#endif /* SANSA_FUZEV2 */ 1465#endif /* SANSA_FUZEV2 */
1466 1466
1467#ifdef SANSA_FUZEPLUS 1467#ifdef SANSA_FUZEPLUS
1468drivers/synaptics-rmi.c
1469#ifndef BOOTLOADER 1468#ifndef BOOTLOADER
1470drivers/generic_i2c.c 1469drivers/generic_i2c.c
1471target/arm/imx233/fmradio-imx233.c 1470target/arm/imx233/fmradio-imx233.c
diff --git a/firmware/drivers/synaptics-rmi.c b/firmware/drivers/synaptics-rmi.c
deleted file mode 100644
index 1b7cf2f8ef..0000000000
--- a/firmware/drivers/synaptics-rmi.c
+++ /dev/null
@@ -1,104 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "system.h"
22#include "synaptics-rmi.h"
23#include "i2c.h"
24
25static int rmi_cur_page;
26static int rmi_i2c_addr;
27
28static unsigned char dev_ctl_reg;
29
30/* NOTE:
31 * RMI over i2c supports some special aliases on page 0x2 but this driver don't
32 * use them */
33
34int rmi_init(int i2c_dev_addr)
35{
36 rmi_i2c_addr = i2c_dev_addr;
37 rmi_cur_page = 0x4;
38 dev_ctl_reg = rmi_read_single(RMI_DEVICE_CONTROL);
39 return 0;
40}
41
42static int rmi_select_page(unsigned char page)
43{
44 /* Lazy page select */
45 if(page != rmi_cur_page)
46 {
47 rmi_cur_page = page;
48 return i2c_writemem(rmi_i2c_addr, RMI_PAGE_SELECT, &page, 1);
49 }
50 else
51 return 0;
52}
53
54int rmi_read(int address, int byte_count, unsigned char *buffer)
55{
56 int ret;
57 if((ret = rmi_select_page(address >> 8)) < 0)
58 return ret;
59 return i2c_readmem(rmi_i2c_addr, address & 0xff, buffer, byte_count);
60}
61
62int rmi_read_single(int address)
63{
64 unsigned char c;
65 int ret = rmi_read(address, 1, &c);
66 return ret < 0 ? ret : c;
67}
68
69int rmi_write(int address, int byte_count, const unsigned char *buffer)
70{
71 int ret;
72 if((ret = rmi_select_page(address >> 8)) < 0)
73 return ret;
74 return i2c_writemem(rmi_i2c_addr, address & 0xff, buffer, byte_count);
75}
76
77int rmi_write_single(int address, unsigned char byte)
78{
79 return rmi_write(address, 1, &byte);
80}
81
82/* set the device to the given sleep mode */
83void rmi_set_sleep_mode(unsigned char sleep_mode)
84{
85 /* valid value different from the actual one*/
86 if((dev_ctl_reg & RMI_SLEEP_MODE_BM) != sleep_mode)
87 {
88 dev_ctl_reg &= ~RMI_SLEEP_MODE_BM;
89 dev_ctl_reg |= sleep_mode;
90 rmi_write_single(RMI_DEVICE_CONTROL, dev_ctl_reg);
91 }
92}
93
94/* set the device's report rate to the given value */
95void rmi_set_report_rate(unsigned char report_rate)
96{
97 /* valid value different from the actual one*/
98 if((dev_ctl_reg & RMI_REPORT_RATE_BM) != report_rate)
99 {
100 dev_ctl_reg &= ~RMI_REPORT_RATE_BM;
101 dev_ctl_reg |= report_rate;
102 rmi_write_single(RMI_DEVICE_CONTROL, dev_ctl_reg);
103 }
104}
diff --git a/firmware/export/synaptics-rmi.h b/firmware/export/synaptics-rmi.h
index 9d9d285fb6..e7fe14d7d5 100644
--- a/firmware/export/synaptics-rmi.h
+++ b/firmware/export/synaptics-rmi.h
@@ -123,26 +123,4 @@ struct rmi_2d_gesture_data_t
123 unsigned char flick; 123 unsigned char flick;
124} __attribute__((packed)); 124} __attribute__((packed));
125 125
126/* Initialize the RMI driver, the i2c_bus_index is the bus index returned by
127 * the generic_i2c driver; the i2c_dev_addr is the i2c address of the device.
128 * NOTE: the driver automatically handles the page select mechanism used for
129 * RMI over i2c and assumes a standard page select register at 0xff. */
130int rmi_init(int i2c_dev_addr);
131/* Read one or more registers.
132 * WARNING: don't cross a page boundary ! */
133int rmi_read(int address, int byte_count, unsigned char *buffer);
134/* Read a single register (return -1 on error)
135 * WARNING: beware of register consistency (N x read 1 byte != reads N bytes) */
136int rmi_read_single(int address); /* return byte value or <0 in case of error */
137/* Write one of more register
138 * WARNING: don't cross a page boundary ! */
139int rmi_write(int address, int byte_count, const unsigned char *buffer);
140/* Write one register
141 * WARNING: don't cross a page boundary ! */
142int rmi_write_single(int address, unsigned char byte);
143/* set the device to the given sleep mode */
144void rmi_set_sleep_mode(unsigned char sleep_mode);
145/* set the device's report rate to the given value */
146void rmi_set_report_rate(unsigned char report_rate);
147
148#endif 126#endif
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c
index ea4aa083e5..2c3358e7dc 100644
--- a/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c
+++ b/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c
@@ -22,7 +22,7 @@
22#include "system.h" 22#include "system.h"
23#include "system-target.h" 23#include "system-target.h"
24#include "pinctrl-imx233.h" 24#include "pinctrl-imx233.h"
25#include "generic_i2c.h" 25#include "i2c-imx233.h"
26#include "synaptics-rmi.h" 26#include "synaptics-rmi.h"
27#include "lcd.h" 27#include "lcd.h"
28#include "string.h" 28#include "string.h"
@@ -30,6 +30,7 @@
30#include "button-imx233.h" 30#include "button-imx233.h"
31#include "touchpad.h" 31#include "touchpad.h"
32#include "stdio.h" 32#include "stdio.h"
33#include "font.h"
33 34
34struct imx233_button_map_t imx233_button_map[] = 35struct imx233_button_map_t imx233_button_map[] =
35{ 36{
@@ -39,7 +40,428 @@ struct imx233_button_map_t imx233_button_map[] =
39 IMX233_BUTTON_(END, END(), "") 40 IMX233_BUTTON_(END, END(), "")
40}; 41};
41 42
43/**
44 * RMI API
45 */
46
47#define RMI_I2C_ADDR 0x40
48
49static unsigned char dev_ctl_reg; /* cached value of control register */
50
51/* NOTE:
52 * RMI over i2c supports some special aliases on page 0x2 but this driver don't
53 * use them */
54
55struct rmi_xfer_t;
56typedef void (*rmi_xfer_cb_t)(struct rmi_xfer_t *xfer);
57
58/* Represent a typical RMI transaction: a first transfer to select the page
59 * and a second transfer to read/write registers. The API takes care of annoying
60 * details and will simply call the callback at the end of the transfer. */
61struct rmi_xfer_t
62{
63 struct imx233_i2c_xfer_t xfer_page; /* first transfer: page select */
64 struct imx233_i2c_xfer_t xfer_rw; /* second transfer: read/write */
65 uint8_t sel_page[2]; /* write command to select page */
66 uint8_t sel_reg; /* write command to select register */
67 volatile enum imx233_i2c_error_t status; /* transfer status */
68 rmi_xfer_cb_t callback; /* callback */
69};
70
71/* Synchronous transfer: add a semaphore to block */
72struct rmi_xfer_sync_t
73{
74 struct rmi_xfer_t xfer;
75 struct semaphore sema; /* semaphore for completion */
76};
77
78/* callback for first transfer: record error if any */
79static void rmi_i2c_first_callback(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status)
80{
81 struct rmi_xfer_t *rxfer = container_of(xfer, struct rmi_xfer_t, xfer_page);
82 /* record status */
83 rxfer->status = status;
84}
85
86/* callback for first transfer: handle error and callback */
87static void rmi_i2c_second_callback(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status)
88{
89 struct rmi_xfer_t *rxfer = container_of(xfer, struct rmi_xfer_t, xfer_rw);
90 /* record status, only if not skipping (ie the error was in first transfer) */
91 if(status != I2C_SKIP)
92 rxfer->status = status;
93 /* callback */
94 if(rxfer->callback)
95 rxfer->callback(rxfer);
96}
97
98/* build a rmi transaction to read/write registers; do NOT cross page boundary ! */
99static void rmi_build_xfer(struct rmi_xfer_t *xfer, bool read, int address,
100 int byte_count, unsigned char *buffer, rmi_xfer_cb_t callback)
101{
102 /* first transfer: change page */
103 xfer->xfer_page.next = &xfer->xfer_rw;
104 xfer->xfer_page.fast_mode = true;
105 xfer->xfer_page.dev_addr = RMI_I2C_ADDR;
106 xfer->xfer_page.mode = I2C_WRITE;
107 xfer->xfer_page.count[0] = 2;
108 xfer->xfer_page.data[0] = &xfer->sel_page;
109 xfer->xfer_page.count[1] = 0;
110 xfer->xfer_page.tmo_ms = 1000;
111 xfer->xfer_page.callback = &rmi_i2c_first_callback;
112 /* second transfer: read/write */
113 xfer->xfer_rw.next = NULL;
114 xfer->xfer_rw.fast_mode = true;
115 xfer->xfer_rw.dev_addr = RMI_I2C_ADDR;
116 xfer->xfer_rw.mode = read ? I2C_READ : I2C_WRITE;
117 xfer->xfer_rw.count[0] = 1;
118 xfer->xfer_rw.data[0] = &xfer->sel_reg;
119 xfer->xfer_rw.count[1] = byte_count;
120 xfer->xfer_rw.data[1] = buffer;
121 xfer->xfer_rw.tmo_ms = 1000;
122 xfer->xfer_rw.callback = &rmi_i2c_second_callback;
123 /* general things */
124 xfer->callback = callback;
125 xfer->sel_page[0] = RMI_PAGE_SELECT;
126 xfer->sel_page[1] = address >> 8;
127 xfer->sel_reg = address & 0xff;
128}
129
130/** IMPORTANT NOTE
131 *
132 * All transfers are built using rmi_build_xfer which constructs a transaction
133 * consisting in a page select and register read/writes. Since transactions are
134 * executed "atomically" and are queued, it is safe to call transfers functions
135 * concurrently. However only asynchronous transfers can be used in IRQ context.
136 * In all cases, make sure the the rmi_xfer_t structure lives at least until the
137 * completion of the transfer (callback).
138 */
139
140/* queue transfer to change sleep mode, return true if transfer was queued
141 * and false if ignored because requested mode is already the current one.
142 * call must provide a transfer structure that must exist until completion */
143static bool rmi_set_sleep_mode_async(struct rmi_xfer_t *xfer, uint8_t *buf,
144 unsigned char sleep_mode, rmi_xfer_cb_t callback)
145{
146 /* avoid any race with concurrent changes to the mode */
147 unsigned long cpsr = disable_irq_save();
148 /* valid value different from the actual one */
149 if((dev_ctl_reg & RMI_SLEEP_MODE_BM) != sleep_mode)
150 {
151 /* change cached version */
152 dev_ctl_reg &= ~RMI_SLEEP_MODE_BM;
153 dev_ctl_reg |= sleep_mode;
154 *buf = dev_ctl_reg;
155 restore_irq(cpsr);
156 /* build transfer and kick */
157 rmi_build_xfer(xfer, false, RMI_DEVICE_CONTROL, 1, buf, callback);
158 imx233_i2c_transfer(&xfer->xfer_page);
159 return true;
160 }
161 else
162 {
163 restore_irq(cpsr);
164 return false;
165 }
166}
167
168/* synchronous callback: release semaphore */
169static void rmi_i2c_sync_callback(struct rmi_xfer_t *xfer)
170{
171 struct rmi_xfer_sync_t *sxfer = (void *)xfer;
172 semaphore_release(&sxfer->sema);
173}
174
175/* synchronous read/write */
176static void rmi_rw(bool read, int address, int byte_count, unsigned char *buffer)
177{
178 struct rmi_xfer_sync_t xfer;
179 rmi_build_xfer(&xfer.xfer, read, address, byte_count, buffer, rmi_i2c_sync_callback);
180 semaphore_init(&xfer.sema, 1, 0);
181 /* kick and wait */
182 imx233_i2c_transfer(&xfer.xfer.xfer_page);
183 semaphore_wait(&xfer.sema, TIMEOUT_BLOCK);
184 if(xfer.xfer.status != I2C_SUCCESS)
185 panicf("rmi: i2c err %d", xfer.xfer.status);
186}
187
188/* read registers synchronously */
189static void rmi_read(int address, int byte_count, unsigned char *buffer)
190{
191 rmi_rw(true, address, byte_count, buffer);
192}
193
194/* read single register synchronously */
195static int rmi_read_single(int address)
196{
197 unsigned char c;
198 rmi_rw(true, address, 1, &c);
199 return c;
200}
201
202/* write single register synchronously */
203static void rmi_write_single(int address, unsigned char byte)
204{
205 return rmi_rw(false, address, 1, &byte);
206}
207
208/* synchronously change sleep mode, this is a nop if current mode is the same as requested */
209static void rmi_set_sleep_mode(unsigned char sleep_mode)
210{
211 struct rmi_xfer_sync_t xfer;
212 uint8_t buf;
213 semaphore_init(&xfer.sema, 1, 0);
214 /* kick asynchronous transfer and only wait if mode was actually changed */
215 if(rmi_set_sleep_mode_async(&xfer.xfer, &buf, sleep_mode, &rmi_i2c_sync_callback))
216 {
217 semaphore_wait(&xfer.sema, TIMEOUT_BLOCK);
218 if(xfer.xfer.status != I2C_SUCCESS)
219 panicf("rmi: i2c err %d", xfer.xfer.status);
220 }
221}
222
223static void rmi_init(void)
224{
225 /* cache control register */
226 dev_ctl_reg = rmi_read_single(RMI_DEVICE_CONTROL);
227}
228
229
230/**
231 * Touchpad API
232 */
233
42#ifndef BOOTLOADER 234#ifndef BOOTLOADER
235/* we emulate a 3x3 grid, this gives the button mapping */
236int button_mapping[3][3] =
237{
238 {BUTTON_BOTTOMLEFT, BUTTON_LEFT, BUTTON_BACK},
239 {BUTTON_DOWN, BUTTON_SELECT, BUTTON_UP},
240 {BUTTON_BOTTOMRIGHT, BUTTON_RIGHT, BUTTON_PLAYPAUSE},
241};
242
243/* timeout before lowering touchpad power from lack of activity */
244#define ACTIVITY_TMO (5 * HZ)
245#define TOUCHPAD_WIDTH 3010
246#define TOUCHPAD_HEIGHT 1975
247#define DEADZONE_MULTIPLIER 2 /* deadzone multiplier */
248
249/* power level when touchpad is active: experiments show that "low power" reduce
250 * power consumption and hardly makes a difference in quality. */
251#define ACTIVE_POWER_LEVEL RMI_SLEEP_MODE_LOW_POWER
252
253static int touchpad_btns = 0; /* button bitmap for the touchpad */
254static unsigned last_activity = 0; /* tick of the last touchpad activity */
255static bool t_enable = true; /* is touchpad enabled? */
256static int deadzone; /* deadzone size */
257static struct timeout activity_tmo; /* activity timeout */
258
259/* Ignore deadzone function. If outside of the pad, project to border. */
260static int find_button_no_deadzone(int x, int y)
261{
262 /* compute grid coordinate */
263 int gx = MAX(MIN(x * 3 / TOUCHPAD_WIDTH, 2), 0);
264 int gy = MAX(MIN(y * 3 / TOUCHPAD_HEIGHT, 2), 0);
265
266 return button_mapping[gx][gy];
267}
268
269static int find_button(int x, int y)
270{
271 /* find button ignoring deadzones */
272 int btn = find_button_no_deadzone(x, y);
273 /* To check if we are in a deadzone, we try to shift the coordinates
274 * and see if we get the same button. Not that we do not want to apply
275 * the deadzone in the borders ! The code works even in the borders because
276 * the find_button_no_deadzone() project out-of-bound coordinates to the
277 * borders */
278 if(find_button_no_deadzone(x + deadzone, y) != btn ||
279 find_button_no_deadzone(x - deadzone, y) != btn ||
280 find_button_no_deadzone(x, y + deadzone) != btn ||
281 find_button_no_deadzone(x, y - deadzone) != btn)
282 return 0;
283 return btn;
284}
285
286void touchpad_set_deadzone(int touchpad_deadzone)
287{
288 deadzone = touchpad_deadzone * DEADZONE_MULTIPLIER;
289}
290
291static int touchpad_read_device(void)
292{
293 return touchpad_btns;
294}
295
296/* i2c transfer only used for irq processing
297 * NOTE we use two sets of transfers because we setup one in the callback of the
298 * other, using one would be unsafe */
299static struct rmi_xfer_t rmi_irq_xfer[2];
300static uint8_t rmi_irq_buf; /* buffer to hold irq status register and sleep mode */
301static union
302{
303 unsigned char data[10];
304 struct
305 {
306 struct rmi_2d_absolute_data_t absolute;
307 struct rmi_2d_relative_data_t relative;
308 struct rmi_2d_gesture_data_t gesture;
309 }s;
310}rmi_irq_data; /* buffer to hold touchpad data */
311
312static void rmi_attn_cb(int bank, int pin, intptr_t user);
313
314/* callback for i2c transfer to change power level after irq */
315static void rmi_power_irq_cb(struct rmi_xfer_t *xfer)
316{
317 /* we do not recover from error for now */
318 if(xfer->status != I2C_SUCCESS)
319 panicf("rmi: clear i2c err %d", xfer->status);
320 /* now that interrupt is cleared, we can renable attention irq */
321 imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
322}
323
324/* callback for i2c transfer to read/clear interrupt status register */
325static void rmi_clear_irq_cb(struct rmi_xfer_t *xfer)
326{
327 /* we do not recover from error for now */
328 if(xfer->status != I2C_SUCCESS)
329 panicf("rmi: clear i2c err %d", xfer->status);
330 /* at this point, we might have processed an event and the touchpad still be
331 * in very low power mode because of some previous inactivity; if it's the case,
332 * schedule another transfer to switch to a higher power mode before accepting the
333 * next event */
334 /* kick asynchronous transfer and only wait if mode was actually changed */
335 if(!rmi_set_sleep_mode_async(&rmi_irq_xfer[0], &rmi_irq_buf, ACTIVE_POWER_LEVEL,
336 &rmi_power_irq_cb))
337 /* now that interrupt is cleared, we can renable attention irq */
338 imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
339}
340
341/* callback for i2c transfer to read touchpad data registers */
342static void rmi_data_irq_cb(struct rmi_xfer_t *xfer)
343{
344 /* we do not recover from error for now */
345 if(xfer->status != I2C_SUCCESS)
346 panicf("rmi: data i2c err %d", xfer->status);
347 /* now that we have the data, setup another transfer to clear interrupt */
348 rmi_build_xfer(&rmi_irq_xfer[1], true, RMI_INTERRUPT_REQUEST, 1,
349 &rmi_irq_buf, &rmi_clear_irq_cb);
350 /* kick transfer */
351 imx233_i2c_transfer(&rmi_irq_xfer[1].xfer_page);
352 /* now process touchpad data */
353 int absolute_x = rmi_irq_data.s.absolute.x_msb << 8 | rmi_irq_data.s.absolute.x_lsb;
354 int absolute_y = rmi_irq_data.s.absolute.y_msb << 8 | rmi_irq_data.s.absolute.y_lsb;
355 int nr_fingers = rmi_irq_data.s.absolute.misc & 7;
356 if(nr_fingers == 1)
357 touchpad_btns = find_button(absolute_x, absolute_y);
358 else
359 touchpad_btns = 0;
360}
361
362/* touchpad attention line interrupt */
363static void rmi_attn_cb(int bank, int pin, intptr_t user)
364{
365 (void) bank;
366 (void) pin;
367 (void) user;
368 /* build transfer to read data registers */
369 rmi_build_xfer(&rmi_irq_xfer[0], true, RMI_DATA_REGISTER(0),
370 sizeof(rmi_irq_data.data), rmi_irq_data.data, &rmi_data_irq_cb);
371 /* kick transfer */
372 imx233_i2c_transfer(&rmi_irq_xfer[0].xfer_page);
373 /* update last activity */
374 last_activity = current_tick;
375}
376
377void touchpad_enable_device(bool en)
378{
379 t_enable = en;
380 rmi_set_sleep_mode(en ? ACTIVE_POWER_LEVEL : RMI_SLEEP_MODE_SENSOR_SLEEP);
381}
382
383void touchpad_set_sensitivity(int level)
384{
385 /* handle negative values as well ! */
386 rmi_write_single(RMI_2D_SENSITIVITY_ADJ, (unsigned char)(int8_t)level);
387}
388
389/* transfer used by the activity timeout to change power level */
390static struct rmi_xfer_t rmi_tmo_xfer;
391static uint8_t rmi_tmo_buf;
392
393/* activity timeout: lower power level after some inactivity */
394static int activity_monitor(struct timeout *tmo)
395{
396 (void) tmo;
397 if(TIME_AFTER(current_tick, last_activity + ACTIVITY_TMO))
398 {
399 /* don't change power mode if touchpad is disabled, it's already in sleep mode */
400 if(t_enable)
401 rmi_set_sleep_mode_async(&rmi_tmo_xfer, &rmi_tmo_buf,
402 RMI_SLEEP_MODE_VERY_LOW_POWER, NULL);
403 }
404
405 return HZ; /* next check in 1 second */
406}
407
408void touchpad_init(void)
409{
410 /* Synaptics TouchPad information:
411 * - product id: 1533
412 * - nr function: 1 (0x10 = 2D touchpad)
413 * 2D Touchpad information (function 0x10)
414 * - nr data sources: 3
415 * - standard layout
416 * - extra data registers: 7
417 * - nr sensors: 1
418 * 2D Touchpad Sensor #0 information:
419 * - has relative data: yes
420 * - has palm detect: yes
421 * - has multi finger: yes
422 * - has enhanced gesture: yes
423 * - has scroller: no
424 * - has 2D scrollers: no
425 * - Maximum X: 3009
426 * - Maxumum Y: 1974
427 * - Resolution: 82
428 */
429
430 imx233_pinctrl_acquire(0, 26, "touchpad power");
431 imx233_pinctrl_set_function(0, 26, PINCTRL_FUNCTION_GPIO);
432 imx233_pinctrl_enable_gpio(0, 26, false);
433 imx233_pinctrl_set_drive(0, 26, PINCTRL_DRIVE_8mA);
434
435 /* use a timer to monitor touchpad activity and manage power level */
436 last_activity = current_tick;
437 timeout_register(&activity_tmo, activity_monitor, HZ, 0);
438
439 rmi_init();
440
441 char product_id[RMI_PRODUCT_ID_LEN];
442 rmi_read(RMI_PRODUCT_ID, RMI_PRODUCT_ID_LEN, product_id);
443 /* The OF adjust the sensitivity based on product_id[1] compared to 2.
444 * Since it doesn't seem to work great, just hardcode the sensitivity to
445 * some reasonable value for now. */
446 rmi_write_single(RMI_2D_SENSITIVITY_ADJ, 13);
447
448 rmi_write_single(RMI_2D_GESTURE_SETTINGS,
449 RMI_2D_GESTURE_PRESS_TIME_300MS |
450 RMI_2D_GESTURE_FLICK_DIST_4MM << RMI_2D_GESTURE_FLICK_DIST_BP |
451 RMI_2D_GESTURE_FLICK_TIME_700MS << RMI_2D_GESTURE_FLICK_TIME_BP);
452
453 /* we don't know in which mode the touchpad start so use a sane default */
454 rmi_set_sleep_mode(ACTIVE_POWER_LEVEL);
455 /* enable interrupt */
456 imx233_pinctrl_acquire(0, 27, "touchpad int");
457 imx233_pinctrl_set_function(0, 27, PINCTRL_FUNCTION_GPIO);
458 imx233_pinctrl_enable_gpio(0, 27, false);
459 imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
460}
461
462/**
463 * Debug screen
464 */
43 465
44bool button_debug_screen(void) 466bool button_debug_screen(void)
45{ 467{
@@ -82,9 +504,27 @@ bool button_debug_screen(void)
82 gesture_vp.y = zone_y - 80; 504 gesture_vp.y = zone_y - 80;
83 gesture_vp.width = LCD_WIDTH / 2; 505 gesture_vp.width = LCD_WIDTH / 2;
84 gesture_vp.height = 80; 506 gesture_vp.height = 80;
85 507 /* remember tick of last gestures */
508 #define GESTURE_TMO HZ / 2
509 int tick_last_tap = current_tick - GESTURE_TMO;
510 int tick_last_doubletap = current_tick - GESTURE_TMO;
511 int tick_last_taphold = current_tick - GESTURE_TMO;
512 int tick_last_flick = current_tick - GESTURE_TMO;
513 int flick_x = 0, flick_y = 0;
514
515 /* BUG the data register are usually read by the IRQ already and it is
516 * important to not read them again, otherwise we could miss some events
517 * (most notable gestures). However, we only read registers when the
518 * touchpad is active so the data might be outdated if touchpad is
519 * inactive. We should implement a continuous reading mode for the debug
520 * screen. */
521
522 lcd_setfont(FONT_SYSFIXED);
86 while(1) 523 while(1)
87 { 524 {
525 /* call button_get() to avoid an overflow in the button queue */
526 button_get(false);
527
88 unsigned char sleep_mode = rmi_read_single(RMI_DEVICE_CONTROL) & RMI_SLEEP_MODE_BM; 528 unsigned char sleep_mode = rmi_read_single(RMI_DEVICE_CONTROL) & RMI_SLEEP_MODE_BM;
89 lcd_set_viewport(NULL); 529 lcd_set_viewport(NULL);
90 lcd_clear_display(); 530 lcd_clear_display();
@@ -92,10 +532,9 @@ bool button_debug_screen(void)
92 lcd_putsf(0, 0, "button bitmap: %x", btns); 532 lcd_putsf(0, 0, "button bitmap: %x", btns);
93 lcd_putsf(0, 1, "RMI: id=%s info=%s", product_id, product_info_str); 533 lcd_putsf(0, 1, "RMI: id=%s info=%s", product_id, product_info_str);
94 lcd_putsf(0, 2, "xmax=%d ymax=%d res=%d", x_max, y_max, sensor_resol); 534 lcd_putsf(0, 2, "xmax=%d ymax=%d res=%d", x_max, y_max, sensor_resol);
95 lcd_putsf(0, 3, "attn=%d ctl=%x int=%x", 535 lcd_putsf(0, 3, "attn=%d ctl=%x",
96 imx233_pinctrl_get_gpio(0, 27) ? 0 : 1, 536 imx233_pinctrl_get_gpio(0, 27) ? 0 : 1,
97 rmi_read_single(RMI_DEVICE_CONTROL), 537 rmi_read_single(RMI_DEVICE_CONTROL));
98 rmi_read_single(RMI_INTERRUPT_REQUEST));
99 lcd_putsf(0, 4, "sensi: %d min_dist: %d", (int)sensitivity.value, min_dist); 538 lcd_putsf(0, 4, "sensi: %d min_dist: %d", (int)sensitivity.value, min_dist);
100 lcd_putsf(0, 5, "gesture: %x", gesture_settings); 539 lcd_putsf(0, 5, "gesture: %x", gesture_settings);
101 540
@@ -109,21 +548,49 @@ bool button_debug_screen(void)
109 struct rmi_2d_gesture_data_t gesture; 548 struct rmi_2d_gesture_data_t gesture;
110 }s; 549 }s;
111 }u; 550 }u;
551
552 /* Disable IRQs when reading to avoid reading incorrect data */
553 unsigned long cpsr = disable_irq_save();
554 memcpy(&u, &rmi_irq_data, sizeof(u));
555 restore_irq(cpsr);
556
112 int absolute_x = u.s.absolute.x_msb << 8 | u.s.absolute.x_lsb; 557 int absolute_x = u.s.absolute.x_msb << 8 | u.s.absolute.x_lsb;
113 int absolute_y = u.s.absolute.y_msb << 8 | u.s.absolute.y_lsb; 558 int absolute_y = u.s.absolute.y_msb << 8 | u.s.absolute.y_lsb;
114 int nr_fingers = u.s.absolute.misc & 7; 559 int nr_fingers = u.s.absolute.misc & 7;
115 bool gesture = (u.s.absolute.misc & 8) == 8; 560 bool gesture = (u.s.absolute.misc & 8) == 8;
116 int palm_width = u.s.absolute.misc >> 4; 561 int palm_width = u.s.absolute.misc >> 4;
117 562
118 rmi_read(RMI_DATA_REGISTER(0), 10, u.data);
119 lcd_putsf(0, 6, "abs: %d %d %d", absolute_x, absolute_y, (int)u.s.absolute.z); 563 lcd_putsf(0, 6, "abs: %d %d %d", absolute_x, absolute_y, (int)u.s.absolute.z);
120 lcd_putsf(0, 7, "rel: %d %d", (int)u.s.relative.x, (int)u.s.relative.y); 564 lcd_putsf(0, 7, "rel: %d %d", (int)u.s.relative.x, (int)u.s.relative.y);
121 lcd_putsf(0, 8, "gesture: %x %x", u.s.gesture.misc, u.s.gesture.flick); 565 lcd_putsf(0, 8, "gesture: %x %x", u.s.gesture.misc, u.s.gesture.flick);
122 lcd_putsf(0, 9, "misc: w=%d g=%d f=%d", palm_width, gesture, nr_fingers); 566 lcd_putsf(0, 9, "misc: w=%d g=%d f=%d", palm_width, gesture, nr_fingers);
123 lcd_putsf(30, 7, "sleep_mode: %d", 1 - sleep_mode); 567 lcd_putsf(0, 10, "sleep_mode: %d", sleep_mode);
568 lcd_putsf(0, 11, "deadzone: %d", deadzone);
569 /* display virtual touchpad with deadzones */
124 lcd_set_viewport(&report_vp); 570 lcd_set_viewport(&report_vp);
571 lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0xff, 0), LCD_BLACK);
572 for(int i = 0; i < 3; i++)
573 for(int j = 0; j < 3; j++)
574 {
575 int x = j * x_max / 3;
576 if(j != 0)
577 x += deadzone;
578 int x2 = (j + 1) * x_max / 3;
579 if(j != 2)
580 x2 -= deadzone;
581 int y = i * y_max / 3;
582 if(i != 0)
583 y += deadzone;
584 int y2 = (i + 1) * y_max / 3;
585 if(i != 2)
586 y2 -= deadzone;
587 x = DX2SX(x); x2 = DX2SX(x2); y = DY2SY(y); y2 = DY2SY(y2);
588 lcd_drawrect(x, y2, x2 - x + 1, y - y2 + 1);
589 }
125 lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0, 0), LCD_BLACK); 590 lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0, 0), LCD_BLACK);
126 lcd_drawrect(0, 0, zone_w, zone_h); 591 lcd_drawrect(0, 0, zone_w, zone_h);
592 /* put a done at the reported position of the finger
593 * also display relative motion by a line as reported by the device */
127 if(nr_fingers == 1) 594 if(nr_fingers == 1)
128 { 595 {
129 lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0, 0, 0xff), LCD_BLACK); 596 lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0, 0, 0xff), LCD_BLACK);
@@ -141,27 +608,37 @@ bool button_debug_screen(void)
141 { 608 {
142 case RMI_2D_GEST_MISC_NO_TAP: break; 609 case RMI_2D_GEST_MISC_NO_TAP: break;
143 case RMI_2D_GEST_MISC_SINGLE_TAP: 610 case RMI_2D_GEST_MISC_SINGLE_TAP:
144 lcd_putsf(0, 0, "TAP!"); 611 tick_last_tap = current_tick;
145 break; 612 break;
146 case RMI_2D_GEST_MISC_DOUBLE_TAP: 613 case RMI_2D_GEST_MISC_DOUBLE_TAP:
147 lcd_putsf(0, 0, "DOUBLE TAP!"); 614 tick_last_doubletap = current_tick;
148 break; 615 break;
149 case RMI_2D_GEST_MISC_TAP_AND_HOLD: 616 case RMI_2D_GEST_MISC_TAP_AND_HOLD:
150 lcd_putsf(0, 0, "TAP & HOLD!"); 617 tick_last_taphold = current_tick;
151 break; 618 break;
152 default: break; 619 default: break;
153 } 620 }
154 621
155 if(u.s.gesture.misc & RMI_2D_GEST_MISC_FLICK) 622 if(u.s.gesture.misc & RMI_2D_GEST_MISC_FLICK)
156 { 623 {
157 lcd_putsf(0, 1, "FLICK!"); 624 tick_last_flick = current_tick;
158 int flick_x = u.s.gesture.flick & RMI_2D_GEST_FLICK_X_BM; 625 flick_x = u.s.gesture.flick & RMI_2D_GEST_FLICK_X_BM;
159 int flick_y = (u.s.gesture.flick & RMI_2D_GEST_FLICK_Y_BM) >> RMI_2D_GEST_FLICK_Y_BP; 626 flick_y = (u.s.gesture.flick & RMI_2D_GEST_FLICK_Y_BM) >> RMI_2D_GEST_FLICK_Y_BP;
160 #define SIGN4EXT(a) \ 627 #define SIGN4EXT(a) \
161 if(a & 8) a = -((a ^ 0xf) + 1); 628 if(a & 8) a = -((a ^ 0xf) + 1);
162 SIGN4EXT(flick_x); 629 SIGN4EXT(flick_x);
163 SIGN4EXT(flick_y); 630 SIGN4EXT(flick_y);
631 }
164 632
633 if(TIME_BEFORE(current_tick, tick_last_tap + GESTURE_TMO))
634 lcd_putsf(0, 0, "TAP!");
635 if(TIME_BEFORE(current_tick, tick_last_doubletap + GESTURE_TMO))
636 lcd_putsf(0, 1, "DOUBLE TAP!");
637 if(TIME_BEFORE(current_tick, tick_last_taphold + GESTURE_TMO))
638 lcd_putsf(0, 2, "TAP & HOLD!");
639 if(TIME_BEFORE(current_tick, tick_last_flick + GESTURE_TMO))
640 {
641 lcd_putsf(0, 3, "FLICK!");
165 int center_x = (LCD_WIDTH * 2) / 3; 642 int center_x = (LCD_WIDTH * 2) / 3;
166 int center_y = 40; 643 int center_y = 40;
167 lcd_drawline(center_x, center_y, center_x + flick_x * 5, center_y - flick_y * 5); 644 lcd_drawline(center_x, center_y, center_x + flick_x * 5, center_y - flick_y * 5);
@@ -185,228 +662,23 @@ bool button_debug_screen(void)
185 volkeys_delay_counter = 0; 662 volkeys_delay_counter = 0;
186 } 663 }
187 } 664 }
188
189 yield();
190 } 665 }
191 666
667 lcd_set_viewport(NULL);
668 lcd_setfont(FONT_UI);
192 return true; 669 return true;
193} 670}
194 671
195/* we emulate a 3x3 grid, this gives the button mapping */ 672#else /* BOOTLOADER */
196int button_mapping[3][3] =
197{
198 {BUTTON_BOTTOMLEFT, BUTTON_LEFT, BUTTON_BACK},
199 {BUTTON_DOWN, BUTTON_SELECT, BUTTON_UP},
200 {BUTTON_BOTTOMRIGHT, BUTTON_RIGHT, BUTTON_PLAYPAUSE},
201};
202
203#define RMI_INTERRUPT 1
204#define RMI_SET_SENSITIVITY 2
205#define RMI_SET_SLEEP_MODE 3
206/* timeout before lowering touchpad power from lack of activity */
207#define ACTIVITY_TMO (5 * HZ)
208#define TOUCHPAD_WIDTH 3010
209#define TOUCHPAD_HEIGHT 1975
210#define DEADZONE_MULTIPLIER 2 /* deadzone multiplier */
211
212static int touchpad_btns = 0;
213static long rmi_stack [DEFAULT_STACK_SIZE/sizeof(long)];
214static const char rmi_thread_name[] = "rmi";
215static struct event_queue rmi_queue;
216static unsigned last_activity = 0;
217static bool t_enable = true;
218static int deadzone;
219
220/* Ignore deadzone function. If outside of the pad, project to border. */
221static int find_button_no_deadzone(int x, int y)
222{
223 /* compute grid coordinate */
224 int gx = MAX(MIN(x * 3 / TOUCHPAD_WIDTH, 2), 0);
225 int gy = MAX(MIN(y * 3 / TOUCHPAD_HEIGHT, 2), 0);
226
227 return button_mapping[gx][gy];
228}
229
230static int find_button(int x, int y)
231{
232 /* find button ignoring deadzones */
233 int btn = find_button_no_deadzone(x, y);
234 /* To check if we are in a deadzone, we try to shift the coordinates
235 * and see if we get the same button. Not that we do not want to apply
236 * the deadzone in the borders ! The code works even in the borders because
237 * the find_button_no_deadzone() project out-of-bound coordinates to the
238 * borders */
239 if(find_button_no_deadzone(x + deadzone, y) != btn ||
240 find_button_no_deadzone(x - deadzone, y) != btn ||
241 find_button_no_deadzone(x, y + deadzone) != btn ||
242 find_button_no_deadzone(x, y - deadzone) != btn)
243 return 0;
244 return btn;
245}
246
247void touchpad_set_deadzone(int touchpad_deadzone)
248{
249 deadzone = touchpad_deadzone * DEADZONE_MULTIPLIER;
250}
251
252static int touchpad_read_device(void)
253{
254 return touchpad_btns;
255}
256
257static void rmi_attn_cb(int bank, int pin, intptr_t user)
258{
259 (void) bank;
260 (void) pin;
261 (void) user;
262 /* the callback will not be fired until interrupt is enabled back so
263 * the queue will not overflow or contain multiple RMI_INTERRUPT events */
264 queue_post(&rmi_queue, RMI_INTERRUPT, 0);
265}
266
267static void do_interrupt(void)
268{
269 /* rmi_set_sleep_mode() does not do anything if the value
270 * it is given is already the one setted */
271 rmi_set_sleep_mode(RMI_SLEEP_MODE_LOW_POWER);
272 last_activity = current_tick;
273 /* clear interrupt */
274 rmi_read_single(RMI_INTERRUPT_REQUEST);
275 /* read data */
276 union
277 {
278 unsigned char data[10];
279 struct
280 {
281 struct rmi_2d_absolute_data_t absolute;
282 struct rmi_2d_relative_data_t relative;
283 struct rmi_2d_gesture_data_t gesture;
284 }s;
285 }u;
286 rmi_read(RMI_DATA_REGISTER(0), 10, u.data);
287 int absolute_x = u.s.absolute.x_msb << 8 | u.s.absolute.x_lsb;
288 int absolute_y = u.s.absolute.y_msb << 8 | u.s.absolute.y_lsb;
289 int nr_fingers = u.s.absolute.misc & 7;
290
291 if(nr_fingers == 1)
292 touchpad_btns = find_button(absolute_x, absolute_y);
293 else
294 touchpad_btns = 0;
295
296 /* enable interrupt */
297 imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
298}
299
300void touchpad_enable_device(bool en)
301{
302 t_enable = en;
303 queue_post(&rmi_queue, RMI_SET_SLEEP_MODE, en ? RMI_SLEEP_MODE_LOW_POWER : RMI_SLEEP_MODE_SENSOR_SLEEP);
304}
305
306void touchpad_set_sensitivity(int level)
307{
308 queue_post(&rmi_queue, RMI_SET_SENSITIVITY, level);
309}
310
311static void rmi_thread(void)
312{
313 struct queue_event ev;
314
315 while(1)
316 {
317 /* make sure to timeout often enough for the activity timeout to take place */
318 queue_wait_w_tmo(&rmi_queue, &ev, HZ);
319 /* handle usb connect and ignore all messages except rmi interrupts */
320 switch(ev.id)
321 {
322 case SYS_USB_CONNECTED:
323 usb_acknowledge(SYS_USB_CONNECTED_ACK);
324 break;
325 case RMI_SET_SENSITIVITY:
326 /* handle negative values as well ! */
327 rmi_write_single(RMI_2D_SENSITIVITY_ADJ, (unsigned char)(int8_t)ev.data);
328 break;
329 case RMI_SET_SLEEP_MODE:
330 /* reset activity */
331 last_activity = current_tick;
332 rmi_set_sleep_mode(ev.data);
333 break;
334 case RMI_INTERRUPT:
335 do_interrupt();
336 break;
337 default:
338 /* activity timeout */
339 if(TIME_AFTER(current_tick, last_activity + ACTIVITY_TMO))
340 {
341 /* don't change power mode if touchpad is disabled, it's already in sleep mode */
342 if(t_enable)
343 rmi_set_sleep_mode(RMI_SLEEP_MODE_VERY_LOW_POWER);
344 }
345 break;
346 }
347 }
348}
349
350static void touchpad_init(void)
351{
352 /* Synaptics TouchPad information:
353 * - product id: 1533
354 * - nr function: 1 (0x10 = 2D touchpad)
355 * 2D Touchpad information (function 0x10)
356 * - nr data sources: 3
357 * - standard layout
358 * - extra data registers: 7
359 * - nr sensors: 1
360 * 2D Touchpad Sensor #0 information:
361 * - has relative data: yes
362 * - has palm detect: yes
363 * - has multi finger: yes
364 * - has enhanced gesture: yes
365 * - has scroller: no
366 * - has 2D scrollers: no
367 * - Maximum X: 3009
368 * - Maxumum Y: 1974
369 * - Resolution: 82
370 */
371
372 imx233_pinctrl_acquire(0, 26, "touchpad power");
373 imx233_pinctrl_set_function(0, 26, PINCTRL_FUNCTION_GPIO);
374 imx233_pinctrl_enable_gpio(0, 26, false);
375 imx233_pinctrl_set_drive(0, 26, PINCTRL_DRIVE_8mA);
376
377 rmi_init(0x40);
378
379 char product_id[RMI_PRODUCT_ID_LEN];
380 rmi_read(RMI_PRODUCT_ID, RMI_PRODUCT_ID_LEN, product_id);
381 /* The OF adjust the sensitivity based on product_id[1] compared to 2.
382 * Since it doesn't to work great, just hardcode the sensitivity to
383 * some reasonable value for now. */
384 rmi_write_single(RMI_2D_SENSITIVITY_ADJ, 13);
385
386 rmi_write_single(RMI_2D_GESTURE_SETTINGS,
387 RMI_2D_GESTURE_PRESS_TIME_300MS |
388 RMI_2D_GESTURE_FLICK_DIST_4MM << RMI_2D_GESTURE_FLICK_DIST_BP |
389 RMI_2D_GESTURE_FLICK_TIME_700MS << RMI_2D_GESTURE_FLICK_TIME_BP);
390
391 queue_init(&rmi_queue, true);
392 create_thread(rmi_thread, rmi_stack, sizeof(rmi_stack), 0,
393 rmi_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
394 /* low power mode seems to be enough for normal use */
395 rmi_set_sleep_mode(RMI_SLEEP_MODE_LOW_POWER);
396 /* enable interrupt */
397 imx233_pinctrl_acquire(0, 27, "touchpad int");
398 imx233_pinctrl_set_function(0, 27, PINCTRL_FUNCTION_GPIO);
399 imx233_pinctrl_enable_gpio(0, 27, false);
400 imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
401}
402
403#else
404int touchpad_read_device(void) 673int touchpad_read_device(void)
405{ 674{
406 return 0; 675 return 0;
407} 676}
408#endif 677#endif
409 678
679/**
680 * Button API
681 */
410void button_init_device(void) 682void button_init_device(void)
411{ 683{
412#ifndef BOOTLOADER 684#ifndef BOOTLOADER