1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Daniel Stenberg
12 * iPod driver based on code from the ipodlinux project - http://ipodlinux.org
13 * Adapted for Rockbox in December 2005
14 * Original file: linux/arch/armnommu/mach-ipod/keyboard.c
15 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
24 * KIND, either express or implied.
26 ****************************************************************************/
29 * Rockbox button functions
38 #include "backlight.h"
41 #include "powermgmt.h"
44 static int int_btn
= BUTTON_NONE
;
46 /* The 1st Gen wheel draws ~12mA when enabled permanently. Therefore
47 * we only enable it for a very short time to check for changes every
48 * tick, and only keep it enabled if there is activity. */
49 #define WHEEL_TIMEOUT (HZ/4)
53 #define WHEELCLICKS_PER_ROTATION 96
54 #define WHEEL_BASE_SENSITIVITY 6 /* Compute every ... clicks */
55 #define WHEEL_REPEAT_VELOCITY 45 /* deg/s */
56 #define WHEEL_SMOOTHING_VELOCITY 100 /* deg/s */
58 static void handle_scroll_wheel(int new_scroll
, int was_hold
)
60 static const signed char scroll_state
[4][4] = {
67 static int prev_scroll
= 0;
68 static int direction
= 0;
70 static long next_backlight_on
= 0;
72 static unsigned long last_wheel_usec
= 0;
73 static unsigned long wheel_delta
= 1ul << 24;
74 static unsigned long wheel_velocity
= 0;
76 int wheel_keycode
= BUTTON_NONE
;
77 int scroll
= scroll_state
[prev_scroll
][new_scroll
];
81 prev_scroll
= new_scroll
;
83 if (direction
!= scroll
) {
84 /* direction reversal - reset all */
87 wheel_delta
= 1ul << 24;
92 /* hold - reset all */
94 wheel_delta
= 1ul << 24;
99 /* poke backlight every 1/4s of activity */
100 if (TIME_AFTER(current_tick
, next_backlight_on
)) {
102 reset_poweroff_timer();
103 next_backlight_on
= current_tick
+ HZ
/4;
106 /* has wheel travelled far enough? */
107 if (++count
< WHEEL_BASE_SENSITIVITY
) {
111 /* reset travel count and do calculations */
114 /* 1st..3rd Gen wheel has inverse direction mapping
115 * compared to Mini 1st Gen wheel. */
118 wheel_keycode
= BUTTON_SCROLL_BACK
;
121 wheel_keycode
= BUTTON_SCROLL_FWD
;
124 /* only happens if we get out of sync */
131 v
= usec
- last_wheel_usec
;
133 /* calculate deg/s based upon sensitivity-adjusted interrupt period */
136 /* timer wrapped (no activity for awhile), skip acceleration */
138 wheel_delta
= 1ul << 24;
141 if (v
> 0xfffffffful
/WHEELCLICKS_PER_ROTATION
) {
142 v
= 0xfffffffful
/WHEELCLICKS_PER_ROTATION
; /* check overflow below */
145 v
= 360000000ul*WHEEL_BASE_SENSITIVITY
/ (v
*WHEELCLICKS_PER_ROTATION
);
148 v
= 0xfffffful
; /* limit to 24 bits */
151 if (v
< WHEEL_SMOOTHING_VELOCITY
) {
152 /* very slow - no smoothing */
156 /* some velocity filtering to smooth things out */
157 wheel_velocity
= (7*wheel_velocity
+ v
) / 8;
160 if (v
>= WHEEL_REPEAT_VELOCITY
) {
161 /* quick enough - generate repeats - use unsmoothed v to guage */
162 wheel_keycode
|= BUTTON_REPEAT
;
165 if (queue_empty(&button_queue
)) {
166 /* post wheel keycode with wheel data */
167 queue_post(&button_queue
, wheel_keycode
,
168 (wheel_velocity
>= WHEEL_ACCEL_START
? (1ul << 31) : 0)
169 | wheel_delta
| wheel_velocity
);
170 /* message posted - reset delta */
171 wheel_delta
= 1ul << 24;
174 /* skipped post - increment delta and limit to 7 bits */
175 wheel_delta
+= 1ul << 24;
177 if (wheel_delta
> (0x7ful
<< 24))
178 wheel_delta
= 0x7ful
<< 24;
181 last_wheel_usec
= usec
;
184 static void handle_scroll_wheel(int new_scroll
, int was_hold
)
186 int wheel_keycode
= BUTTON_NONE
;
187 static int prev_scroll
= -1;
188 static int direction
= 0;
189 static int count
= 0;
190 static int scroll_state
[4][4] = {
197 if ( prev_scroll
== -1 ) {
198 prev_scroll
= new_scroll
;
200 else if (direction
!= scroll_state
[prev_scroll
][new_scroll
]) {
201 direction
= scroll_state
[prev_scroll
][new_scroll
];
204 else if (!was_hold
) {
206 reset_poweroff_timer();
207 if (++count
== 6) { /* reduce sensitivity */
209 /* 1st..3rd Gen wheel has inverse direction mapping
210 * compared to Mini 1st Gen wheel. */
213 wheel_keycode
= BUTTON_SCROLL_BACK
;
216 wheel_keycode
= BUTTON_SCROLL_FWD
;
219 /* only happens if we get out of sync */
224 if (wheel_keycode
!= BUTTON_NONE
&& queue_empty(&button_queue
))
225 queue_post(&button_queue
, wheel_keycode
, 0);
226 prev_scroll
= new_scroll
;
230 static int ipod_3g_button_read(void)
232 unsigned char source
, state
;
233 static bool was_hold
= false;
234 int btn
= BUTTON_NONE
;
236 /* get source of interupts */
237 source
= GPIOA_INT_STAT
;
239 /* get current keypad status */
240 state
= GPIOA_INPUT_VAL
;
242 /* toggle interrupt level */
243 GPIOA_INT_LEV
= ~state
;
246 if (was_hold
&& source
== 0x40 && state
== 0xbf) {
247 /* ack any active interrupts */
248 GPIOA_INT_CLR
= source
;
253 if ((state
& 0x20) == 0) {
254 /* 3g hold switch is active low */
256 /* hold switch on 3g causes all outputs to go low */
257 /* we shouldn't interpret these as key presses */
258 GPIOA_INT_CLR
= source
;
261 #elif defined IPOD_1G2G
263 /* 1g/2g hold switch is active high */
264 GPIOA_INT_CLR
= source
;
268 if ((state
& 0x1) == 0) {
271 if ((state
& 0x2) == 0) {
272 btn
|= BUTTON_SELECT
;
274 if ((state
& 0x4) == 0) {
277 if ((state
& 0x8) == 0) {
280 if ((state
& 0x10) == 0) {
285 handle_scroll_wheel((state
& 0xc0) >> 6, was_hold
);
288 /* ack any active interrupts */
289 GPIOA_INT_CLR
= source
;
294 void ipod_3g_button_int(void)
296 CPU_INT_DIS
= GPIO_MASK
;
297 int_btn
= ipod_3g_button_read();
298 CPU_INT_EN
= GPIO_MASK
;
301 void button_init_device(void)
306 GPIOA_INT_LEV
= ~GPIOA_INPUT_VAL
;
307 GPIOA_INT_CLR
= GPIOA_INT_STAT
;
310 if ((IPOD_HW_REVISION
>> 16) == 1)
311 { /* enable scroll wheel */
312 GPIOB_ENABLE
|= 0x01;
313 GPIOB_OUTPUT_EN
|= 0x01;
314 GPIOB_OUTPUT_VAL
|= 0x01;
319 CPU_INT_EN
= GPIO_MASK
;
323 * Get button pressed from hardware
325 int button_read_device(void)
327 static bool hold_button
= false;
328 bool hold_button_old
;
330 static int wheel_timeout
= 0;
331 static unsigned char last_wheel_value
= 0;
332 unsigned char wheel_value
;
334 if ((IPOD_HW_REVISION
>> 16) == 1)
336 if (!hold_button
&& (wheel_timeout
== 0))
338 GPIOB_OUTPUT_VAL
|= 0x01; /* enable wheel */
339 udelay(50); /* let the voltage settle */
341 wheel_value
= GPIOA_INPUT_VAL
>> 6;
342 if (wheel_value
!= last_wheel_value
)
344 last_wheel_value
= wheel_value
;
345 wheel_timeout
= WHEEL_TIMEOUT
; /* keep wheel enabled */
346 GPIOA_INT_EN
= 0xff; /* enable wheel interrupts */
352 GPIOA_INT_EN
= 0x3f; /* disable wheel interrupts */
353 GPIOB_OUTPUT_VAL
&= ~0x01; /* disable wheel */
359 hold_button_old
= hold_button
;
360 hold_button
= button_hold();
362 if (hold_button
!= hold_button_old
)
363 backlight_hold_changed(hold_button
);
368 bool button_hold(void)
371 return (GPIOA_INPUT_VAL
& 0x20);
373 return !(GPIOA_INPUT_VAL
& 0x20);
377 bool headphones_inserted(void)
380 if ((IPOD_HW_REVISION
>> 16) == 2)
382 /* 2G uses GPIO B bit 0 */
383 return (GPIOB_INPUT_VAL
& 0x1)?true:false;
387 /* 1G has no headphone detection, so fake insertion */
391 /* 3G uses GPIO C bit 0 */
392 return (GPIOC_INPUT_VAL
& 0x1)?true:false;