When building a static linux binary link libusb static.
[Rockbox.git] / firmware / backlight.c
blobfcb7159ccee7e68455a46a992aca46aacf1395ca
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "config.h"
20 #include <stdlib.h>
21 #include "cpu.h"
22 #include "kernel.h"
23 #include "thread.h"
24 #include "i2c.h"
25 #include "debug.h"
26 #include "rtc.h"
27 #include "usb.h"
28 #include "power.h"
29 #include "system.h"
30 #include "button.h"
31 #include "timer.h"
32 #include "backlight.h"
34 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
35 #include "lcd.h" /* for lcd_enable() and lcd_sleep() */
36 #endif
37 #ifdef HAVE_REMOTE_LCD
38 #include "lcd-remote.h"
39 #endif
40 #ifndef SIMULATOR
41 #include "backlight-target.h"
42 #endif
44 #ifdef SIMULATOR
45 /* TODO: find a better way to do it but we need a kernel thread somewhere to
46 handle this */
47 extern void screen_dump(void);
49 static inline void _backlight_on(void)
51 sim_backlight(100);
54 static inline void _backlight_off(void)
56 sim_backlight(0);
59 static inline void _backlight_set_brightness(int val)
61 (void)val;
64 static inline void _buttonlight_on(void)
68 static inline void _buttonlight_off(void)
72 static inline void _buttonlight_set_brightness(int val)
74 (void)val;
76 #ifdef HAVE_REMOTE_LCD
77 static inline void _remote_backlight_on(void)
79 sim_remote_backlight(100);
82 static inline void _remote_backlight_off(void)
84 sim_remote_backlight(0);
86 #endif /* HAVE_REMOTE_LCD */
88 #endif /* SIMULATOR */
90 #if defined(HAVE_BACKLIGHT) && !defined(BOOTLOADER)
92 enum {
93 BACKLIGHT_ON,
94 BACKLIGHT_OFF,
95 #ifdef HAVE_REMOTE_LCD
96 REMOTE_BACKLIGHT_ON,
97 REMOTE_BACKLIGHT_OFF,
98 #endif
99 #if defined(_BACKLIGHT_FADE_BOOST) || defined(_BACKLIGHT_FADE_ENABLE)
100 BACKLIGHT_FADE_FINISH,
101 #endif
102 #ifdef HAVE_LCD_SLEEP
103 LCD_SLEEP,
104 #endif
105 #ifdef HAVE_BUTTON_LIGHT
106 BUTTON_LIGHT_ON,
107 BUTTON_LIGHT_OFF,
108 #endif
111 static void backlight_thread(void);
112 static long backlight_stack[DEFAULT_STACK_SIZE/sizeof(long)];
113 static const char backlight_thread_name[] = "backlight";
114 static struct event_queue backlight_queue;
116 static int backlight_timer;
117 static int backlight_timeout;
118 static int backlight_timeout_normal = 5*HZ;
119 #if CONFIG_CHARGING
120 static int backlight_timeout_plugged = 5*HZ;
121 #endif
122 #ifdef HAS_BUTTON_HOLD
123 static int backlight_on_button_hold = 0;
124 #endif
126 #ifdef HAVE_BUTTON_LIGHT
127 static int buttonlight_timer;
128 int _buttonlight_timeout = 5*HZ;
130 /* Update state of buttonlight according to timeout setting */
131 static void buttonlight_update_state(void)
133 buttonlight_timer = _buttonlight_timeout;
135 /* Buttonlight == OFF in the setting? */
136 if (buttonlight_timer < 0)
138 buttonlight_timer = 0; /* Disable the timeout */
139 _buttonlight_off();
141 else
142 _buttonlight_on();
145 /* external interface */
146 void buttonlight_on(void)
148 queue_remove_from_head(&backlight_queue, BUTTON_LIGHT_ON);
149 queue_post(&backlight_queue, BUTTON_LIGHT_ON, 0);
152 void buttonlight_off(void)
154 queue_post(&backlight_queue, BUTTON_LIGHT_OFF, 0);
157 void buttonlight_set_timeout(int value)
159 _buttonlight_timeout = HZ * value;
160 buttonlight_update_state();
163 #endif
165 #ifdef HAVE_REMOTE_LCD
166 static int remote_backlight_timer;
167 static int remote_backlight_timeout;
168 static int remote_backlight_timeout_normal = 5*HZ;
169 #if CONFIG_CHARGING
170 static int remote_backlight_timeout_plugged = 5*HZ;
171 #endif
172 #ifdef HAS_REMOTE_BUTTON_HOLD
173 static int remote_backlight_on_button_hold = 0;
174 #endif
175 #endif
177 #ifdef HAVE_LCD_SLEEP
178 const signed char lcd_sleep_timeout_value[10] =
180 -1, 0, 5, 10, 15, 20, 30, 45, 60, 90
182 int _lcd_sleep_timer;
183 int _lcd_sleep_timeout = 10*HZ;
184 #endif
186 #if defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR)
187 /* backlight fading */
188 #define BL_PWM_INTERVAL 5 /* Cycle interval in ms */
189 #define BL_PWM_BITS 8
190 #define BL_PWM_COUNT (1<<BL_PWM_BITS)
192 /* s15.16 fixed point variables */
193 static int32_t bl_fade_in_step = ((BL_PWM_INTERVAL*BL_PWM_COUNT)<<16)/300;
194 static int32_t bl_fade_out_step = ((BL_PWM_INTERVAL*BL_PWM_COUNT)<<16)/2000;
195 static int32_t bl_dim_fraction = 0;
197 static int bl_dim_target = 0;
198 static int bl_dim_current = 0;
199 static enum {DIM_STATE_START, DIM_STATE_MAIN} bl_dim_state = DIM_STATE_START;
200 static bool bl_timer_active = false;
202 static void backlight_isr(void)
204 int timer_period = (TIMER_FREQ*BL_PWM_INTERVAL/1000);
205 bool idle = false;
207 switch (bl_dim_state)
209 /* New cycle */
210 case DIM_STATE_START:
211 bl_dim_current = bl_dim_fraction >> 16;
213 if (bl_dim_current > 0 && bl_dim_current < BL_PWM_COUNT)
215 _backlight_on_isr();
216 timer_period = (timer_period * bl_dim_current) >> BL_PWM_BITS;
217 bl_dim_state = DIM_STATE_MAIN;
219 else
221 if (bl_dim_current)
222 _backlight_on_isr();
223 else
224 _backlight_off_isr();
225 if (bl_dim_current == bl_dim_target)
226 idle = true;
228 if (bl_dim_current < bl_dim_target)
230 bl_dim_fraction = MIN(bl_dim_fraction + bl_fade_in_step,
231 (BL_PWM_COUNT<<16));
233 else if (bl_dim_current > bl_dim_target)
235 bl_dim_fraction = MAX(bl_dim_fraction - bl_fade_out_step, 0);
237 break;
239 /* Dim main screen */
240 case DIM_STATE_MAIN:
241 _backlight_off_isr();
242 timer_period = (timer_period * (BL_PWM_COUNT - bl_dim_current))
243 >> BL_PWM_BITS;
244 bl_dim_state = DIM_STATE_START;
245 break ;
247 if (idle)
249 #if defined(_BACKLIGHT_FADE_BOOST) || defined(_BACKLIGHT_FADE_ENABLE)
250 queue_post(&backlight_queue, BACKLIGHT_FADE_FINISH, 0);
251 #endif
252 timer_unregister();
253 bl_timer_active = false;
255 else
256 timer_set_period(timer_period);
259 static void backlight_switch(void)
261 if (bl_dim_target > (BL_PWM_COUNT/2))
263 _backlight_on_normal();
264 bl_dim_fraction = (BL_PWM_COUNT<<16);
266 else
268 _backlight_off_normal();
269 bl_dim_fraction = 0;
273 static void backlight_release_timer(void)
275 #ifdef _BACKLIGHT_FADE_BOOST
276 cpu_boost(false);
277 #endif
278 timer_unregister();
279 bl_timer_active = false;
280 backlight_switch();
283 static void backlight_dim(int value)
285 /* protect from extraneous calls with the same target value */
286 if (value == bl_dim_target)
287 return;
289 bl_dim_target = value;
291 if (bl_timer_active)
292 return ;
294 if (timer_register(0, backlight_release_timer, 2, 0, backlight_isr))
296 #ifdef _BACKLIGHT_FADE_BOOST
297 /* Prevent cpu frequency changes while dimming. */
298 cpu_boost(true);
299 #endif
300 bl_timer_active = true;
302 else
303 backlight_switch();
306 static void _backlight_on(void)
308 if (bl_fade_in_step > 0)
310 #ifdef _BACKLIGHT_FADE_ENABLE
311 _backlight_hw_enable(true);
312 #endif
313 backlight_dim(BL_PWM_COUNT);
315 else
317 bl_dim_target = BL_PWM_COUNT;
318 bl_dim_fraction = (BL_PWM_COUNT<<16);
319 _backlight_on_normal();
321 #ifdef HAVE_LCD_SLEEP
322 _lcd_sleep_timer = 0; /* LCD should be awake already */
323 #endif
326 static void _backlight_off(void)
328 if (bl_fade_out_step > 0)
330 backlight_dim(0);
332 else
334 bl_dim_target = bl_dim_fraction = 0;
335 _backlight_off_normal();
337 #ifdef HAVE_LCD_SLEEP
338 /* Start LCD sleep countdown */
339 if (_lcd_sleep_timeout < 0)
341 _lcd_sleep_timer = 0; /* Setting == Always */
342 lcd_sleep();
344 else
345 _lcd_sleep_timer = _lcd_sleep_timeout;
346 #endif
349 void backlight_set_fade_in(int value)
351 if (value > 0)
352 bl_fade_in_step = ((BL_PWM_INTERVAL*BL_PWM_COUNT)<<16) / value;
353 else
354 bl_fade_in_step = 0;
357 void backlight_set_fade_out(int value)
359 if (value > 0)
360 bl_fade_out_step = ((BL_PWM_INTERVAL*BL_PWM_COUNT)<<16) / value;
361 else
362 bl_fade_out_step = 0;
364 #endif /* defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR) */
366 /* Update state of backlight according to timeout setting */
367 static void backlight_update_state(void)
369 #ifdef HAS_BUTTON_HOLD
370 if (button_hold() && (backlight_on_button_hold != 0))
371 backlight_timeout = (backlight_on_button_hold == 2) ? 0 : -1;
372 /* always on or always off */
373 else
374 #endif
375 #if CONFIG_CHARGING
376 if (charger_inserted()
377 #ifdef HAVE_USB_POWER
378 || usb_powered()
379 #endif
381 backlight_timeout = backlight_timeout_plugged;
382 else
383 #endif
384 backlight_timeout = backlight_timeout_normal;
386 /* Backlight == OFF in the setting? */
387 if (backlight_timeout < 0)
389 backlight_timer = 0; /* Disable the timeout */
390 _backlight_off();
392 else
394 backlight_timer = backlight_timeout;
395 _backlight_on();
399 #ifdef HAVE_REMOTE_LCD
400 /* Update state of remote backlight according to timeout setting */
401 static void remote_backlight_update_state(void)
403 #ifdef HAS_REMOTE_BUTTON_HOLD
404 if (remote_button_hold() && (remote_backlight_on_button_hold != 0))
405 remote_backlight_timeout = (remote_backlight_on_button_hold == 2)
406 ? 0 : -1; /* always on or always off */
407 else
408 #endif
409 #if CONFIG_CHARGING
410 if (charger_inserted()
411 #ifdef HAVE_USB_POWER
412 || usb_powered()
413 #endif
415 remote_backlight_timeout = remote_backlight_timeout_plugged;
416 else
417 #endif
418 remote_backlight_timeout = remote_backlight_timeout_normal;
420 /* Backlight == OFF in the setting? */
421 if (remote_backlight_timeout < 0)
423 remote_backlight_timer = 0; /* Disable the timeout */
424 _remote_backlight_off();
426 else
428 remote_backlight_timer = remote_backlight_timeout;
429 _remote_backlight_on();
432 #endif /* HAVE_REMOTE_LCD */
434 void backlight_thread(void)
436 struct queue_event ev;
437 bool locked = false;
439 while(1)
441 queue_wait(&backlight_queue, &ev);
442 switch(ev.id)
443 { /* These events must always be processed */
444 #ifdef _BACKLIGHT_FADE_BOOST
445 case BACKLIGHT_FADE_FINISH:
446 cpu_boost(false);
447 break;
448 #endif
449 #ifdef _BACKLIGHT_FADE_ENABLE
450 case BACKLIGHT_FADE_FINISH:
451 _backlight_hw_enable((bl_dim_current|bl_dim_target) != 0);
452 break;
453 #endif
455 #if defined(HAVE_REMOTE_LCD) && !defined(SIMULATOR)
456 /* Here for now or else the aggressive init messes up scrolling */
457 case SYS_REMOTE_PLUGGED:
458 lcd_remote_on();
459 lcd_remote_update();
460 break;
462 case SYS_REMOTE_UNPLUGGED:
463 lcd_remote_off();
464 break;
465 #endif /* defined(HAVE_REMOTE_LCD) && !defined(SIMULATOR) */
466 #ifdef SIMULATOR
467 /* This one here too for lack of a better place */
468 case SYS_SCREENDUMP:
469 screen_dump();
470 break;
471 #endif
472 case SYS_USB_CONNECTED:
473 /* Tell the USB thread that we are safe */
474 DEBUGF("backlight_thread got SYS_USB_CONNECTED\n");
475 usb_acknowledge(SYS_USB_CONNECTED_ACK);
476 break;
478 case SYS_USB_DISCONNECTED:
479 usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
480 break;
482 if (locked)
483 continue;
485 switch(ev.id)
486 { /* These events are only processed if backlight isn't locked */
487 #ifdef HAVE_REMOTE_LCD
488 case REMOTE_BACKLIGHT_ON:
489 remote_backlight_update_state();
490 break;
492 case REMOTE_BACKLIGHT_OFF:
493 remote_backlight_timer = 0; /* Disable the timeout */
494 _remote_backlight_off();
495 break;
496 #endif /* HAVE_REMOTE_LCD */
498 case BACKLIGHT_ON:
499 backlight_update_state();
500 break;
502 case BACKLIGHT_OFF:
503 backlight_timer = 0; /* Disable the timeout */
504 _backlight_off();
505 break;
507 #ifdef HAVE_LCD_SLEEP
508 case LCD_SLEEP:
509 lcd_sleep();
510 break;
511 #endif
512 #ifdef HAVE_BUTTON_LIGHT
513 case BUTTON_LIGHT_ON:
514 buttonlight_update_state();
515 break;
517 case BUTTON_LIGHT_OFF:
518 buttonlight_timer = 0;
519 _buttonlight_off();
520 break;
521 #endif
523 case SYS_POWEROFF: /* Lock backlight on poweroff so it doesn't */
524 locked = true; /* go off before power is actually cut. */
525 /* fall through */
526 #if CONFIG_CHARGING
527 case SYS_CHARGER_CONNECTED:
528 case SYS_CHARGER_DISCONNECTED:
529 #endif
530 backlight_update_state();
531 #ifdef HAVE_REMOTE_LCD
532 remote_backlight_update_state();
533 #endif
534 break;
536 } /* end while */
539 static void backlight_tick(void)
541 if(backlight_timer)
543 backlight_timer--;
544 if(backlight_timer == 0)
546 backlight_off();
549 #ifdef HAVE_LCD_SLEEP
550 else if(_lcd_sleep_timer)
552 _lcd_sleep_timer--;
553 if(_lcd_sleep_timer == 0)
555 /* Queue on bl thread or freeze! */
556 queue_post(&backlight_queue, LCD_SLEEP, 0);
559 #endif /* HAVE_LCD_SLEEP */
561 #ifdef HAVE_REMOTE_LCD
562 if(remote_backlight_timer)
564 remote_backlight_timer--;
565 if(remote_backlight_timer == 0)
567 remote_backlight_off();
570 #endif /* HAVE_REMOVE_LCD */
571 #ifdef HAVE_BUTTON_LIGHT
572 if (buttonlight_timer)
574 buttonlight_timer--;
575 if (buttonlight_timer == 0)
577 buttonlight_off();
580 #endif /* HAVE_BUTTON_LIGHT */
583 void backlight_init(void)
585 queue_init(&backlight_queue, true);
587 #ifndef SIMULATOR
588 if (_backlight_init())
590 # ifdef HAVE_BACKLIGHT_PWM_FADING
591 /* If backlight is already on, don't fade in. */
592 bl_dim_target = BL_PWM_COUNT;
593 bl_dim_fraction = (BL_PWM_COUNT<<16);
594 # endif
596 #endif
597 /* Leave all lights as set by the bootloader here. The settings load will
598 * call the appropriate backlight_set_*() functions, only changing light
599 * status if necessary. */
601 create_thread(backlight_thread, backlight_stack,
602 sizeof(backlight_stack), 0, backlight_thread_name
603 IF_PRIO(, PRIORITY_SYSTEM)
604 IF_COP(, CPU));
605 tick_add_task(backlight_tick);
608 void backlight_on(void)
610 queue_remove_from_head(&backlight_queue, BACKLIGHT_ON);
611 queue_post(&backlight_queue, BACKLIGHT_ON, 0);
614 void backlight_off(void)
616 queue_post(&backlight_queue, BACKLIGHT_OFF, 0);
619 /* returns true when the backlight is on OR when it's set to always off */
620 bool is_backlight_on(void)
622 if (backlight_timer || backlight_timeout <= 0)
623 return true;
624 else
625 return false;
628 /* return value in ticks; 0 means always on, <0 means always off */
629 int backlight_get_current_timeout(void)
631 return backlight_timeout;
634 void backlight_set_timeout(int value)
636 backlight_timeout_normal = HZ * value;
637 backlight_update_state();
640 #if CONFIG_CHARGING
641 void backlight_set_timeout_plugged(int value)
643 backlight_timeout_plugged = HZ * value;
644 backlight_update_state();
646 #endif /* CONFIG_CHARGING */
648 #ifdef HAS_BUTTON_HOLD
649 /* Hold button change event handler. */
650 void backlight_hold_changed(bool hold_button)
652 if (!hold_button || (backlight_on_button_hold > 0))
653 /* if unlocked or override in effect */
654 backlight_on();
657 void backlight_set_on_button_hold(int index)
659 if ((unsigned)index >= 3)
660 /* if given a weird value, use default */
661 index = 0;
663 backlight_on_button_hold = index;
664 backlight_update_state();
666 #endif /* HAS_BUTTON_HOLD */
668 #ifdef HAVE_LCD_SLEEP
669 void lcd_set_sleep_after_backlight_off(int index)
671 if ((unsigned)index >= sizeof(lcd_sleep_timeout_value))
672 /* if given a weird value, use default */
673 index = 3;
675 _lcd_sleep_timeout = HZ * lcd_sleep_timeout_value[index];
677 if (backlight_timer > 0 || backlight_get_current_timeout() == 0)
678 /* Timer will be set when bl turns off or bl set to on. */
679 return;
681 /* Backlight is Off */
682 if (_lcd_sleep_timeout < 0)
683 _lcd_sleep_timer = 1; /* Always - sleep next tick */
684 else
685 _lcd_sleep_timer = _lcd_sleep_timeout; /* Never, other */
687 #endif /* HAVE_LCD_SLEEP */
689 #ifdef HAVE_REMOTE_LCD
690 void remote_backlight_on(void)
692 queue_post(&backlight_queue, REMOTE_BACKLIGHT_ON, 0);
695 void remote_backlight_off(void)
697 queue_post(&backlight_queue, REMOTE_BACKLIGHT_OFF, 0);
700 void remote_backlight_set_timeout(int value)
702 remote_backlight_timeout_normal = HZ * value;
703 remote_backlight_update_state();
706 #if CONFIG_CHARGING
707 void remote_backlight_set_timeout_plugged(int value)
709 remote_backlight_timeout_plugged = HZ * value;
710 remote_backlight_update_state();
712 #endif /* CONFIG_CHARGING */
714 #ifdef HAS_REMOTE_BUTTON_HOLD
715 /* Remote hold button change event handler. */
716 void remote_backlight_hold_changed(bool rc_hold_button)
718 if (!rc_hold_button || (remote_backlight_on_button_hold > 0))
719 /* if unlocked or override */
720 remote_backlight_on();
723 void remote_backlight_set_on_button_hold(int index)
725 if ((unsigned)index >= 3)
726 /* if given a weird value, use default */
727 index = 0;
729 remote_backlight_on_button_hold = index;
730 remote_backlight_update_state();
732 #endif /* HAS_REMOTE_BUTTON_HOLD */
734 /* return value in ticks; 0 means always on, <0 means always off */
735 int remote_backlight_get_current_timeout(void)
737 return remote_backlight_timeout;
740 /* returns true when the backlight is on OR when it's set to always off */
741 bool is_remote_backlight_on(void)
743 if (remote_backlight_timer != 0 || remote_backlight_timeout <= 0)
744 return true;
745 else
746 return false;
749 #endif /* HAVE_REMOTE_LCD */
751 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
752 void backlight_set_brightness(int val)
754 if (val < MIN_BRIGHTNESS_SETTING)
755 val = MIN_BRIGHTNESS_SETTING;
756 else if (val > MAX_BRIGHTNESS_SETTING)
757 val = MAX_BRIGHTNESS_SETTING;
759 _backlight_set_brightness(val);
761 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
763 #ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
764 void buttonlight_set_brightness(int val)
766 if (val < MIN_BRIGHTNESS_SETTING)
767 val = MIN_BRIGHTNESS_SETTING;
768 else if (val > MAX_BRIGHTNESS_SETTING)
769 val = MAX_BRIGHTNESS_SETTING;
771 _buttonlight_set_brightness(val);
773 #endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
775 #else /* !defined(HAVE_BACKLIGHT) || defined(BOOTLOADER)
776 -- no backlight, empty dummy functions */
778 #if defined(BOOTLOADER) && defined(HAVE_BACKLIGHT)
779 void backlight_init(void)
781 (void)_backlight_init();
782 _backlight_on();
784 #endif
786 void backlight_on(void) {}
787 void backlight_off(void) {}
788 void buttonlight_on(void) {}
789 void backlight_set_timeout(int value) {(void)value;}
790 bool is_backlight_on(void) {return true;}
791 #ifdef HAVE_REMOTE_LCD
792 void remote_backlight_on(void) {}
793 void remote_backlight_off(void) {}
794 void remote_backlight_set_timeout(int value) {(void)value;}
795 bool is_remote_backlight_on(void) {return true;}
796 #endif /* HAVE_REMOTE_LCD */
797 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
798 void backlight_set_brightness(int val) { (void)val; }
799 #endif
800 #ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
801 void buttonlight_set_brightness(int val) { (void)val; }
802 #endif
803 #endif /* defined(HAVE_BACKLIGHT) && !defined(BOOTLOADER) */