Fix FS#8196 - Gather Runtime Data > User Rating not working
[Rockbox.git] / firmware / backlight.c
blob78ba4929429ba548a22fa144a5ccd6d6f1547447
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 const signed char backlight_timeout_value[19] =
94 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 45, 60, 90
97 enum {
98 BACKLIGHT_ON,
99 BACKLIGHT_OFF,
100 #ifdef HAVE_REMOTE_LCD
101 REMOTE_BACKLIGHT_ON,
102 REMOTE_BACKLIGHT_OFF,
103 #endif
104 #if defined(_BACKLIGHT_FADE_BOOST) || defined(_BACKLIGHT_FADE_ENABLE)
105 BACKLIGHT_FADE_FINISH,
106 #endif
107 #ifdef HAVE_LCD_SLEEP
108 LCD_SLEEP,
109 #endif
110 #ifdef HAVE_BUTTON_LIGHT
111 BUTTON_LIGHT_ON,
112 BUTTON_LIGHT_OFF,
113 #endif
116 static void backlight_thread(void);
117 static long backlight_stack[DEFAULT_STACK_SIZE/sizeof(long)];
118 static const char backlight_thread_name[] = "backlight";
119 static struct event_queue backlight_queue;
121 static int backlight_timer;
122 static int backlight_timeout;
123 static int backlight_timeout_normal = 5*HZ;
124 #if CONFIG_CHARGING
125 static int backlight_timeout_plugged = 5*HZ;
126 #endif
127 #ifdef HAS_BUTTON_HOLD
128 static int backlight_on_button_hold = 0;
129 #endif
131 #ifdef HAVE_BUTTON_LIGHT
132 static int buttonlight_timer;
133 int _buttonlight_timeout = 5*HZ;
135 /* Update state of buttonlight according to timeout setting */
136 static void buttonlight_update_state(void)
138 buttonlight_timer = _buttonlight_timeout;
140 /* Buttonlight == OFF in the setting? */
141 if (buttonlight_timer < 0)
143 buttonlight_timer = 0; /* Disable the timeout */
144 _buttonlight_off();
146 else
147 _buttonlight_on();
150 /* external interface */
151 void buttonlight_on(void)
153 queue_remove_from_head(&backlight_queue, BUTTON_LIGHT_ON);
154 queue_post(&backlight_queue, BUTTON_LIGHT_ON, 0);
157 void buttonlight_off(void)
159 queue_post(&backlight_queue, BUTTON_LIGHT_OFF, 0);
162 void buttonlight_set_timeout(int index)
164 if((unsigned)index >= sizeof(backlight_timeout_value))
165 /* if given a weird value, use default */
166 index = 6;
167 _buttonlight_timeout = HZ * backlight_timeout_value[index];
168 buttonlight_update_state();
171 #endif
173 #ifdef HAVE_REMOTE_LCD
174 static int remote_backlight_timer;
175 static int remote_backlight_timeout;
176 static int remote_backlight_timeout_normal = 5*HZ;
177 #if CONFIG_CHARGING
178 static int remote_backlight_timeout_plugged = 5*HZ;
179 #endif
180 #ifdef HAS_REMOTE_BUTTON_HOLD
181 static int remote_backlight_on_button_hold = 0;
182 #endif
183 #endif
185 #ifdef HAVE_LCD_SLEEP
186 const signed char lcd_sleep_timeout_value[10] =
188 -1, 0, 5, 10, 15, 20, 30, 45, 60, 90
190 int _lcd_sleep_timer;
191 int _lcd_sleep_timeout = 10*HZ;
192 #endif
194 #if defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR)
195 /* backlight fading */
196 #define BL_PWM_INTERVAL 5000 /* Cycle interval in us */
197 #define BL_PWM_COUNT 100
198 static const char backlight_fade_value[8] = { 0, 1, 2, 4, 6, 8, 10, 20 };
199 static int fade_in_count = 1;
200 static int fade_out_count = 4;
202 static bool bl_timer_active = false;
203 static int bl_dim_current = 0;
204 static int bl_dim_target = 0;
205 static int bl_pwm_counter = 0;
206 static volatile int bl_cycle_counter = 0;
207 static enum {DIM_STATE_START, DIM_STATE_MAIN} bl_dim_state = DIM_STATE_START;
209 static void backlight_isr(void)
211 int timer_period;
212 bool idle = false;
214 timer_period = TIMER_FREQ / 1000 * BL_PWM_INTERVAL / 1000;
215 switch (bl_dim_state)
217 /* New cycle */
218 case DIM_STATE_START:
219 bl_pwm_counter = 0;
220 bl_cycle_counter++;
222 if (bl_dim_current > 0 && bl_dim_current < BL_PWM_COUNT)
224 _backlight_on_isr();
225 bl_pwm_counter = bl_dim_current;
226 timer_period = timer_period * bl_pwm_counter / BL_PWM_COUNT;
227 bl_dim_state = DIM_STATE_MAIN;
229 else
231 if (bl_dim_current)
232 _backlight_on_isr();
233 else
234 _backlight_off_isr();
235 if (bl_dim_current == bl_dim_target)
236 idle = true;
239 break ;
241 /* Dim main screen */
242 case DIM_STATE_MAIN:
243 _backlight_off_isr();
244 bl_dim_state = DIM_STATE_START;
245 timer_period = timer_period * (BL_PWM_COUNT - bl_pwm_counter) / BL_PWM_COUNT;
246 break ;
249 if ((bl_dim_target > bl_dim_current) && (bl_cycle_counter >= fade_in_count))
251 bl_dim_current++;
252 bl_cycle_counter = 0;
255 if ((bl_dim_target < bl_dim_current) && (bl_cycle_counter >= fade_out_count))
257 bl_dim_current--;
258 bl_cycle_counter = 0;
261 if (idle)
263 #if defined(_BACKLIGHT_FADE_BOOST) || defined(_BACKLIGHT_FADE_ENABLE)
264 queue_post(&backlight_queue, BACKLIGHT_FADE_FINISH, 0);
265 #endif
266 timer_unregister();
267 bl_timer_active = false;
269 else
270 timer_set_period(timer_period);
273 static void backlight_switch(void)
275 if (bl_dim_target > (BL_PWM_COUNT/2))
277 _backlight_on_normal();
278 bl_dim_current = BL_PWM_COUNT;
280 else
282 _backlight_off_normal();
283 bl_dim_current = 0;
287 static void backlight_release_timer(void)
289 #ifdef _BACKLIGHT_FADE_BOOST
290 cpu_boost(false);
291 #endif
292 timer_unregister();
293 bl_timer_active = false;
294 backlight_switch();
297 static void backlight_dim(int value)
299 /* protect from extraneous calls with the same target value */
300 if (value == bl_dim_target)
301 return;
303 bl_dim_target = value;
305 if (bl_timer_active)
306 return ;
308 if (timer_register(0, backlight_release_timer, 2, 0, backlight_isr))
310 #ifdef _BACKLIGHT_FADE_BOOST
311 /* Prevent cpu frequency changes while dimming. */
312 cpu_boost(true);
313 #endif
314 bl_timer_active = true;
316 else
317 backlight_switch();
320 static void _backlight_on(void)
322 if (fade_in_count > 0)
324 #ifdef _BACKLIGHT_FADE_ENABLE
325 _backlight_hw_enable(true);
326 #endif
327 backlight_dim(BL_PWM_COUNT);
329 else
331 bl_dim_target = bl_dim_current = BL_PWM_COUNT;
332 _backlight_on_normal();
334 #ifdef HAVE_LCD_SLEEP
335 _lcd_sleep_timer = 0; /* LCD should be awake already */
336 #endif
339 static void _backlight_off(void)
341 if (fade_out_count > 0)
343 backlight_dim(0);
345 else
347 bl_dim_target = bl_dim_current = 0;
348 _backlight_off_normal();
350 #ifdef HAVE_LCD_SLEEP
351 /* Start LCD sleep countdown */
352 if (_lcd_sleep_timeout < 0)
354 _lcd_sleep_timer = 0; /* Setting == Always */
355 lcd_sleep();
357 else
358 _lcd_sleep_timer = _lcd_sleep_timeout;
359 #endif
362 void backlight_set_fade_in(int index)
364 fade_in_count = backlight_fade_value[index];
367 void backlight_set_fade_out(int index)
369 fade_out_count = backlight_fade_value[index];
371 #endif /* defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR) */
373 /* Update state of backlight according to timeout setting */
374 static void backlight_update_state(void)
376 #ifdef HAS_BUTTON_HOLD
377 if (button_hold() && (backlight_on_button_hold != 0))
378 backlight_timeout = (backlight_on_button_hold == 2) ? 0 : -1;
379 /* always on or always off */
380 else
381 #endif
382 #if CONFIG_CHARGING
383 if (charger_inserted()
384 #ifdef HAVE_USB_POWER
385 || usb_powered()
386 #endif
388 backlight_timeout = backlight_timeout_plugged;
389 else
390 #endif
391 backlight_timeout = backlight_timeout_normal;
393 /* Backlight == OFF in the setting? */
394 if (backlight_timeout < 0)
396 backlight_timer = 0; /* Disable the timeout */
397 _backlight_off();
399 else
401 backlight_timer = backlight_timeout;
402 _backlight_on();
406 #ifdef HAVE_REMOTE_LCD
407 /* Update state of remote backlight according to timeout setting */
408 static void remote_backlight_update_state(void)
410 #ifdef HAS_REMOTE_BUTTON_HOLD
411 if (remote_button_hold() && (remote_backlight_on_button_hold != 0))
412 remote_backlight_timeout = (remote_backlight_on_button_hold == 2)
413 ? 0 : -1; /* always on or always off */
414 else
415 #endif
416 #if CONFIG_CHARGING
417 if (charger_inserted()
418 #ifdef HAVE_USB_POWER
419 || usb_powered()
420 #endif
422 remote_backlight_timeout = remote_backlight_timeout_plugged;
423 else
424 #endif
425 remote_backlight_timeout = remote_backlight_timeout_normal;
427 /* Backlight == OFF in the setting? */
428 if (remote_backlight_timeout < 0)
430 remote_backlight_timer = 0; /* Disable the timeout */
431 _remote_backlight_off();
433 else
435 remote_backlight_timer = remote_backlight_timeout;
436 _remote_backlight_on();
439 #endif /* HAVE_REMOTE_LCD */
441 void backlight_thread(void)
443 struct queue_event ev;
444 bool locked = false;
446 while(1)
448 queue_wait(&backlight_queue, &ev);
449 switch(ev.id)
450 { /* These events must always be processed */
451 #ifdef _BACKLIGHT_FADE_BOOST
452 case BACKLIGHT_FADE_FINISH:
453 cpu_boost(false);
454 break;
455 #endif
456 #ifdef _BACKLIGHT_FADE_ENABLE
457 case BACKLIGHT_FADE_FINISH:
458 _backlight_hw_enable((bl_dim_current|bl_dim_target) != 0);
459 break;
460 #endif
462 #if defined(HAVE_REMOTE_LCD) && !defined(SIMULATOR)
463 /* Here for now or else the aggressive init messes up scrolling */
464 case SYS_REMOTE_PLUGGED:
465 lcd_remote_on();
466 lcd_remote_update();
467 break;
469 case SYS_REMOTE_UNPLUGGED:
470 lcd_remote_off();
471 break;
472 #endif /* defined(HAVE_REMOTE_LCD) && !defined(SIMULATOR) */
473 #ifdef SIMULATOR
474 /* This one here too for lack of a better place */
475 case SYS_SCREENDUMP:
476 screen_dump();
477 break;
478 #endif
479 case SYS_USB_CONNECTED:
480 /* Tell the USB thread that we are safe */
481 DEBUGF("backlight_thread got SYS_USB_CONNECTED\n");
482 usb_acknowledge(SYS_USB_CONNECTED_ACK);
483 break;
485 case SYS_USB_DISCONNECTED:
486 usb_acknowledge(SYS_USB_DISCONNECTED_ACK);
487 break;
489 if (locked)
490 continue;
492 switch(ev.id)
493 { /* These events are only processed if backlight isn't locked */
494 #ifdef HAVE_REMOTE_LCD
495 case REMOTE_BACKLIGHT_ON:
496 remote_backlight_update_state();
497 break;
499 case REMOTE_BACKLIGHT_OFF:
500 remote_backlight_timer = 0; /* Disable the timeout */
501 _remote_backlight_off();
502 break;
503 #endif /* HAVE_REMOTE_LCD */
505 case BACKLIGHT_ON:
506 backlight_update_state();
507 break;
509 case BACKLIGHT_OFF:
510 backlight_timer = 0; /* Disable the timeout */
511 _backlight_off();
512 break;
514 #ifdef HAVE_LCD_SLEEP
515 case LCD_SLEEP:
516 lcd_sleep();
517 break;
518 #endif
519 #ifdef HAVE_BUTTON_LIGHT
520 case BUTTON_LIGHT_ON:
521 buttonlight_update_state();
522 break;
524 case BUTTON_LIGHT_OFF:
525 buttonlight_timer = 0;
526 _buttonlight_off();
527 break;
528 #endif
530 case SYS_POWEROFF: /* Lock backlight on poweroff so it doesn't */
531 locked = true; /* go off before power is actually cut. */
532 /* fall through */
533 #if CONFIG_CHARGING
534 case SYS_CHARGER_CONNECTED:
535 case SYS_CHARGER_DISCONNECTED:
536 #endif
537 backlight_update_state();
538 #ifdef HAVE_REMOTE_LCD
539 remote_backlight_update_state();
540 #endif
541 break;
543 } /* end while */
546 static void backlight_tick(void)
548 if(backlight_timer)
550 backlight_timer--;
551 if(backlight_timer == 0)
553 backlight_off();
556 #ifdef HAVE_LCD_SLEEP
557 else if(_lcd_sleep_timer)
559 _lcd_sleep_timer--;
560 if(_lcd_sleep_timer == 0)
562 /* Queue on bl thread or freeze! */
563 queue_post(&backlight_queue, LCD_SLEEP, 0);
566 #endif /* HAVE_LCD_SLEEP */
568 #ifdef HAVE_REMOTE_LCD
569 if(remote_backlight_timer)
571 remote_backlight_timer--;
572 if(remote_backlight_timer == 0)
574 remote_backlight_off();
577 #endif /* HAVE_REMOVE_LCD */
578 #ifdef HAVE_BUTTON_LIGHT
579 if (buttonlight_timer)
581 buttonlight_timer--;
582 if (buttonlight_timer == 0)
584 buttonlight_off();
587 #endif /* HAVE_BUTTON_LIGHT */
590 void backlight_init(void)
592 queue_init(&backlight_queue, true);
594 #ifndef SIMULATOR
595 if (_backlight_init())
597 # ifdef HAVE_BACKLIGHT_PWM_FADING
598 /* If backlight is already on, don't fade in. */
599 bl_dim_current = BL_PWM_COUNT;
600 bl_dim_target = BL_PWM_COUNT;
601 # endif
603 #endif
604 /* Leave all lights as set by the bootloader here. The settings load will
605 * call the appropriate backlight_set_*() functions, only changing light
606 * status if necessary. */
608 create_thread(backlight_thread, backlight_stack,
609 sizeof(backlight_stack), 0, backlight_thread_name
610 IF_PRIO(, PRIORITY_SYSTEM)
611 IF_COP(, CPU));
612 tick_add_task(backlight_tick);
615 void backlight_on(void)
617 queue_remove_from_head(&backlight_queue, BACKLIGHT_ON);
618 queue_post(&backlight_queue, BACKLIGHT_ON, 0);
621 void backlight_off(void)
623 queue_post(&backlight_queue, BACKLIGHT_OFF, 0);
626 /* returns true when the backlight is on OR when it's set to always off */
627 bool is_backlight_on(void)
629 if (backlight_timer || backlight_timeout <= 0)
630 return true;
631 else
632 return false;
635 /* return value in ticks; 0 means always on, <0 means always off */
636 int backlight_get_current_timeout(void)
638 return backlight_timeout;
641 void backlight_set_timeout(int index)
643 if((unsigned)index >= sizeof(backlight_timeout_value))
644 /* if given a weird value, use default */
645 index = 6;
646 backlight_timeout_normal = HZ * backlight_timeout_value[index];
647 backlight_update_state();
650 #if CONFIG_CHARGING
651 void backlight_set_timeout_plugged(int index)
653 if((unsigned)index >= sizeof(backlight_timeout_value))
654 /* if given a weird value, use default */
655 index = 6;
656 backlight_timeout_plugged = HZ * backlight_timeout_value[index];
657 backlight_update_state();
659 #endif /* CONFIG_CHARGING */
661 #ifdef HAS_BUTTON_HOLD
662 /* Hold button change event handler. */
663 void backlight_hold_changed(bool hold_button)
665 if (!hold_button || (backlight_on_button_hold > 0))
666 /* if unlocked or override in effect */
667 backlight_on();
670 void backlight_set_on_button_hold(int index)
672 if ((unsigned)index >= 3)
673 /* if given a weird value, use default */
674 index = 0;
676 backlight_on_button_hold = index;
677 backlight_update_state();
679 #endif /* HAS_BUTTON_HOLD */
681 #ifdef HAVE_LCD_SLEEP
682 void lcd_set_sleep_after_backlight_off(int index)
684 if ((unsigned)index >= sizeof(lcd_sleep_timeout_value))
685 /* if given a weird value, use default */
686 index = 3;
688 _lcd_sleep_timeout = HZ * lcd_sleep_timeout_value[index];
690 if (backlight_timer > 0 || backlight_get_current_timeout() == 0)
691 /* Timer will be set when bl turns off or bl set to on. */
692 return;
694 /* Backlight is Off */
695 if (_lcd_sleep_timeout < 0)
696 _lcd_sleep_timer = 1; /* Always - sleep next tick */
697 else
698 _lcd_sleep_timer = _lcd_sleep_timeout; /* Never, other */
700 #endif /* HAVE_LCD_SLEEP */
702 #ifdef HAVE_REMOTE_LCD
703 void remote_backlight_on(void)
705 queue_post(&backlight_queue, REMOTE_BACKLIGHT_ON, 0);
708 void remote_backlight_off(void)
710 queue_post(&backlight_queue, REMOTE_BACKLIGHT_OFF, 0);
713 void remote_backlight_set_timeout(int index)
715 if((unsigned)index >= sizeof(backlight_timeout_value))
716 /* if given a weird value, use default */
717 index=6;
718 remote_backlight_timeout_normal = HZ * backlight_timeout_value[index];
719 remote_backlight_update_state();
722 #if CONFIG_CHARGING
723 void remote_backlight_set_timeout_plugged(int index)
725 if((unsigned)index >= sizeof(backlight_timeout_value))
726 /* if given a weird value, use default */
727 index=6;
728 remote_backlight_timeout_plugged = HZ * backlight_timeout_value[index];
729 remote_backlight_update_state();
731 #endif /* CONFIG_CHARGING */
733 #ifdef HAS_REMOTE_BUTTON_HOLD
734 /* Remote hold button change event handler. */
735 void remote_backlight_hold_changed(bool rc_hold_button)
737 if (!rc_hold_button || (remote_backlight_on_button_hold > 0))
738 /* if unlocked or override */
739 remote_backlight_on();
742 void remote_backlight_set_on_button_hold(int index)
744 if ((unsigned)index >= 3)
745 /* if given a weird value, use default */
746 index = 0;
748 remote_backlight_on_button_hold = index;
749 remote_backlight_update_state();
751 #endif /* HAS_REMOTE_BUTTON_HOLD */
753 /* return value in ticks; 0 means always on, <0 means always off */
754 int remote_backlight_get_current_timeout(void)
756 return remote_backlight_timeout;
759 /* returns true when the backlight is on OR when it's set to always off */
760 bool is_remote_backlight_on(void)
762 if (remote_backlight_timer != 0 || remote_backlight_timeout <= 0)
763 return true;
764 else
765 return false;
768 #endif /* HAVE_REMOTE_LCD */
770 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
771 void backlight_set_brightness(int val)
773 if (val < MIN_BRIGHTNESS_SETTING)
774 val = MIN_BRIGHTNESS_SETTING;
775 else if (val > MAX_BRIGHTNESS_SETTING)
776 val = MAX_BRIGHTNESS_SETTING;
778 _backlight_set_brightness(val);
780 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
782 #ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
783 void buttonlight_set_brightness(int val)
785 if (val < MIN_BRIGHTNESS_SETTING)
786 val = MIN_BRIGHTNESS_SETTING;
787 else if (val > MAX_BRIGHTNESS_SETTING)
788 val = MAX_BRIGHTNESS_SETTING;
790 _buttonlight_set_brightness(val);
792 #endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
794 #else /* !defined(HAVE_BACKLIGHT) || defined(BOOTLOADER)
795 -- no backlight, empty dummy functions */
797 #if defined(BOOTLOADER) && defined(HAVE_BACKLIGHT)
798 void backlight_init(void)
800 (void)_backlight_init();
801 _backlight_on();
803 #endif
805 void backlight_on(void) {}
806 void backlight_off(void) {}
807 void buttonlight_on(void) {}
808 void backlight_set_timeout(int index) {(void)index;}
809 bool is_backlight_on(void) {return true;}
810 #ifdef HAVE_REMOTE_LCD
811 void remote_backlight_on(void) {}
812 void remote_backlight_off(void) {}
813 void remote_backlight_set_timeout(int index) {(void)index;}
814 bool is_remote_backlight_on(void) {return true;}
815 #endif /* HAVE_REMOTE_LCD */
816 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
817 void backlight_set_brightness(int val) { (void)val; }
818 #endif
819 #ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
820 void buttonlight_set_brightness(int val) { (void)val; }
821 #endif
822 #endif /* defined(HAVE_BACKLIGHT) && !defined(BOOTLOADER) */