Sansa Clip+: detect AMSv2 variant (just like was already done for fuze v2), this...
[maemo-rb.git] / firmware / target / arm / as3525 / sansa-fuzev2 / button-fuzev2.c
blob7920bff80e4044fed8e9665fe858e3c61a71bbd8
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 by Thomas Martitz
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 #include "config.h"
23 #include "system.h"
24 #include "button.h"
25 #include "backlight.h"
27 #ifdef HAS_BUTTON_HOLD
28 static bool hold_button = false;
29 #endif
31 #ifdef HAVE_SCROLLWHEEL
32 #define SCROLLWHEEL_BITS (1<<7|1<<6)
33 /* TIMER units */
34 #define TIMER_TICK (KERNEL_TIMER_FREQ/HZ)/* how long a tick lasts */
35 #define TIMER_MS (TIMER_TICK/(1000/HZ))/* how long a ms lasts */
37 #define WHEEL_REPEAT_INTERVAL (300*TIMER_MS) /* 300ms */
38 #define WHEEL_FAST_ON_INTERVAL ( 20*TIMER_MS) /* 20ms */
39 #define WHEEL_FAST_OFF_INTERVAL ( 60*TIMER_MS) /* 60ms */
40 /* phsyical clicks per rotation * wheel value changes per phys click */
41 #define WHEEL_CHANGES_PER_CLICK 4
42 #define WHEELCLICKS_PER_ROTATION (12*WHEEL_CHANGES_PER_CLICK)
45 * based on button-e200.c, adjusted to the AMS timers and fuzev2's
46 * scrollwheel and cleaned up a little
48 static void scrollwheel(unsigned int wheel_value)
50 /* wheel values and times from the previous irq */
51 static unsigned int old_wheel_value = 0;
52 static unsigned int wheel_repeat = BUTTON_NONE;
53 static long last_wheel_post = 0;
55 /* We only post every 4th action, as this matches better with the physical
56 * clicks of the wheel */
57 static unsigned int wheel_click_count = 0;
58 /* number of items to skip in lists, 1 in slow mode */
59 static unsigned int wheel_delta = 0;
60 /* accumulated wheel rotations per second */
61 static unsigned long wheel_velocity = 0;
62 /* fast or slow mode? */
63 static int wheel_fast_mode = 0;
65 /* Read wheel
66 * Bits 6 and 7 of GPIOA change as follows (Gray Code):
67 * Clockwise rotation 00 -> 01 -> 11 -> 10 -> 00
68 * Counter-clockwise 00 -> 10 -> 11 -> 01 -> 00
70 * For easy look-up, actual wheel values act as indicies also,
71 * which is why the table seems to be not ordered correctly
73 static const unsigned char wheel_tbl[2][4] =
75 { 2, 0, 3, 1 }, /* Clockwise rotation */
76 { 1, 3, 0, 2 }, /* Counter-clockwise */
79 unsigned int btn = BUTTON_NONE;
81 if (old_wheel_value == wheel_tbl[0][wheel_value])
82 btn = BUTTON_SCROLL_FWD;
83 else if (old_wheel_value == wheel_tbl[1][wheel_value])
84 btn = BUTTON_SCROLL_BACK;
86 if (btn == BUTTON_NONE)
88 old_wheel_value = wheel_value;
89 return;
92 int repeat = 1; /* assume repeat */
93 long time = (TIMER_TICK - TIMER2_VALUE) + current_tick*TIMER_TICK; /* to timer unit */
94 long v = (time - last_wheel_post);
96 /* interpolate velocity in timer_freq/timer_unit == 1/s */
97 if (v) v = TIMER_FREQ / v;
99 /* accumulate velocities over time with each v */
100 wheel_velocity = (7*wheel_velocity + v) / 8;
102 if (btn != wheel_repeat)
104 /* direction reversals nullify all fast mode states */
105 wheel_repeat = btn;
106 repeat =
107 wheel_velocity =
108 wheel_click_count = 0;
111 if (wheel_fast_mode != 0)
113 /* fast OFF happens immediately when velocity drops below
114 threshold */
115 if (TIME_AFTER(time,
116 last_wheel_post + WHEEL_FAST_OFF_INTERVAL))
118 /* moving out of fast mode */
119 wheel_fast_mode = 0;
120 /* reset velocity */
121 wheel_velocity = 0;
122 /* wheel_delta is always 1 in slow mode */
123 wheel_delta = 1;
126 else
128 /* fast ON gets filtered to avoid inadvertent jumps to fast mode */
129 if (repeat && wheel_velocity > TIMER_FREQ/WHEEL_FAST_ON_INTERVAL)
131 /* moving into fast mode */
132 wheel_fast_mode = 1 << 31;
133 wheel_click_count = 0;
134 wheel_velocity = TIMER_FREQ/WHEEL_FAST_OFF_INTERVAL;
136 else if (++wheel_click_count < WHEEL_CHANGES_PER_CLICK)
137 { /* skip some wheel changes, so that 1 post represents
138 * 1 item in lists */
139 btn = BUTTON_NONE;
142 /* wheel_delta is always 1 in slow mode */
143 wheel_delta = 1;
146 if (btn != BUTTON_NONE)
148 wheel_click_count = 0;
150 /* generate repeats if quick enough */
151 if (repeat && TIME_BEFORE(time,
152 last_wheel_post + WHEEL_REPEAT_INTERVAL))
153 btn |= BUTTON_REPEAT;
155 last_wheel_post = time;
157 if (queue_empty(&button_queue))
159 queue_post(&button_queue, btn, wheel_fast_mode |
160 (wheel_delta << 24) | wheel_velocity*360/WHEELCLICKS_PER_ROTATION);
161 /* message posted - reset delta and poke backlight on*/
162 wheel_delta = 1;
163 backlight_on();
164 buttonlight_on();
166 else
168 /* skipped post - increment delta */
169 if (++wheel_delta > 0x7f)
170 wheel_delta = 0x7f;
174 old_wheel_value = wheel_value;
176 #endif
178 void button_init_device(void)
180 #if defined(HAVE_SCROLLWHEEL)
181 GPIOA_DIR &= ~(1<<6|1<<7);
182 GPIOC_DIR = 0;
183 GPIOB_DIR |= (1<<4)|(1<<0);
185 GPIOB_PIN(4) = 1<<4; /* activate the wheel */
187 /* setup scrollwheel isr */
188 /* clear previous irq if any */
189 GPIOA_IC = SCROLLWHEEL_BITS;
190 /* enable edge detecting */
191 GPIOA_IS &= ~SCROLLWHEEL_BITS;
192 /* detect both raising and falling edges */
193 GPIOA_IBE |= SCROLLWHEEL_BITS;
194 /* lastly, enable the interrupt */
195 GPIOA_IE |= SCROLLWHEEL_BITS;
196 #endif
199 /* read the 2 bits at the same time */
200 #define GPIOA_PIN76_offset ((1<<(6+2)) | (1<<(7+2)))
201 #define GPIOA_PIN76 (*(volatile unsigned char*)(GPIOA_BASE+GPIOA_PIN76_offset))
203 void button_gpioa_isr(void)
205 #if defined(HAVE_SCROLLWHEEL)
206 /* scroll wheel handling */
207 if (GPIOA_MIS & SCROLLWHEEL_BITS)
208 scrollwheel(GPIOA_PIN76 >> 6);
210 /* ack interrupt */
211 GPIOA_IC = SCROLLWHEEL_BITS;
212 #endif
217 * Get button pressed from hardware
219 int button_read_device(void)
221 int btn = 0;
222 static bool hold_button_old = false;
223 static long power_counter = 0;
224 unsigned gpiod6;
226 /* if we don't wait for the fifo to empty, we'll see screen corruption
227 * (the higher the CPU frequency the higher the corruption) */
228 while ((DBOP_STAT & (1<<10)) == 0);
230 int delay = 30;
231 while(delay--) nop;
233 bool ccu_io_bit12 = CCU_IO & (1<<12);
234 CCU_IO &= ~(1<<12);
236 /* B1 is shared with FM i2c */
237 bool gpiob_pin0_dir = GPIOB_DIR & (1<<1);
238 GPIOB_DIR &= ~(1<<1);
240 GPIOB_PIN(0) = 1<<0;
241 udelay(4);
243 gpiod6 = GPIOD_PIN(6);
245 GPIOB_PIN(0) = 0;
246 udelay(2);
248 if (GPIOC_PIN(1) & 1<<1)
249 btn |= BUTTON_DOWN;
250 if (GPIOC_PIN(2) & 1<<2)
251 btn |= BUTTON_UP;
252 if (GPIOC_PIN(3) & 1<<3)
253 btn |= BUTTON_LEFT;
254 if (GPIOC_PIN(4) & 1<<4)
255 btn |= BUTTON_SELECT;
256 if (GPIOC_PIN(5) & 1<<5)
257 btn |= BUTTON_RIGHT;
258 if (GPIOB_PIN(1) & 1<<1)
259 btn |= BUTTON_HOME;
260 if (amsv2_variant == 1)
261 btn ^= BUTTON_HOME;
263 if (gpiod6 & 1<<6)
264 { /* power/hold is on the same pin. we know it's hold if the bit isn't
265 * set now anymore */
266 if (GPIOD_PIN(6) & 1<<6)
268 hold_button = false;
269 btn |= BUTTON_POWER;
271 else
273 hold_button = true;
277 if(gpiob_pin0_dir)
278 GPIOB_DIR |= 1<<1;
280 if(ccu_io_bit12)
281 CCU_IO |= 1<<12;
283 #ifdef HAS_BUTTON_HOLD
284 #ifndef BOOTLOADER
285 /* light handling */
286 if (hold_button != hold_button_old)
288 hold_button_old = hold_button;
289 backlight_hold_changed(hold_button);
290 /* mask scrollwheel irq so we don't need to check for
291 * the hold button in the isr */
292 if (hold_button)
293 GPIOA_IE &= ~SCROLLWHEEL_BITS;
294 else
295 GPIOA_IE |= SCROLLWHEEL_BITS;
297 #else
298 (void)hold_button_old;
299 #endif
300 if (hold_button)
302 power_counter = HZ;
303 return 0;
305 /* read power, but not if hold button was just released, since
306 * you basically always hit power due to the slider mechanism after releasing
307 * (fuze only)
309 else if (power_counter > 0)
311 power_counter--;
312 btn &= ~BUTTON_POWER;
314 #endif
315 return btn;
318 #ifdef HAS_BUTTON_HOLD
319 bool button_hold(void)
321 return hold_button;
323 #endif