Implement software pwm to control c200v2 display brightness.
[kugel-rb.git] / firmware / target / arm / as3525 / kernel-as3525.c
blob4c421e50fc125d06f1cbd8d57f50b1eda9a44e60
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright © 2008 Rafaël Carré
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 ****************************************************************************/
21 #include "config.h"
22 #include "system.h"
23 #include "kernel.h"
24 #include "panic.h"
25 #include "timer.h"
27 #ifdef HAVE_SCROLLWHEEL
28 /* let the timer interrupt twice as often for the scrollwheel polling */
29 #define KERNEL_TIMER_FREQ (TIMER_FREQ/2)
30 #else
31 #define KERNEL_TIMER_FREQ TIMER_FREQ
32 #endif
34 #ifdef HAVE_SCROLLWHEEL
35 #include "button-target.h"
36 /* The scrollwheel is polled every 5 ms (the tick tasks only every 10) */
37 static int poll_scrollwheel = 0;
39 static inline void do_scrollwheel(void)
41 if (!poll_scrollwheel)
42 call_tick_tasks(); /* Run through the list of tick tasks
43 * (that includes reading the scrollwheel) */
44 else
46 if (!button_hold())
47 button_read_dbop(); /* Read the scrollwheel */
50 poll_scrollwheel ^= 1;
52 #else
53 static inline void do_scrollwheel(void)
55 call_tick_tasks(); /* Run through the list of tick tasks */
57 #endif
59 #if defined(SANSA_C200V2)
60 #include "backlight-target.h"
62 static int timer2_cycles_per_tick = 0;
63 static int timer2_cycles_pwmon = 0;
64 static int timer2_cycles_pwmoff = 0;
65 static int timer2_pwm_state = 0;
66 static int timer2_pwm_on = 0;
68 void _set_timer2_pwm_ratio(int ratio)
70 int cycles = timer2_cycles_per_tick;
73 * Rather arbitrary limits, but since the CPU
74 * needs some to time in the interrupt handler
75 * there sure is some limit.
76 * More specifically, if the cycles needed to do
77 * the pwm handling are more than the reloaded counter needs
78 * to reach 0 again it will reload to the old value most
79 * likely leading to a (slight) slowdown in tick rate.
82 if (ratio < 10) {
84 * Permanent off, reduce interrupt rate to save power
86 TIMER2_BGLOAD = cycles;
87 timer2_pwm_on = 0;
88 _backlight_pwm(0);
89 return;
92 if (ratio > 990) {
94 * Permanent on, reduce interrupt rate to save power
96 TIMER2_BGLOAD = cycles;
97 timer2_pwm_on = 0;
98 _backlight_pwm(1);
99 return;
102 timer2_cycles_pwmon = cycles*ratio/1000;
103 timer2_cycles_pwmoff = cycles*(1000-ratio)/1000;
105 if (timer2_pwm_on == 0) {
106 timer2_pwm_state = 0;
107 timer2_pwm_on = 1;
108 TIMER2_BGLOAD = timer2_cycles_pwmoff;
112 static void set_timer2_cycles_per_tick(int cycles)
114 timer2_cycles_per_tick = cycles;
117 static inline void do_sw_pwm(void)
119 if (!timer2_pwm_on) {
120 do_scrollwheel(); /* Handle scrollwheel and tick tasks */
121 TIMER2_INTCLR = 0; /* clear interrupt */
122 return;
125 timer2_pwm_state ^= 1;
126 if (timer2_pwm_state) {
127 TIMER2_BGLOAD = timer2_cycles_pwmoff;
128 _backlight_pwm(1);
130 * Always do scrollwheel and tick tasks during the longer cycle for safety,
131 * since the short cycle can be quite short.
132 * (minimum: 1us if ratio is 10 or 990 or 0.5us with scrollwheel,
133 * or just about 6000 clock cycles at 60MHz)
135 if (timer2_cycles_pwmon > timer2_cycles_pwmoff)
136 do_scrollwheel(); /* Handle scrollwheel and tick tasks */
137 } else {
138 TIMER2_BGLOAD = timer2_cycles_pwmon;
139 _backlight_pwm(0);
140 if (!(timer2_cycles_pwmon > timer2_cycles_pwmoff))
141 do_scrollwheel(); /* Handle scrollwheel and tick tasks */
144 TIMER2_INTCLR = 0; /* clear interrupt */
146 #else
147 static inline void do_sw_pwm(void)
149 do_scrollwheel(); /* Handle scrollwheel and tick tasks */
152 static void set_timer2_cycles_per_tick(int cycles)
154 (void)cycles;
156 #endif
159 void INT_TIMER2(void)
162 * Timer is stacked as follows:
163 * Lowest layer: Software PWM (if configured)
164 * Alternates timer2 reload value to implement
165 * software pwm at 100Hz (no scrollwheel)
166 * or 200Hz (scrollwheel) with variable pulse width 1% to 99%
167 * Middle layer: Scrollwheel handling (if configured, 200Hz)
168 * Alternate between polling scrollwheel and running tick
169 * tasks (includes scrollwheel polling).
170 * Top layer: Run tick tasks at 100Hz
172 do_sw_pwm();
174 TIMER2_INTCLR = 0; /* clear interrupt */
177 void tick_start(unsigned int interval_in_ms)
179 int cycles = KERNEL_TIMER_FREQ / 1000 * interval_in_ms;
181 CGU_PERI |= CGU_TIMER2_CLOCK_ENABLE; /* enable peripheral */
182 VIC_INT_ENABLE = INTERRUPT_TIMER2; /* enable interrupt */
184 set_timer2_cycles_per_tick(cycles);
185 TIMER2_LOAD = TIMER2_BGLOAD = cycles; /* timer period */
187 /* /!\ bit 4 (reserved) must not be modified
188 * periodic mode, interrupt enabled, no prescale, 32 bits counter */
189 TIMER2_CONTROL = (TIMER2_CONTROL & (1<<4)) |
190 TIMER_ENABLE |
191 TIMER_PERIODIC |
192 TIMER_INT_ENABLE |
193 TIMER_32_BIT;