Fix no-backlight colours for H100 series and M3.
[kugel-rb.git] / firmware / drivers / button.c
blob2a9428565c5b8e9e4d6b147a62a9bf2da3d8a445
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Daniel Stenberg
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 ****************************************************************************/
23 * Rockbox button functions
26 #include <stdlib.h>
27 #include "config.h"
28 #include "system.h"
29 #include "button.h"
30 #include "kernel.h"
31 #include "thread.h"
32 #include "backlight.h"
33 #include "serial.h"
34 #include "power.h"
35 #include "powermgmt.h"
36 #include "button-target.h"
38 #ifdef HAVE_REMOTE_LCD
39 #include "lcd-remote.h"
40 #endif
42 #ifndef SIMULATOR
43 #if 0
44 /* Older than MAX_EVENT_AGE button events are going to be ignored.
45 * Used to prevent for example volume going up uncontrollable when events
46 * are getting queued and UI is lagging too much.
48 #define MAX_EVENT_AGE HZ
49 #endif
51 struct event_queue button_queue;
53 static long lastbtn; /* Last valid button status */
54 static long last_read; /* Last button status, for debouncing/filtering */
55 static intptr_t button_data; /* data value from last message dequeued */
56 #ifdef HAVE_LCD_BITMAP
57 static bool flipped; /* buttons can be flipped to match the LCD flip */
58 #endif
59 #ifdef HAVE_BACKLIGHT
60 static bool filter_first_keypress;
61 #ifdef HAVE_REMOTE_LCD
62 static bool remote_filter_first_keypress;
63 #endif
64 #endif /* HAVE_BACKLIGHT */
65 #ifdef HAVE_HEADPHONE_DETECTION
66 static bool phones_present = false;
67 #endif
69 /* how long until repeat kicks in, in ticks */
70 #define REPEAT_START 30
72 /* the speed repeat starts at, in ticks */
73 #define REPEAT_INTERVAL_START 16
75 /* speed repeat finishes at, in ticks */
76 #define REPEAT_INTERVAL_FINISH 5
78 #ifdef HAVE_BUTTON_DATA
79 static int button_read(int *data);
80 #else
81 static int button_read(void);
82 #endif
84 #if defined(HAVE_HEADPHONE_DETECTION)
85 static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
86 /* This callback can be used for many different functions if needed -
87 just check to which object tmo points */
88 static int btn_detect_callback(struct timeout *tmo)
90 /* Try to post only transistions */
91 const long id = tmo->data ? SYS_PHONE_PLUGGED : SYS_PHONE_UNPLUGGED;
92 queue_remove_from_head(&button_queue, id);
93 queue_post(&button_queue, id, 0);
94 return 0;
96 #endif
98 static void button_tick(void)
100 static int count = 0;
101 static int repeat_speed = REPEAT_INTERVAL_START;
102 static int repeat_count = 0;
103 static bool repeat = false;
104 static bool post = false;
105 #ifdef HAVE_BACKLIGHT
106 static bool skip_release = false;
107 #ifdef HAVE_REMOTE_LCD
108 static bool skip_remote_release = false;
109 #endif
110 #endif
111 int diff;
112 int btn;
113 #ifdef HAVE_BUTTON_DATA
114 int data = 0;
115 #else
116 const int data = 0;
117 #endif
119 #ifdef HAS_SERIAL_REMOTE
120 /* Post events for the remote control */
121 btn = remote_control_rx();
122 if(btn)
124 queue_post(&button_queue, btn, 0);
126 #endif
128 #ifdef HAVE_BUTTON_DATA
129 btn = button_read(&data);
130 #else
131 btn = button_read();
132 #endif
134 #if defined(HAVE_HEADPHONE_DETECTION)
135 if (headphones_inserted() != phones_present)
137 /* Use the autoresetting oneshot to debounce the detection signal */
138 phones_present = !phones_present;
139 timeout_register(&hp_detect_timeout, btn_detect_callback,
140 HZ, phones_present);
142 #endif
144 /* Find out if a key has been released */
145 diff = btn ^ lastbtn;
146 if(diff && (btn & diff) == 0)
148 #ifdef HAVE_BACKLIGHT
149 #ifdef HAVE_REMOTE_LCD
150 if(diff & BUTTON_REMOTE)
151 if(!skip_remote_release)
152 queue_post(&button_queue, BUTTON_REL | diff, data);
153 else
154 skip_remote_release = false;
155 else
156 #endif
157 if(!skip_release)
158 queue_post(&button_queue, BUTTON_REL | diff, data);
159 else
160 skip_release = false;
161 #else
162 queue_post(&button_queue, BUTTON_REL | diff, data);
163 #endif
165 else
167 if ( btn )
169 /* normal keypress */
170 if ( btn != lastbtn )
172 post = true;
173 repeat = false;
174 repeat_speed = REPEAT_INTERVAL_START;
176 else /* repeat? */
178 if ( repeat )
180 if (!post)
181 count--;
182 if (count == 0) {
183 post = true;
184 /* yes we have repeat */
185 if (repeat_speed > REPEAT_INTERVAL_FINISH)
186 repeat_speed--;
187 count = repeat_speed;
189 repeat_count++;
191 /* Send a SYS_POWEROFF event if we have a device
192 which doesn't shut down easily with the OFF
193 key */
194 #ifdef HAVE_SW_POWEROFF
195 if ((btn == POWEROFF_BUTTON
196 #ifdef RC_POWEROFF_BUTTON
197 || btn == RC_POWEROFF_BUTTON
198 #endif
199 ) &&
200 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
201 !charger_inserted() &&
202 #endif
203 repeat_count > POWEROFF_COUNT)
205 /* Tell the main thread that it's time to
206 power off */
207 sys_poweroff();
209 /* Safety net for players without hardware
210 poweroff */
211 if(repeat_count > POWEROFF_COUNT * 10)
212 power_off();
214 #endif
217 else
219 if (count++ > REPEAT_START)
221 post = true;
222 repeat = true;
223 repeat_count = 0;
224 /* initial repeat */
225 count = REPEAT_INTERVAL_START;
229 if ( post )
231 if (repeat)
233 /* Only post repeat events if the queue is empty,
234 * to avoid afterscroll effects. */
235 if (queue_empty(&button_queue))
237 queue_post(&button_queue, BUTTON_REPEAT | btn, data);
238 #ifdef HAVE_BACKLIGHT
239 #ifdef HAVE_REMOTE_LCD
240 skip_remote_release = false;
241 #endif
242 skip_release = false;
243 #endif
244 post = false;
247 else
249 #ifdef HAVE_BACKLIGHT
250 #ifdef HAVE_REMOTE_LCD
251 if (btn & BUTTON_REMOTE) {
252 if (!remote_filter_first_keypress
253 || is_remote_backlight_on(false)
254 #if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
255 || (remote_type()==REMOTETYPE_H300_NONLCD)
256 #endif
258 queue_post(&button_queue, btn, data);
259 else
260 skip_remote_release = true;
262 else
263 #endif
264 if (!filter_first_keypress || is_backlight_on(false)
265 #if BUTTON_REMOTE
266 || (btn & BUTTON_REMOTE)
267 #endif
269 queue_post(&button_queue, btn, data);
270 else
271 skip_release = true;
272 #else /* no backlight, nothing to skip */
273 queue_post(&button_queue, btn, data);
274 #endif
275 post = false;
277 #ifdef HAVE_REMOTE_LCD
278 if(btn & BUTTON_REMOTE)
279 remote_backlight_on();
280 else
281 #endif
283 backlight_on();
284 #ifdef HAVE_BUTTON_LIGHT
285 buttonlight_on();
286 #endif
289 reset_poweroff_timer();
292 else
294 repeat = false;
295 count = 0;
298 lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
301 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
302 static void button_boost(bool state)
304 static bool boosted = false;
306 if (state && !boosted)
308 cpu_boost(true);
309 boosted = true;
311 else if (!state && boosted)
313 cpu_boost(false);
314 boosted = false;
317 #endif /* HAVE_ADJUSTABLE_CPU_FREQ */
319 int button_queue_count( void )
321 return queue_count(&button_queue);
324 long button_get(bool block)
326 struct queue_event ev;
327 int pending_count = queue_count(&button_queue);
329 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
330 /* Control the CPU boost trying to keep queue empty. */
331 if (pending_count == 0)
332 button_boost(false);
333 else if (pending_count > 2)
334 button_boost(true);
335 #endif
337 if ( block || pending_count )
339 queue_wait(&button_queue, &ev);
341 #if 0
342 /* Ignore if the event was too old and for simplicity, just
343 * wait for a new button_get() request. */
344 if (current_tick - ev.tick > MAX_EVENT_AGE)
345 return BUTTON_NONE;
346 #endif
347 button_data = ev.data;
348 return ev.id;
351 return BUTTON_NONE;
354 long button_get_w_tmo(int ticks)
356 struct queue_event ev;
358 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
359 /* Be sure to keep boosted state. */
360 if (!queue_empty(&button_queue))
361 return button_get(true);
363 button_boost(false);
364 #endif
366 queue_wait_w_tmo(&button_queue, &ev, ticks);
367 if (ev.id == SYS_TIMEOUT)
368 ev.id = BUTTON_NONE;
369 else
370 button_data = ev.data;
371 return ev.id;
374 intptr_t button_get_data(void)
376 return button_data;
379 void button_init(void)
381 /* Init used objects first */
382 queue_init(&button_queue, true);
384 #ifdef HAVE_BUTTON_DATA
385 int temp;
386 #endif
387 /* hardware inits */
388 button_init_device();
390 #ifdef HAVE_BUTTON_DATA
391 button_read(&temp);
392 lastbtn = button_read(&temp);
393 #else
394 button_read();
395 lastbtn = button_read();
396 #endif
398 reset_poweroff_timer();
400 #ifdef HAVE_LCD_BITMAP
401 flipped = false;
402 #endif
403 #ifdef HAVE_BACKLIGHT
404 filter_first_keypress = false;
405 #ifdef HAVE_REMOTE_LCD
406 remote_filter_first_keypress = false;
407 #endif
408 #endif
410 /* Start polling last */
411 tick_add_task(button_tick);
414 #ifdef BUTTON_DRIVER_CLOSE
415 void button_close(void)
417 tick_remove_task(button_tick);
419 #endif /* BUTTON_DRIVER_CLOSE */
421 #ifdef HAVE_LCD_BITMAP /* only bitmap displays can be flipped */
423 * helper function to swap LEFT/RIGHT, UP/DOWN (if present), and F1/F3 (Recorder)
425 static int button_flip(int button)
427 int newbutton;
429 newbutton = button &
430 ~(BUTTON_LEFT | BUTTON_RIGHT
431 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
432 | BUTTON_UP | BUTTON_DOWN
433 #endif
434 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
435 | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD
436 #endif
437 #if CONFIG_KEYPAD == RECORDER_PAD
438 | BUTTON_F1 | BUTTON_F3
439 #endif
440 #if CONFIG_KEYPAD == SANSA_C200_PAD
441 | BUTTON_VOL_UP | BUTTON_VOL_DOWN
442 #endif
445 if (button & BUTTON_LEFT)
446 newbutton |= BUTTON_RIGHT;
447 if (button & BUTTON_RIGHT)
448 newbutton |= BUTTON_LEFT;
449 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
450 if (button & BUTTON_UP)
451 newbutton |= BUTTON_DOWN;
452 if (button & BUTTON_DOWN)
453 newbutton |= BUTTON_UP;
454 #endif
455 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
456 if (button & BUTTON_SCROLL_BACK)
457 newbutton |= BUTTON_SCROLL_FWD;
458 if (button & BUTTON_SCROLL_FWD)
459 newbutton |= BUTTON_SCROLL_BACK;
460 #endif
461 #if CONFIG_KEYPAD == RECORDER_PAD
462 if (button & BUTTON_F1)
463 newbutton |= BUTTON_F3;
464 if (button & BUTTON_F3)
465 newbutton |= BUTTON_F1;
466 #endif
467 #if CONFIG_KEYPAD == SANSA_C200_PAD
468 if (button & BUTTON_VOL_UP)
469 newbutton |= BUTTON_VOL_DOWN;
470 if (button & BUTTON_VOL_DOWN)
471 newbutton |= BUTTON_VOL_UP;
472 #endif
474 return newbutton;
478 * set the flip attribute
479 * better only call this when the queue is empty
481 void button_set_flip(bool flip)
483 if (flip != flipped) /* not the current setting */
485 /* avoid race condition with the button_tick() */
486 int oldlevel = disable_irq_save();
487 lastbtn = button_flip(lastbtn);
488 flipped = flip;
489 restore_irq(oldlevel);
492 #endif /* HAVE_LCD_BITMAP */
494 #ifdef HAVE_BACKLIGHT
495 void set_backlight_filter_keypress(bool value)
497 filter_first_keypress = value;
499 #ifdef HAVE_REMOTE_LCD
500 void set_remote_backlight_filter_keypress(bool value)
502 remote_filter_first_keypress = value;
504 #endif
505 #endif
508 * Get button pressed from hardware
510 #ifdef HAVE_BUTTON_DATA
511 static int button_read(int *data)
513 int btn = button_read_device(data);
514 #else
515 static int button_read(void)
517 int btn = button_read_device();
518 #endif
519 int retval;
521 #ifdef HAVE_LCD_BITMAP
522 if (btn && flipped)
523 btn = button_flip(btn); /* swap upside down */
524 #endif
526 /* Filter the button status. It is only accepted if we get the same
527 status twice in a row. */
528 #ifndef HAVE_TOUCHSCREEN
529 if (btn != last_read)
530 retval = lastbtn;
531 else
532 #endif
533 retval = btn;
534 last_read = btn;
536 return retval;
540 int button_status(void)
542 return lastbtn;
545 void button_clear_queue(void)
547 queue_clear(&button_queue);
550 #endif /* SIMULATOR */
552 #ifdef HAVE_SCROLLWHEEL
553 /* WHEEL_ACCEL_FACTOR = 2^16 / WHEEL_ACCEL_START */
554 #define WHEEL_ACCEL_FACTOR (1<<16)/WHEEL_ACCEL_START
556 * data:
557 * [31] Use acceleration
558 * [30:24] Message post count (skipped + 1) (1-127)
559 * [23:0] Velocity - degree/sec
561 * WHEEL_ACCEL_FACTOR:
562 * Value in degree/sec -- configurable via settings -- above which
563 * the accelerated scrolling starts. Factor is internally scaled by
564 * 1<<16 in respect to the following 32bit integer operations.
566 int button_apply_acceleration(const unsigned int data)
568 int delta = (data >> 24) & 0x7f;
570 if ((data & (1 << 31)) != 0)
572 /* read driver's velocity from data */
573 unsigned int v = data & 0xffffff;
575 /* v = 28.4 fixed point */
576 v = (WHEEL_ACCEL_FACTOR * v)>>(16-4);
578 /* Calculate real numbers item to scroll based upon acceleration
579 * setting, use correct roundoff */
580 #if (WHEEL_ACCELERATION == 1)
581 v = (v*v + (1<< 7))>> 8;
582 #elif (WHEEL_ACCELERATION == 2)
583 v = (v*v*v + (1<<11))>>12;
584 #elif (WHEEL_ACCELERATION == 3)
585 v = (v*v*v*v + (1<<15))>>16;
586 #endif
588 if (v > 1)
589 delta *= v;
592 return delta;
594 #endif /* HAVE_SCROLLWHEEL */