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_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 */
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;
46 static bool hold_button
= false;
47 static bool hold_button_old
= false;
48 #define _button_hold() hold_button
50 #define _button_hold() ((GPIOF_INPUT_VAL & 0x80) != 0)
51 #endif /* BOOTLOADER */
52 static int int_btn
= BUTTON_NONE
;
54 void button_init_device(void)
56 /* Enable all buttons */
57 GPIOF_OUTPUT_EN
&= ~0xff;
60 /* Scrollwheel light - enable control through GPIOG pin 7 and set timeout */
61 GPIOG_OUTPUT_EN
|= 0x80;
65 /* Mask these before performing init ... because init has possibly
67 GPIOF_INT_EN
&= ~0xff;
68 GPIOH_INT_EN
&= ~0xc0;
70 /* Get current tick before enabling button interrupts */
71 last_wheel_usec
= USEC_TIMER
;
72 last_wheel_post
= last_wheel_usec
;
75 GPIOH_OUTPUT_EN
&= ~0xc0;
77 /* Read initial buttons */
81 /* Read initial wheel value (bit 6-7 of GPIOH) */
82 old_wheel_value
= GPIOH_INPUT_VAL
& 0xc0;
83 GPIOH_INT_LEV
= (GPIOH_INT_LEV
& ~0xc0) | (old_wheel_value
^ 0xc0);
86 /* Enable button interrupts */
91 CPU_HI_INT_EN
= GPIO1_MASK
;
92 #endif /* BOOTLOADER */
95 bool button_hold(void)
97 return _button_hold();
102 void clickwheel_int(void)
105 * Bits 6 and 7 of GPIOH change as follows:
106 * Clockwise rotation 01 -> 00 -> 10 -> 11
107 * Counter-clockwise 11 -> 10 -> 00 -> 01
109 * This is equivalent to wheel_value of:
110 * Clockwise rotation 0x40 -> 0x00 -> 0x80 -> 0xc0
111 * Counter-clockwise 0xc0 -> 0x80 -> 0x00 -> 0x40
113 static const unsigned char wheel_tbl
[2][4] =
115 /* 0x00 0x40 0x80 0xc0 */ /* Wheel value */
116 { 0x40, 0xc0, 0x00, 0x80 }, /* Clockwise rotation */
117 { 0x80, 0x00, 0xc0, 0x40 }, /* Counter-clockwise */
120 unsigned int wheel_value
;
122 wheel_value
= GPIOH_INPUT_VAL
& 0xc0;
123 GPIOH_INT_LEV
= (GPIOH_INT_LEV
& ~0xc0) | (wheel_value
^ 0xc0);
124 GPIOH_INT_CLR
= GPIOH_INT_STAT
& 0xc0;
128 unsigned int btn
= BUTTON_NONE
;
130 if (old_wheel_value
== wheel_tbl
[0][wheel_value
>> 6])
131 btn
= BUTTON_SCROLL_FWD
;
132 else if (old_wheel_value
== wheel_tbl
[1][wheel_value
>> 6])
133 btn
= BUTTON_SCROLL_BACK
;
135 if (btn
!= BUTTON_NONE
)
137 int repeat
= 1; /* assume repeat */
138 unsigned long usec
= USEC_TIMER
;
139 unsigned v
= (usec
- last_wheel_usec
) & 0x7fffffff;
141 v
= (v
>0) ? 1000000 / v
: 0; /* clicks/sec = 1000000 * clicks/usec */
142 v
= (v
>0xffffff) ? 0xffffff : v
; /* limit to 24 bit */
144 /* some velocity filtering to smooth things out */
145 wheel_velocity
= (7*wheel_velocity
+ v
) / 8;
147 if (btn
!= wheel_repeat
)
149 /* direction reversals nullify all fast mode states */
154 wheel_click_count
= 0;
157 if (wheel_fast_mode
!= 0)
159 /* fast OFF happens immediately when velocity drops below
162 last_wheel_usec
+ WHEEL_FAST_OFF_INTERVAL
))
164 /* moving out of fast mode */
168 /* wheel_delta is always 1 in slow mode */
174 /* fast ON gets filtered to avoid inadvertent jumps to fast mode */
175 if (repeat
&& wheel_velocity
> 1000000/WHEEL_FAST_ON_INTERVAL
)
177 /* moving into fast mode */
178 wheel_fast_mode
= 1 << 31;
179 wheel_click_count
= 0;
180 wheel_velocity
= 1000000/WHEEL_FAST_OFF_INTERVAL
;
182 else if (++wheel_click_count
< 2)
187 /* wheel_delta is always 1 in slow mode */
191 if (TIME_AFTER(current_tick
, next_backlight_on
) ||
194 /* poke backlight to turn it on or maintain it no more often
195 than every 1/4 second*/
196 next_backlight_on
= current_tick
+ HZ
/4;
199 reset_poweroff_timer();
202 if (btn
!= BUTTON_NONE
)
204 wheel_click_count
= 0;
206 /* generate repeats if quick enough */
207 if (repeat
&& TIME_BEFORE(usec
,
208 last_wheel_post
+ WHEEL_REPEAT_INTERVAL
))
209 btn
|= BUTTON_REPEAT
;
211 last_wheel_post
= usec
;
213 if (queue_empty(&button_queue
))
215 queue_post(&button_queue
, btn
, wheel_fast_mode
|
216 (wheel_delta
<< 24) | wheel_velocity
*360/WHEELCLICKS_PER_ROTATION
);
217 /* message posted - reset delta */
222 /* skipped post - increment delta */
223 if (++wheel_delta
> 0x7f)
228 last_wheel_usec
= usec
;
232 old_wheel_value
= wheel_value
;
234 #endif /* BOOTLOADER */
237 void button_int(void)
241 int_btn
= BUTTON_NONE
;
243 state
= GPIOF_INPUT_VAL
& 0xff;
246 GPIOF_INT_LEV
= (GPIOF_INT_LEV
& ~0xff) | (state
^ 0xff);
247 GPIOF_INT_CLR
= GPIOF_INT_STAT
;
249 hold_button
= (state
& 0x80) != 0;
254 /* Read normal buttons */
255 if ((state
& 0x01) == 0) int_btn
|= BUTTON_REC
;
256 if ((state
& 0x02) == 0) int_btn
|= BUTTON_DOWN
;
257 if ((state
& 0x04) == 0) int_btn
|= BUTTON_RIGHT
;
258 if ((state
& 0x08) == 0) int_btn
|= BUTTON_LEFT
;
259 if ((state
& 0x10) == 0) int_btn
|= BUTTON_SELECT
; /* The centre button */
260 if ((state
& 0x20) == 0) int_btn
|= BUTTON_UP
; /* The "play" button */
261 if ((state
& 0x40) != 0) int_btn
|= BUTTON_POWER
;
266 * Get button pressed from hardware
268 int button_read_device(void)
271 /* Read buttons directly in the bootloader */
275 if (hold_button
!= hold_button_old
)
277 hold_button_old
= hold_button
;
278 backlight_hold_changed(hold_button
);
280 #endif /* BOOTLOADER */
282 /* The int_btn variable is set in the button interrupt handler */