1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
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 ****************************************************************************/
34 #include "backlight.h"
37 #ifdef HAVE_REMOTE_LCD
38 #include "lcd-remote.h"
41 #include "backlight-target.h"
44 #if !defined(BOOTLOADER)
45 /* The whole driver should be built */
46 #define BACKLIGHT_FULL_INIT
50 /* TODO: find a better way to do it but we need a kernel thread somewhere to
52 extern void screen_dump(void);
54 static inline void _backlight_on(void)
59 static inline void _backlight_off(void)
64 static inline void _backlight_set_brightness(int val
)
69 static inline void _buttonlight_on(void)
73 static inline void _buttonlight_off(void)
77 static inline void _buttonlight_set_brightness(int val
)
81 #ifdef HAVE_REMOTE_LCD
82 static inline void _remote_backlight_on(void)
84 sim_remote_backlight(100);
87 static inline void _remote_backlight_off(void)
89 sim_remote_backlight(0);
91 #endif /* HAVE_REMOTE_LCD */
93 #endif /* SIMULATOR */
95 #if defined(HAVE_BACKLIGHT) && defined(BACKLIGHT_FULL_INIT)
100 #ifdef HAVE_REMOTE_LCD
102 REMOTE_BACKLIGHT_OFF
,
104 #if defined(_BACKLIGHT_FADE_BOOST) || defined(_BACKLIGHT_FADE_ENABLE)
105 BACKLIGHT_FADE_FINISH
,
107 #ifdef HAVE_LCD_SLEEP
110 #ifdef HAVE_BUTTON_LIGHT
114 #ifdef BACKLIGHT_DRIVER_CLOSE
119 static void backlight_thread(void);
120 static long backlight_stack
[DEFAULT_STACK_SIZE
/sizeof(long)];
121 static const char backlight_thread_name
[] = "backlight";
122 static struct event_queue backlight_queue
;
123 #ifdef BACKLIGHT_DRIVER_CLOSE
124 static struct thread_entry
*backlight_thread_p
= NULL
;
127 static int backlight_timer SHAREDBSS_ATTR
;
128 static int backlight_timeout SHAREDBSS_ATTR
;
129 static int backlight_timeout_normal
= 5*HZ
;
131 static int backlight_timeout_plugged
= 5*HZ
;
133 #ifdef HAS_BUTTON_HOLD
134 static int backlight_on_button_hold
= 0;
137 #ifdef HAVE_BUTTON_LIGHT
138 static int buttonlight_timer
;
139 int _buttonlight_timeout
= 5*HZ
;
141 /* Update state of buttonlight according to timeout setting */
142 static void buttonlight_update_state(void)
144 buttonlight_timer
= _buttonlight_timeout
;
146 /* Buttonlight == OFF in the setting? */
147 if (buttonlight_timer
< 0)
149 buttonlight_timer
= 0; /* Disable the timeout */
156 /* external interface */
157 void buttonlight_on(void)
159 queue_remove_from_head(&backlight_queue
, BUTTON_LIGHT_ON
);
160 queue_post(&backlight_queue
, BUTTON_LIGHT_ON
, 0);
163 void buttonlight_off(void)
165 queue_post(&backlight_queue
, BUTTON_LIGHT_OFF
, 0);
168 void buttonlight_set_timeout(int value
)
170 _buttonlight_timeout
= HZ
* value
;
171 buttonlight_update_state();
174 #endif /* HAVE_BUTTON_LIGHT */
176 #ifdef HAVE_REMOTE_LCD
177 static int remote_backlight_timer
;
178 static int remote_backlight_timeout
;
179 static int remote_backlight_timeout_normal
= 5*HZ
;
181 static int remote_backlight_timeout_plugged
= 5*HZ
;
183 #ifdef HAS_REMOTE_BUTTON_HOLD
184 static int remote_backlight_on_button_hold
= 0;
186 #endif /* HAVE_REMOTE_LCD */
188 #ifdef HAVE_LCD_SLEEP
189 #ifdef HAVE_LCD_SLEEP_SETTING
190 const signed char lcd_sleep_timeout_value
[10] =
192 -1, 0, 5, 10, 15, 20, 30, 45, 60, 90
194 static int lcd_sleep_timeout
= 10*HZ
;
196 /* Target defines needed value */
197 static const int lcd_sleep_timeout
= LCD_SLEEP_TIMEOUT
;
200 static int lcd_sleep_timer
= 0;
202 void backlight_lcd_sleep_countdown(bool start
)
206 /* Cancel the LCD sleep countdown */
211 /* Start LCD sleep countdown */
212 if (lcd_sleep_timeout
< 0)
214 lcd_sleep_timer
= 0; /* Setting == Always */
219 lcd_sleep_timer
= lcd_sleep_timeout
;
222 #endif /* HAVE_LCD_SLEEP */
224 #if defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR)
225 /* backlight fading */
226 #define BL_PWM_INTERVAL 5 /* Cycle interval in ms */
227 #define BL_PWM_BITS 8
228 #define BL_PWM_COUNT (1<<BL_PWM_BITS)
230 /* s15.16 fixed point variables */
231 static int32_t bl_fade_in_step
= ((BL_PWM_INTERVAL
*BL_PWM_COUNT
)<<16)/300;
232 static int32_t bl_fade_out_step
= ((BL_PWM_INTERVAL
*BL_PWM_COUNT
)<<16)/2000;
233 static int32_t bl_dim_fraction
= 0;
235 static int bl_dim_target
= 0;
236 static int bl_dim_current
= 0;
237 static enum {DIM_STATE_START
, DIM_STATE_MAIN
} bl_dim_state
= DIM_STATE_START
;
238 static bool bl_timer_active
= false;
240 static void backlight_isr(void)
242 int timer_period
= (TIMER_FREQ
*BL_PWM_INTERVAL
/1000);
245 switch (bl_dim_state
)
248 case DIM_STATE_START
:
249 bl_dim_current
= bl_dim_fraction
>> 16;
251 if (bl_dim_current
> 0 && bl_dim_current
< BL_PWM_COUNT
)
254 timer_period
= (timer_period
* bl_dim_current
) >> BL_PWM_BITS
;
255 bl_dim_state
= DIM_STATE_MAIN
;
262 _backlight_off_isr();
263 if (bl_dim_current
== bl_dim_target
)
266 if (bl_dim_current
< bl_dim_target
)
268 bl_dim_fraction
= MIN(bl_dim_fraction
+ bl_fade_in_step
,
271 else if (bl_dim_current
> bl_dim_target
)
273 bl_dim_fraction
= MAX(bl_dim_fraction
- bl_fade_out_step
, 0);
277 /* Dim main screen */
279 _backlight_off_isr();
280 timer_period
= (timer_period
* (BL_PWM_COUNT
- bl_dim_current
))
282 bl_dim_state
= DIM_STATE_START
;
287 #if defined(_BACKLIGHT_FADE_BOOST) || defined(_BACKLIGHT_FADE_ENABLE)
288 queue_post(&backlight_queue
, BACKLIGHT_FADE_FINISH
, 0);
291 bl_timer_active
= false;
294 timer_set_period(timer_period
);
297 static void backlight_switch(void)
299 if (bl_dim_target
> (BL_PWM_COUNT
/2))
301 _backlight_on_normal();
302 bl_dim_fraction
= (BL_PWM_COUNT
<<16);
306 _backlight_off_normal();
311 static void backlight_release_timer(void)
313 #ifdef _BACKLIGHT_FADE_BOOST
317 bl_timer_active
= false;
321 static void backlight_dim(int value
)
323 /* protect from extraneous calls with the same target value */
324 if (value
== bl_dim_target
)
327 bl_dim_target
= value
;
332 if (timer_register(0, backlight_release_timer
, 2, 0, backlight_isr
335 #ifdef _BACKLIGHT_FADE_BOOST
336 /* Prevent cpu frequency changes while dimming. */
339 bl_timer_active
= true;
345 static void _backlight_on(void)
347 #ifdef HAVE_LCD_SLEEP
348 backlight_lcd_sleep_countdown(false);
351 if (bl_fade_in_step
> 0)
353 #ifdef _BACKLIGHT_FADE_ENABLE
354 _backlight_hw_enable(true);
356 backlight_dim(BL_PWM_COUNT
);
360 bl_dim_target
= BL_PWM_COUNT
;
361 bl_dim_fraction
= (BL_PWM_COUNT
<<16);
362 _backlight_on_normal();
366 static void _backlight_off(void)
368 if (bl_fade_out_step
> 0)
374 bl_dim_target
= bl_dim_fraction
= 0;
375 _backlight_off_normal();
378 #ifdef HAVE_LCD_SLEEP
379 backlight_lcd_sleep_countdown(true);
383 void backlight_set_fade_in(int value
)
386 bl_fade_in_step
= ((BL_PWM_INTERVAL
*BL_PWM_COUNT
)<<16) / value
;
391 void backlight_set_fade_out(int value
)
394 bl_fade_out_step
= ((BL_PWM_INTERVAL
*BL_PWM_COUNT
)<<16) / value
;
396 bl_fade_out_step
= 0;
398 #endif /* defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR) */
400 /* Update state of backlight according to timeout setting */
401 static void backlight_update_state(void)
403 #ifdef HAS_BUTTON_HOLD
404 if ((backlight_on_button_hold
!= 0)
405 #ifdef HAVE_REMOTE_LCD_AS_MAIN
406 && remote_button_hold()
411 backlight_timeout
= (backlight_on_button_hold
== 2) ? 0 : -1;
412 /* always on or always off */
416 if (charger_inserted()
417 #ifdef HAVE_USB_POWER
421 backlight_timeout
= backlight_timeout_plugged
;
424 backlight_timeout
= backlight_timeout_normal
;
426 /* Backlight == OFF in the setting? */
427 if (backlight_timeout
< 0)
429 backlight_timer
= 0; /* Disable the timeout */
434 backlight_timer
= backlight_timeout
;
439 #ifdef HAVE_REMOTE_LCD
440 /* Update state of remote backlight according to timeout setting */
441 static void remote_backlight_update_state(void)
443 #ifdef HAS_REMOTE_BUTTON_HOLD
444 if (remote_button_hold() && (remote_backlight_on_button_hold
!= 0))
445 remote_backlight_timeout
= (remote_backlight_on_button_hold
== 2)
446 ? 0 : -1; /* always on or always off */
450 if (charger_inserted()
451 #ifdef HAVE_USB_POWER
455 remote_backlight_timeout
= remote_backlight_timeout_plugged
;
458 remote_backlight_timeout
= remote_backlight_timeout_normal
;
460 /* Backlight == OFF in the setting? */
461 if (remote_backlight_timeout
< 0)
463 remote_backlight_timer
= 0; /* Disable the timeout */
464 _remote_backlight_off();
468 remote_backlight_timer
= remote_backlight_timeout
;
469 _remote_backlight_on();
472 #endif /* HAVE_REMOTE_LCD */
474 void backlight_thread(void)
476 struct queue_event ev
;
481 queue_wait(&backlight_queue
, &ev
);
483 { /* These events must always be processed */
484 #ifdef _BACKLIGHT_FADE_BOOST
485 case BACKLIGHT_FADE_FINISH
:
489 #ifdef _BACKLIGHT_FADE_ENABLE
490 case BACKLIGHT_FADE_FINISH
:
491 _backlight_hw_enable((bl_dim_current
|bl_dim_target
) != 0);
496 /* Here for now or else the aggressive init messes up scrolling */
497 #ifdef HAVE_REMOTE_LCD
498 case SYS_REMOTE_PLUGGED
:
503 case SYS_REMOTE_UNPLUGGED
:
506 #elif defined HAVE_REMOTE_LCD_AS_MAIN
507 case SYS_REMOTE_PLUGGED
:
512 case SYS_REMOTE_UNPLUGGED
:
515 #endif /* HAVE_REMOTE_LCD/ HAVE_REMOTE_LCD_AS_MAIN */
516 #endif /* !SIMULATOR */
518 /* This one here too for lack of a better place */
523 case SYS_USB_CONNECTED
:
524 /* Tell the USB thread that we are safe */
525 DEBUGF("backlight_thread got SYS_USB_CONNECTED\n");
526 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
529 case SYS_USB_DISCONNECTED
:
530 usb_acknowledge(SYS_USB_DISCONNECTED_ACK
);
533 #ifdef BACKLIGHT_DRIVER_CLOSE
534 /* Get out of here */
543 { /* These events are only processed if backlight isn't locked */
544 #ifdef HAVE_REMOTE_LCD
545 case REMOTE_BACKLIGHT_ON
:
546 remote_backlight_update_state();
549 case REMOTE_BACKLIGHT_OFF
:
550 remote_backlight_timer
= 0; /* Disable the timeout */
551 _remote_backlight_off();
553 #endif /* HAVE_REMOTE_LCD */
556 backlight_update_state();
560 backlight_timer
= 0; /* Disable the timeout */
564 #ifdef HAVE_LCD_SLEEP
569 #ifdef HAVE_BUTTON_LIGHT
570 case BUTTON_LIGHT_ON
:
571 buttonlight_update_state();
574 case BUTTON_LIGHT_OFF
:
575 buttonlight_timer
= 0;
580 case SYS_POWEROFF
: /* Lock backlight on poweroff so it doesn't */
581 locked
= true; /* go off before power is actually cut. */
584 case SYS_CHARGER_CONNECTED
:
585 case SYS_CHARGER_DISCONNECTED
:
587 backlight_update_state();
588 #ifdef HAVE_REMOTE_LCD
589 remote_backlight_update_state();
596 static void backlight_tick(void)
601 if(backlight_timer
== 0)
606 #ifdef HAVE_LCD_SLEEP
607 else if(lcd_sleep_timer
)
610 if(lcd_sleep_timer
== 0)
612 /* Queue on bl thread or freeze! */
613 queue_post(&backlight_queue
, LCD_SLEEP
, 0);
616 #endif /* HAVE_LCD_SLEEP */
618 #ifdef HAVE_REMOTE_LCD
619 if(remote_backlight_timer
)
621 remote_backlight_timer
--;
622 if(remote_backlight_timer
== 0)
624 remote_backlight_off();
627 #endif /* HAVE_REMOVE_LCD */
628 #ifdef HAVE_BUTTON_LIGHT
629 if (buttonlight_timer
)
632 if (buttonlight_timer
== 0)
637 #endif /* HAVE_BUTTON_LIGHT */
640 void backlight_init(void)
642 queue_init(&backlight_queue
, true);
645 if (_backlight_init())
647 # ifdef HAVE_BACKLIGHT_PWM_FADING
648 /* If backlight is already on, don't fade in. */
649 bl_dim_target
= BL_PWM_COUNT
;
650 bl_dim_fraction
= (BL_PWM_COUNT
<<16);
654 /* Leave all lights as set by the bootloader here. The settings load will
655 * call the appropriate backlight_set_*() functions, only changing light
656 * status if necessary. */
657 #ifdef BACKLIGHT_DRIVER_CLOSE
660 create_thread(backlight_thread
, backlight_stack
,
661 sizeof(backlight_stack
), 0, backlight_thread_name
662 IF_PRIO(, PRIORITY_USER_INTERFACE
)
664 tick_add_task(backlight_tick
);
667 #ifdef BACKLIGHT_DRIVER_CLOSE
668 void backlight_close(void)
670 struct thread_entry
*thread
= backlight_thread_p
;
672 /* Wait for thread to exit */
676 backlight_thread_p
= NULL
;
678 queue_post(&backlight_queue
, BACKLIGHT_QUIT
, 0);
681 #endif /* BACKLIGHT_DRIVER_CLOSE */
683 void backlight_on(void)
685 queue_remove_from_head(&backlight_queue
, BACKLIGHT_ON
);
686 queue_post(&backlight_queue
, BACKLIGHT_ON
, 0);
689 void backlight_off(void)
691 queue_post(&backlight_queue
, BACKLIGHT_OFF
, 0);
694 /* returns true when the backlight is on,
695 * and optionally when it's set to always off. */
696 bool is_backlight_on(bool ignore_always_off
)
698 return (backlight_timer
> 0) /* countdown */
699 || (backlight_timeout
== 0) /* always on */
700 || ((backlight_timeout
< 0) && !ignore_always_off
);
703 /* return value in ticks; 0 means always on, <0 means always off */
704 int backlight_get_current_timeout(void)
706 return backlight_timeout
;
709 void backlight_set_timeout(int value
)
711 backlight_timeout_normal
= HZ
* value
;
712 backlight_update_state();
716 void backlight_set_timeout_plugged(int value
)
718 backlight_timeout_plugged
= HZ
* value
;
719 backlight_update_state();
721 #endif /* CONFIG_CHARGING */
723 #ifdef HAS_BUTTON_HOLD
724 /* Hold button change event handler. */
725 void backlight_hold_changed(bool hold_button
)
727 if (!hold_button
|| (backlight_on_button_hold
> 0))
728 /* if unlocked or override in effect */
732 void backlight_set_on_button_hold(int index
)
734 if ((unsigned)index
>= 3)
735 /* if given a weird value, use default */
738 backlight_on_button_hold
= index
;
739 backlight_update_state();
741 #endif /* HAS_BUTTON_HOLD */
743 #ifdef HAVE_LCD_SLEEP_SETTING
744 void lcd_set_sleep_after_backlight_off(int index
)
746 if ((unsigned)index
>= sizeof(lcd_sleep_timeout_value
))
747 /* if given a weird value, use default */
750 lcd_sleep_timeout
= HZ
* lcd_sleep_timeout_value
[index
];
752 if (backlight_timer
> 0 || backlight_get_current_timeout() == 0)
753 /* Timer will be set when bl turns off or bl set to on. */
756 /* Backlight is Off */
757 if (lcd_sleep_timeout
< 0)
758 lcd_sleep_timer
= 1; /* Always - sleep next tick */
760 lcd_sleep_timer
= lcd_sleep_timeout
; /* Never, other */
762 #endif /* HAVE_LCD_SLEEP_SETTING */
764 #ifdef HAVE_REMOTE_LCD
765 void remote_backlight_on(void)
767 queue_post(&backlight_queue
, REMOTE_BACKLIGHT_ON
, 0);
770 void remote_backlight_off(void)
772 queue_post(&backlight_queue
, REMOTE_BACKLIGHT_OFF
, 0);
775 void remote_backlight_set_timeout(int value
)
777 remote_backlight_timeout_normal
= HZ
* value
;
778 remote_backlight_update_state();
782 void remote_backlight_set_timeout_plugged(int value
)
784 remote_backlight_timeout_plugged
= HZ
* value
;
785 remote_backlight_update_state();
787 #endif /* CONFIG_CHARGING */
789 #ifdef HAS_REMOTE_BUTTON_HOLD
790 /* Remote hold button change event handler. */
791 void remote_backlight_hold_changed(bool rc_hold_button
)
793 if (!rc_hold_button
|| (remote_backlight_on_button_hold
> 0))
794 /* if unlocked or override */
795 remote_backlight_on();
798 void remote_backlight_set_on_button_hold(int index
)
800 if ((unsigned)index
>= 3)
801 /* if given a weird value, use default */
804 remote_backlight_on_button_hold
= index
;
805 remote_backlight_update_state();
807 #endif /* HAS_REMOTE_BUTTON_HOLD */
809 /* return value in ticks; 0 means always on, <0 means always off */
810 int remote_backlight_get_current_timeout(void)
812 return remote_backlight_timeout
;
815 /* returns true when the backlight is on, and
816 * optionally when it's set to always off */
817 bool is_remote_backlight_on(bool ignore_always_off
)
819 return (remote_backlight_timer
> 0) /* countdown */
820 || (remote_backlight_timeout
== 0) /* always on */
821 || ((remote_backlight_timeout
< 0) && !ignore_always_off
);
824 #endif /* HAVE_REMOTE_LCD */
826 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
827 void backlight_set_brightness(int val
)
829 if (val
< MIN_BRIGHTNESS_SETTING
)
830 val
= MIN_BRIGHTNESS_SETTING
;
831 else if (val
> MAX_BRIGHTNESS_SETTING
)
832 val
= MAX_BRIGHTNESS_SETTING
;
834 _backlight_set_brightness(val
);
836 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
838 #ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
839 void buttonlight_set_brightness(int val
)
841 if (val
< MIN_BRIGHTNESS_SETTING
)
842 val
= MIN_BRIGHTNESS_SETTING
;
843 else if (val
> MAX_BRIGHTNESS_SETTING
)
844 val
= MAX_BRIGHTNESS_SETTING
;
846 _buttonlight_set_brightness(val
);
848 #endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
850 #else /* !defined(HAVE_BACKLIGHT) || !defined(BACKLIGHT_FULL_INIT)
851 -- no backlight, empty dummy functions */
853 #if defined(HAVE_BACKLIGHT) && !defined(BACKLIGHT_FULL_INIT)
854 void backlight_init(void)
856 (void)_backlight_init();
861 void backlight_on(void) {}
862 void backlight_off(void) {}
863 void buttonlight_on(void) {}
864 void backlight_set_timeout(int value
) {(void)value
;}
866 bool is_backlight_on(bool ignore_always_off
)
868 (void)ignore_always_off
;
871 #ifdef HAVE_REMOTE_LCD
872 void remote_backlight_on(void) {}
873 void remote_backlight_off(void) {}
874 void remote_backlight_set_timeout(int value
) {(void)value
;}
876 bool is_remote_backlight_on(bool ignore_always_off
)
878 (void)ignore_always_off
;
881 #endif /* HAVE_REMOTE_LCD */
882 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
883 void backlight_set_brightness(int val
) { (void)val
; }
885 #ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
886 void buttonlight_set_brightness(int val
) { (void)val
; }
888 #endif /* defined(HAVE_BACKLIGHT) && defined(BACKLIGHT_FULL_INIT) */