d5ff84a75e2410faa13bfdc01cb1ca34ae1f314f
[Rockbox.git] / firmware / target / arm / sandisk / sansa-e200 / button-e200.c
blobd5ff84a75e2410faa13bfdc01cb1ca34ae1f314f
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_INTERVAL 300000
30 #define WHEEL_FAST_ON_INTERVAL 20000
31 #define WHEEL_FAST_OFF_INTERVAL 60000
32 #define WHEELCLICKS_PER_ROTATION 48 /* wheelclicks per full rotation */
34 /* Clickwheel */
35 #ifndef BOOTLOADER
36 static unsigned int old_wheel_value = 0;
37 static unsigned int wheel_repeat = BUTTON_NONE;
38 static unsigned int wheel_click_count = 0;
39 static unsigned int wheel_delta = 0;
40 static int wheel_fast_mode = 0;
41 static unsigned long last_wheel_usec = 0;
42 static unsigned long wheel_velocity = 0;
43 static long last_wheel_post = 0;
44 static long next_backlight_on = 0;
45 /* Buttons */
46 static bool hold_button = false;
47 static bool hold_button_old = false;
48 #define _button_hold() hold_button
49 #else
50 #define _button_hold() ((GPIOF_INPUT_VAL & 0x80) != 0)
51 #endif /* BOOTLOADER */
52 static int int_btn = BUTTON_NONE;
54 void button_int(void);
56 void button_init_device(void)
58 /* Enable all buttons */
59 GPIOF_OUTPUT_EN &= ~0xff;
60 GPIOF_ENABLE |= 0xff;
62 /* Scrollwheel light - enable control through GPIOG pin 7 and set timeout */
63 GPIOG_OUTPUT_EN |= 0x80;
64 GPIOG_ENABLE = 0x80;
66 #ifndef BOOTLOADER
67 /* Mask these before performing init ... because init has possibly
68 occurred before */
69 GPIOF_INT_EN &= ~0xff;
70 GPIOH_INT_EN &= ~0xc0;
72 /* Get current tick before enabling button interrupts */
73 last_wheel_usec = USEC_TIMER;
74 last_wheel_post = last_wheel_usec;
76 GPIOH_ENABLE |= 0xc0;
77 GPIOH_OUTPUT_EN &= ~0xc0;
79 /* Read initial buttons */
80 button_int();
81 GPIOF_INT_CLR = 0xff;
83 /* Read initial wheel value (bit 6-7 of GPIOH) */
84 old_wheel_value = GPIOH_INPUT_VAL & 0xc0;
85 GPIOH_INT_LEV = (GPIOH_INT_LEV & ~0xc0) | (old_wheel_value ^ 0xc0);
86 GPIOH_INT_CLR = 0xc0;
88 /* Enable button interrupts */
89 GPIOF_INT_EN |= 0xff;
90 GPIOH_INT_EN |= 0xc0;
92 CPU_INT_EN = HI_MASK;
93 CPU_HI_INT_EN = GPIO1_MASK;
94 #endif /* BOOTLOADER */
97 bool button_hold(void)
99 return _button_hold();
102 /* clickwheel */
103 #ifndef BOOTLOADER
104 void clickwheel_int(void)
106 /* Read wheel
107 * Bits 6 and 7 of GPIOH change as follows:
108 * Clockwise rotation 01 -> 00 -> 10 -> 11
109 * Counter-clockwise 11 -> 10 -> 00 -> 01
111 * This is equivalent to wheel_value of:
112 * Clockwise rotation 0x40 -> 0x00 -> 0x80 -> 0xc0
113 * Counter-clockwise 0xc0 -> 0x80 -> 0x00 -> 0x40
115 static const unsigned char wheel_tbl[2][4] =
117 /* 0x00 0x40 0x80 0xc0 */ /* Wheel value */
118 { 0x40, 0xc0, 0x00, 0x80 }, /* Clockwise rotation */
119 { 0x80, 0x00, 0xc0, 0x40 }, /* Counter-clockwise */
122 unsigned int wheel_value;
124 wheel_value = GPIOH_INPUT_VAL & 0xc0;
125 GPIOH_INT_LEV = (GPIOH_INT_LEV & ~0xc0) | (wheel_value ^ 0xc0);
126 GPIOH_INT_CLR = GPIOH_INT_STAT & 0xc0;
128 if (!hold_button)
130 unsigned int btn = BUTTON_NONE;
132 if (old_wheel_value == wheel_tbl[0][wheel_value >> 6])
133 btn = BUTTON_SCROLL_FWD;
134 else if (old_wheel_value == wheel_tbl[1][wheel_value >> 6])
135 btn = BUTTON_SCROLL_BACK;
137 if (btn != BUTTON_NONE)
139 int repeat = 1; /* assume repeat */
140 unsigned long usec = USEC_TIMER;
141 unsigned v = (usec - last_wheel_usec) & 0x7fffffff;
143 v = (v>0) ? 1000000 / v : 0; /* clicks/sec = 1000000 * clicks/usec */
144 v = (v>0xffffff) ? 0xffffff : v; /* limit to 24 bit */
146 /* some velocity filtering to smooth things out */
147 wheel_velocity = (7*wheel_velocity + v) / 8;
149 if (btn != wheel_repeat)
151 /* direction reversals nullify all fast mode states */
152 wheel_repeat = btn;
153 repeat =
154 wheel_fast_mode =
155 wheel_velocity =
156 wheel_click_count = 0;
159 if (wheel_fast_mode != 0)
161 /* fast OFF happens immediately when velocity drops below
162 threshold */
163 if (TIME_AFTER(usec,
164 last_wheel_usec + WHEEL_FAST_OFF_INTERVAL))
166 /* moving out of fast mode */
167 wheel_fast_mode = 0;
168 /* reset velocity */
169 wheel_velocity = 0;
170 /* wheel_delta is always 1 in slow mode */
171 wheel_delta = 1;
174 else
176 /* fast ON gets filtered to avoid inadvertent jumps to fast mode */
177 if (repeat && wheel_velocity > 1000000/WHEEL_FAST_ON_INTERVAL)
179 /* moving into fast mode */
180 wheel_fast_mode = 1 << 31;
181 wheel_click_count = 0;
182 wheel_velocity = 1000000/WHEEL_FAST_OFF_INTERVAL;
184 else if (++wheel_click_count < 2)
186 btn = BUTTON_NONE;
189 /* wheel_delta is always 1 in slow mode */
190 wheel_delta = 1;
193 if (TIME_AFTER(current_tick, next_backlight_on) ||
194 v <= 4)
196 /* poke backlight to turn it on or maintain it no more often
197 than every 1/4 second*/
198 next_backlight_on = current_tick + HZ/4;
199 backlight_on();
200 buttonlight_on();
201 reset_poweroff_timer();
204 if (btn != BUTTON_NONE)
206 wheel_click_count = 0;
208 /* generate repeats if quick enough */
209 if (repeat && TIME_BEFORE(usec,
210 last_wheel_post + WHEEL_REPEAT_INTERVAL))
211 btn |= BUTTON_REPEAT;
213 last_wheel_post = usec;
215 if (queue_empty(&button_queue))
217 queue_post(&button_queue, btn, wheel_fast_mode |
218 (wheel_delta << 24) | wheel_velocity*360/WHEELCLICKS_PER_ROTATION);
219 /* message posted - reset delta */
220 wheel_delta = 1;
222 else
224 /* skipped post - increment delta */
225 if (++wheel_delta > 0x7f)
226 wheel_delta = 0x7f;
230 last_wheel_usec = usec;
234 old_wheel_value = wheel_value;
236 #endif /* BOOTLOADER */
238 /* device buttons */
239 void button_int(void)
241 unsigned char state;
243 int_btn = BUTTON_NONE;
245 state = GPIOF_INPUT_VAL & 0xff;
247 #ifndef BOOTLOADER
248 GPIOF_INT_LEV = (GPIOF_INT_LEV & ~0xff) | (state ^ 0xff);
249 GPIOF_INT_CLR = GPIOF_INT_STAT;
251 hold_button = (state & 0x80) != 0;
252 #endif
254 if (!_button_hold())
256 /* Read normal buttons */
257 if ((state & 0x01) == 0) int_btn |= BUTTON_REC;
258 if ((state & 0x02) == 0) int_btn |= BUTTON_DOWN;
259 if ((state & 0x04) == 0) int_btn |= BUTTON_RIGHT;
260 if ((state & 0x08) == 0) int_btn |= BUTTON_LEFT;
261 if ((state & 0x10) == 0) int_btn |= BUTTON_SELECT; /* The centre button */
262 if ((state & 0x20) == 0) int_btn |= BUTTON_UP; /* The "play" button */
263 if ((state & 0x40) != 0) int_btn |= BUTTON_POWER;
268 * Get button pressed from hardware
270 int button_read_device(void)
272 #ifdef BOOTLOADER
273 /* Read buttons directly in the bootloader */
274 button_int();
275 #else
276 /* light handling */
277 if (hold_button != hold_button_old)
279 hold_button_old = hold_button;
280 backlight_hold_changed(hold_button);
282 #endif /* BOOTLOADER */
284 /* The int_btn variable is set in the button interrupt handler */
285 return int_btn;