e200v1: What's the wheel deal? Rework wheel code to make it a bit more straightforwar...
[kugel-rb.git] / firmware / target / arm / sandisk / sansa-e200 / button-e200.c
blobbe4a34d49f78c891dce17fdf3d5fef626c27a538
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 buttonlight_on();
175 reset_poweroff_timer();
178 /* Calculate deg/s based upon interval and number of clicks in that
179 * interval - FIR moving average */
180 v = usec - prev_usec[1];
182 if ((long)v <= 0)
184 /* timer wrapped (no activity for awhile), skip acceleration */
185 v = 0;
186 delta = 1ul << 24;
188 else
190 /* Check overflow below */
191 if (v > 0xfffffffful / WHEELCLICKS_PER_ROTATION)
192 v = 0xfffffffful / WHEELCLICKS_PER_ROTATION;
194 v = 360000000ul*WHEEL_BASE_SENSITIVITY / (v*WHEELCLICKS_PER_ROTATION);
196 if (v > 0xfffffful)
197 v = 0xfffffful; /* limit to 24 bits */
200 prev_usec[1] = prev_usec[0];
201 prev_usec[0] = usec;
203 if (v < WHEEL_SMOOTHING_VELOCITY)
205 /* Very slow - no smoothing */
206 velocity = v;
207 /* Ensure backlight never gets stuck for an extended period if tick
208 * wrapped such that next poke is very far ahead */
209 next_backlight_on = current_tick - 1;
211 else
213 /* Some velocity filtering to smooth things out */
214 velocity = (7*velocity + v) / 8;
217 if (fast_mode != 0)
219 /* Fast OFF happens immediately when velocity drops below
220 threshold */
221 if (v < WHEEL_FAST_OFF_VELOCITY)
223 fast_mode = 0; /* moving out of fast mode */
224 velocity = v;
226 /* delta is always 1 in slow mode */
227 delta = 1ul << 24;
230 else
232 /* Fast ON gets filtered to avoid inadvertent jumps to fast mode */
233 if (velocity >= WHEEL_FAST_ON_VELOCITY)
234 fast_mode = 1; /* moving into fast mode */
236 /* delta is always 1 in slow mode */
237 delta = 1ul << 24;
240 count += fast_mode + 1;
241 if (count < WHEEL_BASE_SENSITIVITY)
242 return;
244 count = 0;
246 if (queue_empty(&button_queue))
248 /* Post wheel keycode with wheel data */
249 int key = keycode;
251 if (v >= WHEEL_REPEAT_VELOCITY && prev_keypost == key)
253 /* Quick enough and same key is being posted more than once in a
254 * row - generate repeats - use unsmoothed v */
255 key |= BUTTON_REPEAT;
258 prev_keypost = keycode;
260 queue_post(&button_queue, key, (fast_mode << 31) | delta | velocity);
261 /* Message posted - reset delta */
262 delta = 1ul << 24;
264 else
266 /* Skipped post - increment delta and limit to 7 bits */
267 delta += 1ul << 24;
269 if (delta > (0x7ful << 24))
270 delta = 0x7ful << 24;
273 #endif /* BOOTLOADER */
275 /* device buttons */
276 void button_int(void)
278 unsigned long state = GPIOF_INPUT_VAL & 0xff;
280 #ifndef BOOTLOADER
281 unsigned long status = GPIOF_INT_STAT;
283 GPIO_WRITE_BITWISE(GPIOF_INT_LEV, state ^ 0xff, 0xff);
284 GPIOF_INT_CLR = status;
286 hold_button = (state & 0x80) != 0;
287 #endif
289 int_btn = BUTTON_NONE;
291 if (!_button_hold())
293 /* Read normal buttons */
294 if ((state & 0x01) == 0) int_btn |= BUTTON_REC;
295 if ((state & 0x02) == 0) int_btn |= BUTTON_DOWN;
296 if ((state & 0x04) == 0) int_btn |= BUTTON_RIGHT;
297 if ((state & 0x08) == 0) int_btn |= BUTTON_LEFT;
298 if ((state & 0x10) == 0) int_btn |= BUTTON_SELECT; /* The centre button */
299 if ((state & 0x20) == 0) int_btn |= BUTTON_UP; /* The "play" button */
300 if ((state & 0x40) != 0) int_btn |= BUTTON_POWER;
305 * Get button pressed from hardware
307 int button_read_device(void)
309 #ifdef BOOTLOADER
310 /* Read buttons directly in the bootloader */
311 button_int();
312 #else
313 /* light handling */
314 if (hold_button != hold_button_old)
316 hold_button_old = hold_button;
317 backlight_hold_changed(hold_button);
319 #endif /* BOOTLOADER */
321 /* The int_btn variable is set in the button interrupt handler */
322 return int_btn;