e200v1 seems to be ok using USB-enabled bootloader. Also, include the bootloader...
[kugel-rb.git] / firmware / target / arm / sandisk / sansa-e200 / button-e200.c
blob1e952b3882b8af406aba253ab27f9c9bd28bd94e
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 by Barry Wardell
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /* Taken from button-h10.c by Barry Wardell and reverse engineering by MrH. */
24 #include "system.h"
25 #include "button.h"
26 #include "backlight.h"
27 #include "powermgmt.h"
29 #define WHEEL_REPEAT_VELOCITY 35 /* deg/s */
30 #define WHEEL_SMOOTHING_VELOCITY 50 /* deg/s */
31 #define WHEEL_FAST_ON_VELOCITY 350 /* deg/s */
32 #define WHEEL_FAST_OFF_VELOCITY 150 /* deg/s */
33 #define WHEEL_BASE_SENSITIVITY 2
34 #define WHEELCLICKS_PER_ROTATION 48 /* wheelclicks per full rotation */
36 #ifndef BOOTLOADER
37 /* Wheel */
38 static int read_wheel_keycode(void);
39 /* Buttons */
40 static bool hold_button = false;
41 static bool hold_button_old = false;
42 #define _button_hold() hold_button
43 #else
44 #define _button_hold() ((GPIOF_INPUT_VAL & 0x80) != 0)
45 #endif /* BOOTLOADER */
46 static int int_btn = BUTTON_NONE;
48 void button_init_device(void)
50 /* Enable all buttons */
51 GPIO_CLEAR_BITWISE(GPIOF_OUTPUT_EN, 0xff);
52 GPIO_SET_BITWISE(GPIOF_ENABLE, 0xff);
54 /* Scrollwheel light - enable control through GPIOG pin 7 and set timeout */
55 GPIO_SET_BITWISE(GPIOG_OUTPUT_EN, 0x80);
56 GPIO_SET_BITWISE(GPIOG_ENABLE, 0x80);
58 #ifndef BOOTLOADER
59 /* Mask these before performing init ... because init has possibly
60 occurred before */
61 GPIO_CLEAR_BITWISE(GPIOF_INT_EN, 0xff);
62 GPIO_CLEAR_BITWISE(GPIOH_INT_EN, 0xc0);
64 GPIO_CLEAR_BITWISE(GPIOH_OUTPUT_EN, 0xc0);
65 GPIO_SET_BITWISE(GPIOH_ENABLE, 0xc0);
67 /* Read initial buttons */
68 button_int();
70 /* Read initial wheel value (bit 6-7 of GPIOH) */
71 read_wheel_keycode();
73 /* Enable button interrupts */
74 GPIO_SET_BITWISE(GPIOF_INT_EN, 0xff);
75 GPIO_SET_BITWISE(GPIOH_INT_EN, 0xc0);
77 CPU_INT_EN = HI_MASK;
78 CPU_HI_INT_EN = GPIO1_MASK;
79 #endif /* BOOTLOADER */
82 bool button_hold(void)
84 return _button_hold();
87 /* clickwheel */
88 #ifndef BOOTLOADER
89 static int read_wheel_keycode(void)
91 /* Read wheel
92 * Bits 6 and 7 of GPIOH change as follows:
93 * Clockwise rotation 01 -> 00 -> 10 -> 11
94 * Counter-clockwise 11 -> 10 -> 00 -> 01
96 * This is equivalent to wheel_value of:
97 * Clockwise rotation 0x40 -> 0x00 -> 0x80 -> 0xc0
98 * Counter-clockwise 0xc0 -> 0x80 -> 0x00 -> 0x40
100 static const unsigned char wheel_tbl[2][4] =
102 /* 0x00 0x40 0x80 0xc0 */ /* Wheel value */
103 { 0x40, 0xc0, 0x00, 0x80 }, /* Clockwise rotation */
104 { 0x80, 0x00, 0xc0, 0x40 }, /* Counter-clockwise */
107 static unsigned long prev_value;
109 int keycode = BUTTON_NONE;
111 unsigned long value = GPIOH_INPUT_VAL & 0xc0;
112 GPIO_WRITE_BITWISE(GPIOH_INT_LEV, value ^ 0xc0, 0xc0);
113 GPIOH_INT_CLR = GPIOH_INT_STAT & 0xc0;
115 if (!hold_button)
117 unsigned long prev = prev_value;
118 int scroll = value >> 6;
120 if (prev == wheel_tbl[0][scroll])
121 keycode = BUTTON_SCROLL_FWD;
122 else if (prev == wheel_tbl[1][scroll])
123 keycode = BUTTON_SCROLL_BACK;
126 prev_value = value;
128 return keycode;
131 void clickwheel_int(void)
133 static int prev_keycode = BUTTON_NONE;
134 static int prev_keypost = BUTTON_NONE;
135 static int count = 0;
136 static int fast_mode = 0;
137 static long next_backlight_on = 0;
139 static unsigned long prev_usec[WHEEL_BASE_SENSITIVITY] = { 0 };
140 static unsigned long delta = 1ul << 24;
141 static unsigned long velocity = 0; /* Velocity smoothed or unsmoothed */
143 unsigned long usec = USEC_TIMER;
144 unsigned long v; /* Raw velocity */
146 int keycode = read_wheel_keycode();
148 if (keycode == BUTTON_NONE)
149 return;
151 /* Spurious wheel "reversals" are not uncommon. Resetting also helps
152 * cover them up. As such, prev_keypost is also not reset or else false
153 * non-repeats are generated when it happens. */
154 if (keycode != prev_keycode)
156 /* Direction reversals reset state */
157 unsigned long usec_back = 360000000ul /
158 (WHEEL_REPEAT_VELOCITY*WHEELCLICKS_PER_ROTATION);
159 prev_keycode = keycode;
160 count = 0;
161 fast_mode = 0;
162 prev_usec[0] = usec - usec_back;
163 prev_usec[1] = prev_usec[0] - usec_back;
164 delta = 1ul << 24;
165 velocity = 0;
168 if (TIME_AFTER(current_tick, next_backlight_on))
170 /* Poke backlight to turn it on or maintain it no more often
171 * than every 1/4 second */
172 next_backlight_on = current_tick + HZ/4;
173 backlight_on();
174 #ifdef HAVE_BUTTON_LIGHT
175 buttonlight_on();
176 #endif
177 reset_poweroff_timer();
180 /* Calculate deg/s based upon interval and number of clicks in that
181 * interval - FIR moving average */
182 v = usec - prev_usec[1];
184 if ((long)v <= 0)
186 /* timer wrapped (no activity for awhile), skip acceleration */
187 v = 0;
188 delta = 1ul << 24;
190 else
192 /* Check overflow below */
193 if (v > 0xfffffffful / WHEELCLICKS_PER_ROTATION)
194 v = 0xfffffffful / WHEELCLICKS_PER_ROTATION;
196 v = 360000000ul*WHEEL_BASE_SENSITIVITY / (v*WHEELCLICKS_PER_ROTATION);
198 if (v > 0xfffffful)
199 v = 0xfffffful; /* limit to 24 bits */
202 prev_usec[1] = prev_usec[0];
203 prev_usec[0] = usec;
205 if (v < WHEEL_SMOOTHING_VELOCITY)
207 /* Very slow - no smoothing */
208 velocity = v;
209 /* Ensure backlight never gets stuck for an extended period if tick
210 * wrapped such that next poke is very far ahead */
211 next_backlight_on = current_tick - 1;
213 else
215 /* Some velocity filtering to smooth things out */
216 velocity = (7*velocity + v) / 8;
219 if (fast_mode != 0)
221 /* Fast OFF happens immediately when velocity drops below
222 threshold */
223 if (v < WHEEL_FAST_OFF_VELOCITY)
225 fast_mode = 0; /* moving out of fast mode */
226 velocity = v;
228 /* delta is always 1 in slow mode */
229 delta = 1ul << 24;
232 else
234 /* Fast ON gets filtered to avoid inadvertent jumps to fast mode */
235 if (velocity >= WHEEL_FAST_ON_VELOCITY)
236 fast_mode = 1; /* moving into fast mode */
238 /* delta is always 1 in slow mode */
239 delta = 1ul << 24;
242 count += fast_mode + 1;
243 if (count < WHEEL_BASE_SENSITIVITY)
244 return;
246 count = 0;
248 if (queue_empty(&button_queue))
250 /* Post wheel keycode with wheel data */
251 int key = keycode;
253 if (v >= WHEEL_REPEAT_VELOCITY && prev_keypost == key)
255 /* Quick enough and same key is being posted more than once in a
256 * row - generate repeats - use unsmoothed v */
257 key |= BUTTON_REPEAT;
260 prev_keypost = keycode;
262 queue_post(&button_queue, key, (fast_mode << 31) | delta | velocity);
263 /* Message posted - reset delta */
264 delta = 1ul << 24;
266 else
268 /* Skipped post - increment delta and limit to 7 bits */
269 delta += 1ul << 24;
271 if (delta > (0x7ful << 24))
272 delta = 0x7ful << 24;
275 #else
276 void clickwheel_int(void)
279 #endif /* BOOTLOADER */
281 /* device buttons */
282 void button_int(void)
284 unsigned long state = GPIOF_INPUT_VAL & 0xff;
286 #ifndef BOOTLOADER
287 unsigned long status = GPIOF_INT_STAT;
289 GPIO_WRITE_BITWISE(GPIOF_INT_LEV, state ^ 0xff, 0xff);
290 GPIOF_INT_CLR = status;
292 hold_button = (state & 0x80) != 0;
293 #endif
295 int_btn = BUTTON_NONE;
297 if (!_button_hold())
299 /* Read normal buttons */
300 if ((state & 0x01) == 0) int_btn |= BUTTON_REC;
301 if ((state & 0x02) == 0) int_btn |= BUTTON_DOWN;
302 if ((state & 0x04) == 0) int_btn |= BUTTON_RIGHT;
303 if ((state & 0x08) == 0) int_btn |= BUTTON_LEFT;
304 if ((state & 0x10) == 0) int_btn |= BUTTON_SELECT; /* The centre button */
305 if ((state & 0x20) == 0) int_btn |= BUTTON_UP; /* The "play" button */
306 if ((state & 0x40) != 0) int_btn |= BUTTON_POWER;
311 * Get button pressed from hardware
313 int button_read_device(void)
315 #ifdef BOOTLOADER
316 /* Read buttons directly in the bootloader */
317 button_int();
318 #else
319 /* light handling */
320 if (hold_button != hold_button_old)
322 hold_button_old = hold_button;
323 backlight_hold_changed(hold_button);
325 #endif /* BOOTLOADER */
327 /* The int_btn variable is set in the button interrupt handler */
328 return int_btn;