1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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. */
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 */
38 static int read_wheel_keycode(void);
40 static bool hold_button
= false;
41 static bool hold_button_old
= false;
42 #define _button_hold() hold_button
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);
59 /* Mask these before performing init ... because init has possibly
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 */
70 /* Read initial wheel value (bit 6-7 of GPIOH) */
73 /* Enable button interrupts */
74 GPIO_SET_BITWISE(GPIOF_INT_EN
, 0xff);
75 GPIO_SET_BITWISE(GPIOH_INT_EN
, 0xc0);
78 CPU_HI_INT_EN
= GPIO1_MASK
;
79 #endif /* BOOTLOADER */
82 bool button_hold(void)
84 return _button_hold();
89 static int read_wheel_keycode(void)
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;
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
;
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
)
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
;
162 prev_usec
[0] = usec
- usec_back
;
163 prev_usec
[1] = prev_usec
[0] - usec_back
;
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;
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];
184 /* timer wrapped (no activity for awhile), skip acceleration */
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
);
197 v
= 0xfffffful
; /* limit to 24 bits */
200 prev_usec
[1] = prev_usec
[0];
203 if (v
< WHEEL_SMOOTHING_VELOCITY
)
205 /* Very slow - no smoothing */
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;
213 /* Some velocity filtering to smooth things out */
214 velocity
= (7*velocity
+ v
) / 8;
219 /* Fast OFF happens immediately when velocity drops below
221 if (v
< WHEEL_FAST_OFF_VELOCITY
)
223 fast_mode
= 0; /* moving out of fast mode */
226 /* delta is always 1 in slow mode */
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 */
240 count
+= fast_mode
+ 1;
241 if (count
< WHEEL_BASE_SENSITIVITY
)
246 if (queue_empty(&button_queue
))
248 /* Post wheel keycode with wheel data */
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 */
266 /* Skipped post - increment delta and limit to 7 bits */
269 if (delta
> (0x7ful
<< 24))
270 delta
= 0x7ful
<< 24;
273 #endif /* BOOTLOADER */
276 void button_int(void)
278 unsigned long state
= GPIOF_INPUT_VAL
& 0xff;
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;
289 int_btn
= BUTTON_NONE
;
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)
310 /* Read buttons directly in the bootloader */
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 */