summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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