blob: 2aa65bb2c07fde4222ccd0a42038cf3e770feeb1 [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 Arnold7c37c542007-08-18 09:47:19 +000044static int int_btn = BUTTON_NONE;
45#ifdef IPOD_1G2G
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)
Jens Arnold7c37c542007-08-18 09:47:19 +000050#endif
Jens Arnoldffb121c2007-07-29 08:03:21 +000051
Jens Arnolde249ecc2008-04-01 17:52:22 +000052static void handle_scroll_wheel(int new_scroll, int was_hold)
Barry Wardelld4945dc2006-10-05 10:58:51 +000053{
54 int wheel_keycode = BUTTON_NONE;
55 static int prev_scroll = -1;
56 static int direction = 0;
57 static int count = 0;
58 static int scroll_state[4][4] = {
59 {0, 1, -1, 0},
60 {-1, 0, 0, 1},
61 {1, 0, 0, -1},
62 {0, -1, 1, 0}
63 };
Jens Arnold7c37c542007-08-18 09:47:19 +000064
Barry Wardelld4945dc2006-10-05 10:58:51 +000065 if ( prev_scroll == -1 ) {
66 prev_scroll = new_scroll;
67 }
68 else if (direction != scroll_state[prev_scroll][new_scroll]) {
69 direction = scroll_state[prev_scroll][new_scroll];
70 count = 0;
71 }
72 else if (!was_hold) {
73 backlight_on();
Jens Arnold4012aae2007-01-19 07:33:23 +000074 reset_poweroff_timer();
Barry Wardelld4945dc2006-10-05 10:58:51 +000075 if (++count == 6) { /* reduce sensitivity */
76 count = 0;
Jens Arnolde249ecc2008-04-01 17:52:22 +000077 /* 1st..3rd Gen wheel has inverse direction mapping
78 * compared to Mini 1st Gen wheel. */
Barry Wardelld4945dc2006-10-05 10:58:51 +000079 switch (direction) {
80 case 1:
Jens Arnolde249ecc2008-04-01 17:52:22 +000081 wheel_keycode = BUTTON_SCROLL_BACK;
Barry Wardelld4945dc2006-10-05 10:58:51 +000082 break;
83 case -1:
Jens Arnolde249ecc2008-04-01 17:52:22 +000084 wheel_keycode = BUTTON_SCROLL_FWD;
Barry Wardelld4945dc2006-10-05 10:58:51 +000085 break;
86 default:
87 /* only happens if we get out of sync */
88 break;
89 }
90 }
91 }
92 if (wheel_keycode != BUTTON_NONE && queue_empty(&button_queue))
Michael Sevakis4b902672006-12-19 16:50:07 +000093 queue_post(&button_queue, wheel_keycode, 0);
Barry Wardelld4945dc2006-10-05 10:58:51 +000094 prev_scroll = new_scroll;
95}
96
97static int ipod_3g_button_read(void)
98{
99 unsigned char source, state;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000100 static bool was_hold = false;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000101 int btn = BUTTON_NONE;
Jens Arnoldb7013222007-07-27 09:57:27 +0000102
103#ifdef IPOD_3G
Jens Arnoldffb121c2007-07-29 08:03:21 +0000104 /* The following delay was 250 in the ipodlinux source,
105 * but 50 seems to work fine. 250 causes the wheel to stop
106 * working when spinning it real fast. */
107 udelay(50);
Jens Arnoldb7013222007-07-27 09:57:27 +0000108#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000109
110 /* get source of interupts */
111 source = GPIOA_INT_STAT;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000112
Barry Wardelld4945dc2006-10-05 10:58:51 +0000113 /* get current keypad status */
114 state = GPIOA_INPUT_VAL;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000115
116 /* toggle interrupt level */
Barry Wardelld4945dc2006-10-05 10:58:51 +0000117 GPIOA_INT_LEV = ~state;
118
Jens Arnoldb7013222007-07-27 09:57:27 +0000119#ifdef IPOD_3G
Barry Wardelld4945dc2006-10-05 10:58:51 +0000120 if (was_hold && source == 0x40 && state == 0xbf) {
121 /* ack any active interrupts */
122 GPIOA_INT_CLR = source;
123 return BUTTON_NONE;
124 }
Jens Arnoldffb121c2007-07-29 08:03:21 +0000125 was_hold = false;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000126
Barry Wardelld4945dc2006-10-05 10:58:51 +0000127 if ((state & 0x20) == 0) {
128 /* 3g hold switch is active low */
Jens Arnoldffb121c2007-07-29 08:03:21 +0000129 was_hold = true;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000130 /* hold switch on 3g causes all outputs to go low */
131 /* we shouldn't interpret these as key presses */
132 GPIOA_INT_CLR = source;
133 return BUTTON_NONE;
134 }
Jens Arnoldb7013222007-07-27 09:57:27 +0000135#elif defined IPOD_1G2G
Jens Arnoldffb121c2007-07-29 08:03:21 +0000136 if (state & 0x20) {
137 /* 1g/2g hold switch is active high */
138 GPIOA_INT_CLR = source;
139 return BUTTON_NONE;
140 }
Jens Arnoldb7013222007-07-27 09:57:27 +0000141#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000142 if ((state & 0x1) == 0) {
143 btn |= BUTTON_RIGHT;
144 }
145 if ((state & 0x2) == 0) {
146 btn |= BUTTON_SELECT;
147 }
148 if ((state & 0x4) == 0) {
149 btn |= BUTTON_PLAY;
150 }
151 if ((state & 0x8) == 0) {
152 btn |= BUTTON_LEFT;
153 }
154 if ((state & 0x10) == 0) {
155 btn |= BUTTON_MENU;
156 }
157
158 if (source & 0xc0) {
Jens Arnolde249ecc2008-04-01 17:52:22 +0000159 handle_scroll_wheel((state & 0xc0) >> 6, was_hold);
Barry Wardelld4945dc2006-10-05 10:58:51 +0000160 }
161
162 /* ack any active interrupts */
163 GPIOA_INT_CLR = source;
164
165 return btn;
166}
167
Jens Arnoldffb121c2007-07-29 08:03:21 +0000168void ipod_3g_button_int(void)
169{
170 CPU_INT_CLR = GPIO_MASK;
171 int_btn = ipod_3g_button_read();
172 CPU_INT_EN = GPIO_MASK;
173}
174
Barry Wardelld4945dc2006-10-05 10:58:51 +0000175void button_init_device(void)
176{
Jens Arnoldffb121c2007-07-29 08:03:21 +0000177 GPIOA_ENABLE = 0xff;
178 GPIOA_OUTPUT_EN = 0;
179
Barry Wardelld4945dc2006-10-05 10:58:51 +0000180 GPIOA_INT_LEV = ~GPIOA_INPUT_VAL;
181 GPIOA_INT_CLR = GPIOA_INT_STAT;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000182
Jens Arnoldc68e3cc2007-08-15 17:53:23 +0000183#ifdef IPOD_1G2G
184 if ((IPOD_HW_REVISION >> 16) == 1)
185 { /* enable scroll wheel */
186 GPIOB_ENABLE |= 0x01;
187 GPIOB_OUTPUT_EN |= 0x01;
188 GPIOB_OUTPUT_VAL |= 0x01;
189 }
190#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000191 GPIOA_INT_EN = 0xff;
Jens Arnoldffb121c2007-07-29 08:03:21 +0000192
193 CPU_INT_EN = GPIO_MASK;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000194}
195
196/*
197 * Get button pressed from hardware
198 */
199int button_read_device(void)
200{
201 static bool hold_button = false;
202 bool hold_button_old;
Jens Arnold7c37c542007-08-18 09:47:19 +0000203#ifdef IPOD_1G2G
Jens Arnolde249ecc2008-04-01 17:52:22 +0000204 static int wheel_timeout = 0;
Jens Arnold7c37c542007-08-18 09:47:19 +0000205 static unsigned char last_wheel_value = 0;
206 unsigned char wheel_value;
207
208 if ((IPOD_HW_REVISION >> 16) == 1)
209 {
210 if (!hold_button && (wheel_timeout == 0))
211 {
212 GPIOB_OUTPUT_VAL |= 0x01; /* enable wheel */
213 udelay(50); /* let the voltage settle */
Jens Arnolde249ecc2008-04-01 17:52:22 +0000214 }
215 wheel_value = GPIOA_INPUT_VAL >> 6;
216 if (wheel_value != last_wheel_value)
217 {
218 last_wheel_value = wheel_value;
219 wheel_timeout = WHEEL_TIMEOUT; /* keep wheel enabled */
Jens Arnold1f75bd52008-04-01 18:53:34 +0000220 GPIOA_INT_EN = 0xff; /* enable wheel interrupts */
Jens Arnolde249ecc2008-04-01 17:52:22 +0000221 }
Jens Arnold7c37c542007-08-18 09:47:19 +0000222 if (wheel_timeout)
223 wheel_timeout--;
224 else
Jens Arnolde249ecc2008-04-01 17:52:22 +0000225 {
226 GPIOA_INT_EN = 0x3f; /* disable wheel interrupts */
Jens Arnold7c37c542007-08-18 09:47:19 +0000227 GPIOB_OUTPUT_VAL &= ~0x01; /* disable wheel */
Jens Arnolde249ecc2008-04-01 17:52:22 +0000228 }
Jens Arnold7c37c542007-08-18 09:47:19 +0000229 }
230#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000231
232 /* normal buttons */
233 hold_button_old = hold_button;
234 hold_button = button_hold();
235
236 if (hold_button != hold_button_old)
237 backlight_hold_changed(hold_button);
Jens Arnold7c37c542007-08-18 09:47:19 +0000238
Jens Arnoldffb121c2007-07-29 08:03:21 +0000239 return int_btn;
Barry Wardelld4945dc2006-10-05 10:58:51 +0000240}
241
242bool button_hold(void)
243{
Jens Arnold021c0862007-07-27 12:05:54 +0000244#ifdef IPOD_1G2G
245 return (GPIOA_INPUT_VAL & 0x20);
246#else
247 return !(GPIOA_INPUT_VAL & 0x20);
248#endif
Barry Wardelld4945dc2006-10-05 10:58:51 +0000249}
Daniel Ankers5c6f32a2006-10-07 12:19:34 +0000250
251bool headphones_inserted(void)
252{
Michael Giacomellie243db62008-05-03 21:15:13 +0000253#ifdef IPOD_1G2G
254 if ((IPOD_HW_REVISION >> 16) == 2)
255 {
256 /* 2G uses GPIO B bit 0 */
257 return (GPIOB_INPUT_VAL & 0x1)?true:false;
258 }
259 else
260 {
261 /* 1G has no headphone detection, so fake insertion */
262 return (true);
263 }
264#else
265 /* 3G uses GPIO C bit 0 */
Daniel Ankers5c6f32a2006-10-07 12:19:34 +0000266 return (GPIOC_INPUT_VAL & 0x1)?true:false;
Michael Giacomellie243db62008-05-03 21:15:13 +0000267#endif
Daniel Ankers5c6f32a2006-10-07 12:19:34 +0000268}
Michael Giacomellie243db62008-05-03 21:15:13 +0000269
270