diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/SOURCES | 1 | ||||
-rw-r--r-- | firmware/drivers/synaptics-rmi.c | 104 | ||||
-rw-r--r-- | firmware/export/synaptics-rmi.h | 22 | ||||
-rw-r--r-- | firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c | 720 |
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 |
1468 | drivers/synaptics-rmi.c | ||
1469 | #ifndef BOOTLOADER | 1468 | #ifndef BOOTLOADER |
1470 | drivers/generic_i2c.c | 1469 | drivers/generic_i2c.c |
1471 | target/arm/imx233/fmradio-imx233.c | 1470 | target/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 | |||
25 | static int rmi_cur_page; | ||
26 | static int rmi_i2c_addr; | ||
27 | |||
28 | static 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 | |||
34 | int 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 | |||
42 | static 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 | |||
54 | int 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 | |||
62 | int 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 | |||
69 | int 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 | |||
77 | int 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 */ | ||
83 | void 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 */ | ||
95 | void 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. */ | ||
130 | int rmi_init(int i2c_dev_addr); | ||
131 | /* Read one or more registers. | ||
132 | * WARNING: don't cross a page boundary ! */ | ||
133 | int 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) */ | ||
136 | int 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 ! */ | ||
139 | int rmi_write(int address, int byte_count, const unsigned char *buffer); | ||
140 | /* Write one register | ||
141 | * WARNING: don't cross a page boundary ! */ | ||
142 | int rmi_write_single(int address, unsigned char byte); | ||
143 | /* set the device to the given sleep mode */ | ||
144 | void rmi_set_sleep_mode(unsigned char sleep_mode); | ||
145 | /* set the device's report rate to the given value */ | ||
146 | void 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 | ||
34 | struct imx233_button_map_t imx233_button_map[] = | 35 | struct 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 | |||
49 | static 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 | |||
55 | struct rmi_xfer_t; | ||
56 | typedef 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. */ | ||
61 | struct 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 */ | ||
72 | struct 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 */ | ||
79 | static 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 */ | ||
87 | static 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 ! */ | ||
99 | static 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 */ | ||
143 | static 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 */ | ||
169 | static 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 */ | ||
176 | static 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 */ | ||
189 | static 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 */ | ||
195 | static 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 */ | ||
203 | static 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 */ | ||
209 | static 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 | |||
223 | static 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 */ | ||
236 | int 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 | |||
253 | static int touchpad_btns = 0; /* button bitmap for the touchpad */ | ||
254 | static unsigned last_activity = 0; /* tick of the last touchpad activity */ | ||
255 | static bool t_enable = true; /* is touchpad enabled? */ | ||
256 | static int deadzone; /* deadzone size */ | ||
257 | static struct timeout activity_tmo; /* activity timeout */ | ||
258 | |||
259 | /* Ignore deadzone function. If outside of the pad, project to border. */ | ||
260 | static 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 | |||
269 | static 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 | |||
286 | void touchpad_set_deadzone(int touchpad_deadzone) | ||
287 | { | ||
288 | deadzone = touchpad_deadzone * DEADZONE_MULTIPLIER; | ||
289 | } | ||
290 | |||
291 | static 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 */ | ||
299 | static struct rmi_xfer_t rmi_irq_xfer[2]; | ||
300 | static uint8_t rmi_irq_buf; /* buffer to hold irq status register and sleep mode */ | ||
301 | static 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 | |||
312 | static void rmi_attn_cb(int bank, int pin, intptr_t user); | ||
313 | |||
314 | /* callback for i2c transfer to change power level after irq */ | ||
315 | static 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 */ | ||
325 | static 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 */ | ||
342 | static 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 */ | ||
363 | static 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 | |||
377 | void 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 | |||
383 | void 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 */ | ||
390 | static struct rmi_xfer_t rmi_tmo_xfer; | ||
391 | static uint8_t rmi_tmo_buf; | ||
392 | |||
393 | /* activity timeout: lower power level after some inactivity */ | ||
394 | static 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 | |||
408 | void 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 | ||
44 | bool button_debug_screen(void) | 466 | bool 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 */ |
196 | int 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 | |||
212 | static int touchpad_btns = 0; | ||
213 | static long rmi_stack [DEFAULT_STACK_SIZE/sizeof(long)]; | ||
214 | static const char rmi_thread_name[] = "rmi"; | ||
215 | static struct event_queue rmi_queue; | ||
216 | static unsigned last_activity = 0; | ||
217 | static bool t_enable = true; | ||
218 | static int deadzone; | ||
219 | |||
220 | /* Ignore deadzone function. If outside of the pad, project to border. */ | ||
221 | static 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 | |||
230 | static 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 | |||
247 | void touchpad_set_deadzone(int touchpad_deadzone) | ||
248 | { | ||
249 | deadzone = touchpad_deadzone * DEADZONE_MULTIPLIER; | ||
250 | } | ||
251 | |||
252 | static int touchpad_read_device(void) | ||
253 | { | ||
254 | return touchpad_btns; | ||
255 | } | ||
256 | |||
257 | static 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 | |||
267 | static 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 | |||
300 | void 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 | |||
306 | void touchpad_set_sensitivity(int level) | ||
307 | { | ||
308 | queue_post(&rmi_queue, RMI_SET_SENSITIVITY, level); | ||
309 | } | ||
310 | |||
311 | static 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 | |||
350 | static 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 | ||
404 | int touchpad_read_device(void) | 673 | int 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 | */ | ||
410 | void button_init_device(void) | 682 | void button_init_device(void) |
411 | { | 683 | { |
412 | #ifndef BOOTLOADER | 684 | #ifndef BOOTLOADER |