blob: 54552058cc4fddbbe81b7155e09ab4a07d28b04c [file] [log] [blame]
Barry Wardelld4945dc2006-10-05 10:58:51 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Daniel Stenberg
11 *
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)
16 *
17 *
18 * All files in this archive are subject to the GNU General Public License.
19 * See the file COPYING in the source tree root for full license agreement.
20 *
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
23 *
24 ****************************************************************************/
25
26/*
27 * Rockbox button functions
28 */
29
30#include <stdlib.h>
31#include "config.h"
32#include "cpu.h"
33#include "system.h"
34#include "button.h"
35#include "kernel.h"
36#include "backlight.h"
37#include "adc.h"
38#include "serial.h"
39#include "power.h"
40#include "system.h"
41#include "powermgmt.h"
Jens Arnoldc68e3cc2007-08-15 17:53:23 +000042#include "hwcompat.h"
Barry Wardelld4945dc2006-10-05 10:58:51 +000043
Jens Arnoldffb121c2007-07-29 08:03:21 +000044int int_btn = BUTTON_NONE;
45
Barry Wardelld4945dc2006-10-05 10:58:51 +000046/* iPod 3G and mini 1G, mini 2G uses iPod 4G code */
47void handle_scroll_wheel(int new_scroll, int was_hold, int reverse)
48{
49 int wheel_keycode = BUTTON_NONE;
50 static int prev_scroll = -1;
51 static int direction = 0;
52 static int count = 0;
53 static int scroll_state[4][4] = {
54 {0, 1, -1, 0},
55 {-1, 0, 0, 1},
56 {1, 0, 0, -1},
57 {0, -1, 1, 0}
58 };
59
60 if ( prev_scroll == -1 ) {
61 prev_scroll = new_scroll;
62 }
63 else if (direction != scroll_state[prev_scroll][new_scroll]) {
64 direction = scroll_state[prev_scroll][new_scroll];
65 count = 0;
66 }
67 else if (!was_hold) {
68 backlight_on();
Jens Arnold4012aae2007-01-19 07:33:23 +000069 reset_poweroff_timer();
Barry Wardelld4945dc2006-10-05 10:58:51 +000070 if (++count == 6) { /* reduce sensitivity */
71 count = 0;
72 switch (direction) {
73 case 1:
74 if (reverse) {
75 /* 'r' keypress */
76 wheel_keycode = BUTTON_SCROLL_FWD;
77 }
78 else {
79 /* 'l' keypress */
80 wheel_keycode = BUTTON_SCROLL_BACK;
81 }
82 break;
83 case -1:
84 if (reverse) {
85 /* 'l' keypress */
86 wheel_keycode = BUTTON_SCROLL_BACK;
87 }
88 else {
89 /* 'r' keypress */
90 wheel_keycode = BUTTON_SCROLL_FWD;
91 }
92 break;
93 default:
94 /* only happens if we get out of sync */
95 break;
96 }
97 }
98 }
99 if (wheel_keycode != BUTTON_NONE && queue_empty(&button_queue))
Michael Sevakis4b902672006-12-19 16:50:07 +0000100 queue_post(&button_queue, wheel_keycode, 0);
Barry Wardelld4945dc2006-10-05 10:58:51 +0000101 prev_scroll = new_scroll;
102}
103
104static int ipod_3g_button_read(void)
105{
106 unsigned char source, state;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000107 static bool was_hold = false;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000108 int btn = BUTTON_NONE;
Jens Arnoldb7013222007-07-27 09:57:27 +0000109
110#ifdef IPOD_3G
Jens Arnoldffb121c2007-07-29 08:03:21 +0000111 /* The following delay was 250 in the ipodlinux source,
112 * but 50 seems to work fine. 250 causes the wheel to stop
113 * working when spinning it real fast. */
114 udelay(50);
Jens Arnoldb7013222007-07-27 09:57:27 +0000115#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000116
117 /* get source of interupts */
118 source = GPIOA_INT_STAT;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000119
Barry Wardelld4945dc2006-10-05 10:58:51 +0000120 /* get current keypad status */
121 state = GPIOA_INPUT_VAL;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000122
123 /* toggle interrupt level */
Barry Wardelld4945dc2006-10-05 10:58:51 +0000124 GPIOA_INT_LEV = ~state;
125
Jens Arnoldb7013222007-07-27 09:57:27 +0000126#ifdef IPOD_3G
Barry Wardelld4945dc2006-10-05 10:58:51 +0000127 if (was_hold && source == 0x40 && state == 0xbf) {
128 /* ack any active interrupts */
129 GPIOA_INT_CLR = source;
130 return BUTTON_NONE;
131 }
Jens Arnoldffb121c2007-07-29 08:03:21 +0000132 was_hold = false;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000133
Barry Wardelld4945dc2006-10-05 10:58:51 +0000134 if ((state & 0x20) == 0) {
135 /* 3g hold switch is active low */
Jens Arnoldffb121c2007-07-29 08:03:21 +0000136 was_hold = true;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000137 /* hold switch on 3g causes all outputs to go low */
138 /* we shouldn't interpret these as key presses */
139 GPIOA_INT_CLR = source;
140 return BUTTON_NONE;
141 }
Jens Arnoldb7013222007-07-27 09:57:27 +0000142#elif defined IPOD_1G2G
Jens Arnoldffb121c2007-07-29 08:03:21 +0000143 if (state & 0x20) {
144 /* 1g/2g hold switch is active high */
145 GPIOA_INT_CLR = source;
146 return BUTTON_NONE;
147 }
Jens Arnoldb7013222007-07-27 09:57:27 +0000148#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000149 if ((state & 0x1) == 0) {
150 btn |= BUTTON_RIGHT;
151 }
152 if ((state & 0x2) == 0) {
153 btn |= BUTTON_SELECT;
154 }
155 if ((state & 0x4) == 0) {
156 btn |= BUTTON_PLAY;
157 }
158 if ((state & 0x8) == 0) {
159 btn |= BUTTON_LEFT;
160 }
161 if ((state & 0x10) == 0) {
162 btn |= BUTTON_MENU;
163 }
164
165 if (source & 0xc0) {
166 handle_scroll_wheel((state & 0xc0) >> 6, was_hold, 0);
167 }
168
169 /* ack any active interrupts */
170 GPIOA_INT_CLR = source;
171
172 return btn;
173}
174
Jens Arnoldffb121c2007-07-29 08:03:21 +0000175void ipod_3g_button_int(void)
176{
177 CPU_INT_CLR = GPIO_MASK;
178 int_btn = ipod_3g_button_read();
179 CPU_INT_EN = GPIO_MASK;
180}
181
Barry Wardelld4945dc2006-10-05 10:58:51 +0000182void button_init_device(void)
183{
Jens Arnoldffb121c2007-07-29 08:03:21 +0000184 GPIOA_ENABLE = 0xff;
185 GPIOA_OUTPUT_EN = 0;
186
Barry Wardelld4945dc2006-10-05 10:58:51 +0000187 GPIOA_INT_LEV = ~GPIOA_INPUT_VAL;
188 GPIOA_INT_CLR = GPIOA_INT_STAT;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000189
Jens Arnoldc68e3cc2007-08-15 17:53:23 +0000190#ifdef IPOD_1G2G
191 if ((IPOD_HW_REVISION >> 16) == 1)
192 { /* enable scroll wheel */
193 GPIOB_ENABLE |= 0x01;
194 GPIOB_OUTPUT_EN |= 0x01;
195 GPIOB_OUTPUT_VAL |= 0x01;
196 }
197#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000198 GPIOA_INT_EN = 0xff;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000199
200 CPU_INT_EN = GPIO_MASK;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000201}
202
203/*
204 * Get button pressed from hardware
205 */
206int button_read_device(void)
207{
208 static bool hold_button = false;
209 bool hold_button_old;
210
211 /* normal buttons */
212 hold_button_old = hold_button;
213 hold_button = button_hold();
214
215 if (hold_button != hold_button_old)
Jens Arnold347b3512007-08-18 08:14:07 +0000216 {
Barry Wardelld4945dc2006-10-05 10:58:51 +0000217 backlight_hold_changed(hold_button);
Jens Arnold347b3512007-08-18 08:14:07 +0000218#ifdef IPOD_1G2G
219 /* Disable the 1st gen's wheel on hold in order to save power.
220 * The wheel draws ~12mA when enabled! Toggling the bit doesn't hurt
221 * on 2nd gen, because the pin is set to input (headphone detect). */
222 if (hold_button)
223 GPIOB_OUTPUT_VAL &= ~0x01; /* disable wheel */
224 else
225 GPIOB_OUTPUT_VAL |= 0x01; /* enable wheel */
226#endif
227 }
Barry Wardelld4945dc2006-10-05 10:58:51 +0000228
Jens Arnoldffb121c2007-07-29 08:03:21 +0000229 return int_btn;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000230}
231
232bool button_hold(void)
233{
Jens Arnold021c0862007-07-27 12:05:54 +0000234#ifdef IPOD_1G2G
235 return (GPIOA_INPUT_VAL & 0x20);
236#else
237 return !(GPIOA_INPUT_VAL & 0x20);
238#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000239}
Daniel Ankers5c6f32a2006-10-07 12:19:34 +0000240
241bool headphones_inserted(void)
242{
243 return (GPIOC_INPUT_VAL & 0x1)?true:false;
244}