diff options
Diffstat (limited to 'firmware/target/arm/as3525/button-e200v2-fuze.c')
-rw-r--r-- | firmware/target/arm/as3525/button-e200v2-fuze.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/firmware/target/arm/as3525/button-e200v2-fuze.c b/firmware/target/arm/as3525/button-e200v2-fuze.c new file mode 100644 index 0000000000..6a9f02959c --- /dev/null +++ b/firmware/target/arm/as3525/button-e200v2-fuze.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2008 by Thomas Martitz | ||
11 | * Copyright (C) 2008 by Dominik Wenger | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | |||
23 | #include "system.h" | ||
24 | #include "button.h" | ||
25 | #include "button-target.h" | ||
26 | #include "backlight.h" | ||
27 | |||
28 | |||
29 | #ifdef SANSA_FUZE | ||
30 | #define DBOP_BIT15_BUTTON BUTTON_HOME | ||
31 | #define WHEEL_REPEAT_INTERVAL (HZ/5) | ||
32 | #define WHEEL_COUNTER_DIV 4 | ||
33 | #define ACCEL_INCREMENT 2 | ||
34 | #define ACCEL_SHIFT 2 | ||
35 | #define BUTTON_DELAY 45 | ||
36 | #endif | ||
37 | |||
38 | #ifdef SANSA_E200V2 | ||
39 | #define DBOP_BIT15_BUTTON BUTTON_REC | ||
40 | #define WHEEL_REPEAT_INTERVAL (HZ/5) | ||
41 | #define WHEEL_COUNTER_DIV 2 | ||
42 | #define ACCEL_INCREMENT 3 | ||
43 | #define ACCEL_SHIFT 1 | ||
44 | #define BUTTON_DELAY 10 | ||
45 | |||
46 | /* read_missed is true if buttons could not | ||
47 | * be read (see lcd_button_support) */ | ||
48 | static bool read_missed = false; | ||
49 | |||
50 | #endif | ||
51 | |||
52 | /* Buttons */ | ||
53 | static bool hold_button = false; | ||
54 | #ifndef BOOTLOADER | ||
55 | static bool hold_button_old = false; | ||
56 | #endif | ||
57 | static unsigned short _dbop_din = BUTTON_NONE; | ||
58 | |||
59 | /* in the lcd driver */ | ||
60 | extern bool lcd_button_support(void); | ||
61 | |||
62 | void button_init_device(void) | ||
63 | { | ||
64 | GPIOA_DIR |= (1<<1); | ||
65 | GPIOA_PIN(1) = (1<<1); | ||
66 | } | ||
67 | |||
68 | #if !defined(BOOTLOADER) && defined(HAVE_SCROLLWHEEL) | ||
69 | static void scrollwheel(unsigned short dbop_din) | ||
70 | { | ||
71 | /* current wheel values, parsed from dbop and the resulting button */ | ||
72 | unsigned wheel_value = 0; | ||
73 | unsigned btn = BUTTON_NONE; | ||
74 | /* old wheel values */ | ||
75 | static unsigned old_wheel_value = 0; | ||
76 | static unsigned old_btn = BUTTON_NONE; | ||
77 | |||
78 | /* | ||
79 | * Getting BUTTON_REPEAT works like this: Remember when the btn value was | ||
80 | * posted to the button_queue last, and if it was recent enough, generate | ||
81 | * BUTTON_REPEAT | ||
82 | */ | ||
83 | static long last_wheel_post = 0; | ||
84 | |||
85 | /* | ||
86 | * Providing wheel acceleration works as follows: We increment accel | ||
87 | * by 2 if the wheel was turned, and decrement it by 1 each tick | ||
88 | * (no matter if it was turned), that means: the longer and faster you turn, | ||
89 | * the higher accel will be. accel>>2 will actually posted to the button_queue | ||
90 | */ | ||
91 | static int accel = 0; | ||
92 | /* We only post every 4th action, as this matches better with the physical | ||
93 | * clicks of the wheel */ | ||
94 | static int counter = 0; | ||
95 | /* Read wheel | ||
96 | * Bits 13 and 14 of DBOP_DIN change as follows: | ||
97 | * Clockwise rotation 00 -> 01 -> 11 -> 10 -> 00 | ||
98 | * Counter-clockwise 00 -> 10 -> 11 -> 01 -> 00 | ||
99 | */ | ||
100 | static const unsigned char wheel_tbl[2][4] = | ||
101 | { | ||
102 | { 2, 0, 3, 1 }, /* Clockwise rotation */ | ||
103 | { 1, 3, 0, 2 }, /* Counter-clockwise */ | ||
104 | }; | ||
105 | |||
106 | if(hold_button) | ||
107 | { | ||
108 | accel = counter = 0; | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | wheel_value = dbop_din & (1<<13|1<<14); | ||
113 | wheel_value >>= 13; | ||
114 | |||
115 | if (old_wheel_value == wheel_tbl[0][wheel_value]) | ||
116 | btn = BUTTON_SCROLL_FWD; | ||
117 | else if (old_wheel_value == wheel_tbl[1][wheel_value]) | ||
118 | btn = BUTTON_SCROLL_BACK; | ||
119 | |||
120 | if (btn != BUTTON_NONE) | ||
121 | { | ||
122 | if (btn != old_btn) | ||
123 | { | ||
124 | /* direction reversals nullify acceleration and counters */ | ||
125 | old_btn = btn; | ||
126 | accel = counter = 0; | ||
127 | } | ||
128 | /* wheel_delta will cause lists to jump over items, | ||
129 | * we want this for fast scrolling, but we must keep it accurate | ||
130 | * for slow scrolling */ | ||
131 | int wheel_delta = 0; | ||
132 | /* generate BUTTON_REPEAT if quick enough, scroll slightly faster too*/ | ||
133 | if (TIME_BEFORE(current_tick, last_wheel_post + WHEEL_REPEAT_INTERVAL)) | ||
134 | { | ||
135 | btn |= BUTTON_REPEAT; | ||
136 | wheel_delta = accel>>ACCEL_SHIFT; | ||
137 | } | ||
138 | |||
139 | accel += ACCEL_INCREMENT; | ||
140 | |||
141 | /* the wheel is more reliable if we don't send every change, | ||
142 | * every WHEEL_COUNTER_DIVth is basically one "physical click" | ||
143 | * which should make up 1 item in lists */ | ||
144 | if (++counter >= WHEEL_COUNTER_DIV && queue_empty(&button_queue)) | ||
145 | { | ||
146 | buttonlight_on(); | ||
147 | backlight_on(); | ||
148 | queue_post(&button_queue, btn, ((wheel_delta+1)<<24)); | ||
149 | /* message posted - reset count and remember post */ | ||
150 | counter = 0; | ||
151 | last_wheel_post = current_tick; | ||
152 | } | ||
153 | } | ||
154 | if (accel > 0 | ||
155 | #ifdef SANSA_E200V2 | ||
156 | && !read_missed /* decrement only if reading buttons was successful */ | ||
157 | #endif | ||
158 | ) | ||
159 | accel--; | ||
160 | |||
161 | old_wheel_value = wheel_value; | ||
162 | } | ||
163 | #endif /* !defined(BOOTLOADER) && defined(SCROLLWHEEL) */ | ||
164 | |||
165 | bool button_hold(void) | ||
166 | { | ||
167 | return hold_button; | ||
168 | } | ||
169 | |||
170 | static void button_delay(void) | ||
171 | { | ||
172 | int i = BUTTON_DELAY; | ||
173 | while(i--) asm volatile ("nop\n"); | ||
174 | } | ||
175 | |||
176 | unsigned short button_read_dbop(void) | ||
177 | { | ||
178 | #ifdef SANSA_FUZE | ||
179 | /* skip home and power reading if lcd_button_support was blocked, | ||
180 | * since the dbop bit 15 is invalid then, and use the old value instead | ||
181 | * -20 (arbitary value) indicates valid home&power button read | ||
182 | * (fuze only) */ | ||
183 | int old_home_power = -20; | ||
184 | #endif | ||
185 | if(!lcd_button_support()) | ||
186 | { | ||
187 | #if defined(SANSA_FUZE) | ||
188 | old_home_power = (_dbop_din & (1<<15|1<<8)); | ||
189 | #elif defined(SANSA_E200V2) | ||
190 | read_missed = true; | ||
191 | #endif | ||
192 | } | ||
193 | |||
194 | #ifdef SANSA_E200V2 | ||
195 | if (!read_missed) /* read buttons only if lcd_button_support was not blocked */ | ||
196 | #endif | ||
197 | { | ||
198 | /* Set up dbop for input */ | ||
199 | DBOP_CTRL |= (1<<19); /* Tri-state DBOP on read cycle */ | ||
200 | DBOP_CTRL &= ~(1<<16); /* disable output (1:write enabled) */ | ||
201 | DBOP_TIMPOL_01 = 0xe167e167; /* Set Timing & Polarity regs 0 & 1 */ | ||
202 | DBOP_TIMPOL_23 = 0xe167006e; /* Set Timing & Polarity regs 2 & 3 */ | ||
203 | |||
204 | button_delay(); | ||
205 | DBOP_CTRL |= (1<<15); /* start read */ | ||
206 | while (!(DBOP_STAT & (1<<16))); /* wait for valid data */ | ||
207 | |||
208 | _dbop_din = DBOP_DIN; /* Read dbop data*/ | ||
209 | |||
210 | /* Reset dbop for output */ | ||
211 | DBOP_TIMPOL_01 = 0x6e167; /* Set Timing & Polarity regs 0 & 1 */ | ||
212 | DBOP_TIMPOL_23 = 0xa167e06f; /* Set Timing & Polarity regs 2 & 3 */ | ||
213 | DBOP_CTRL |= (1<<16); /* Enable output (0:write disable) */ | ||
214 | DBOP_CTRL &= ~(1<<19); /* Tri-state when no active write */ | ||
215 | } | ||
216 | |||
217 | #ifdef SANSA_FUZE | ||
218 | /* write back old values if blocked */ | ||
219 | if (old_home_power != -20) | ||
220 | { | ||
221 | _dbop_din |= old_home_power & 1<<15; | ||
222 | _dbop_din &= 0xfeff|(old_home_power & 1<<8); | ||
223 | } | ||
224 | #endif | ||
225 | |||
226 | #if defined(HAVE_SCROLLWHEEL) && !defined(BOOTLOADER) | ||
227 | /* read wheel on bit 13 & 14, but sent to the button queue seperately */ | ||
228 | scrollwheel(_dbop_din); | ||
229 | #endif | ||
230 | |||
231 | #ifdef SANSA_E200V2 | ||
232 | read_missed = false; | ||
233 | #endif | ||
234 | |||
235 | return _dbop_din; | ||
236 | } | ||
237 | |||
238 | /* for the debug menu */ | ||
239 | unsigned short button_dbop_data(void) | ||
240 | { | ||
241 | return _dbop_din; | ||
242 | } | ||
243 | |||
244 | static int button_gpio(void) | ||
245 | { | ||
246 | int btn = BUTTON_NONE; | ||
247 | if(hold_button) | ||
248 | return btn; | ||
249 | /* set afsel, so that we can read our buttons */ | ||
250 | GPIOC_AFSEL &= ~(1<<2|1<<3|1<<4|1<<5|1<<6); | ||
251 | /* set dir so we can read our buttons (but reset the C pins first) */ | ||
252 | GPIOB_DIR &= ~(1<<4); | ||
253 | GPIOC_DIR |= (1<<2|1<<3|1<<4|1<<5|1<<6); | ||
254 | GPIOC_PIN(2) = (1<<2); | ||
255 | GPIOC_PIN(3) = (1<<3); | ||
256 | GPIOC_PIN(4) = (1<<4); | ||
257 | GPIOC_PIN(5) = (1<<5); | ||
258 | GPIOC_PIN(6) = (1<<6); | ||
259 | |||
260 | GPIOC_DIR &= ~(1<<2|1<<3|1<<4|1<<5|1<<6); | ||
261 | |||
262 | /* small delay needed to read buttons correctly */ | ||
263 | button_delay(); | ||
264 | |||
265 | /* direct GPIO connections */ | ||
266 | if (!GPIOC_PIN(3)) | ||
267 | btn |= BUTTON_LEFT; | ||
268 | if (!GPIOC_PIN(2)) | ||
269 | btn |= BUTTON_UP; | ||
270 | if (!GPIOC_PIN(6)) | ||
271 | btn |= BUTTON_DOWN; | ||
272 | if (!GPIOC_PIN(5)) | ||
273 | btn |= BUTTON_RIGHT; | ||
274 | if (!GPIOC_PIN(4)) | ||
275 | btn |= BUTTON_SELECT; | ||
276 | /* return to settings needed for lcd */ | ||
277 | GPIOC_DIR |= (1<<2|1<<3|1<<4|1<<5|1<<6); | ||
278 | GPIOC_AFSEL |= (1<<2|1<<3|1<<4|1<<5|1<<6); | ||
279 | |||
280 | return btn; | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * Get button pressed from hardware | ||
285 | */ | ||
286 | int button_read_device(void) | ||
287 | { | ||
288 | int btn = BUTTON_NONE; | ||
289 | unsigned short dbop = button_read_dbop(); | ||
290 | #ifdef SANSA_FUZE | ||
291 | static unsigned power_counter = 0; | ||
292 | #endif | ||
293 | /* hold button */ | ||
294 | if(dbop & (1<<12)) | ||
295 | { | ||
296 | #ifdef SANSA_FUZE | ||
297 | power_counter = HZ; | ||
298 | #endif | ||
299 | hold_button = true; | ||
300 | } | ||
301 | else | ||
302 | { | ||
303 | hold_button = false; | ||
304 | #ifdef SANSA_FUZE | ||
305 | /* read power on bit 8, but not if hold button was just released, since | ||
306 | * you basically always hit power due to the slider mechanism after releasing | ||
307 | * (fuze only) | ||
308 | * hold (wait 1 sec) */ | ||
309 | if (power_counter) | ||
310 | power_counter--; | ||
311 | #endif | ||
312 | if (dbop & (1<<8) | ||
313 | #ifdef SANSA_FUZE | ||
314 | && !power_counter | ||
315 | #endif | ||
316 | ) | ||
317 | btn |= BUTTON_POWER; | ||
318 | /* read home on bit 15 */ | ||
319 | if (!(dbop & (1<<15))) | ||
320 | btn |= DBOP_BIT15_BUTTON; | ||
321 | |||
322 | btn |= button_gpio(); | ||
323 | } | ||
324 | |||
325 | #ifndef BOOTLOADER | ||
326 | /* light handling */ | ||
327 | if (hold_button != hold_button_old) | ||
328 | { | ||
329 | hold_button_old = hold_button; | ||
330 | backlight_hold_changed(hold_button); | ||
331 | } | ||
332 | #endif /* BOOTLOADER */ | ||
333 | |||
334 | return btn; | ||
335 | } | ||