diff options
Diffstat (limited to 'firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c')
-rw-r--r-- | firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c | 192 |
1 files changed, 180 insertions, 12 deletions
diff --git a/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c b/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c index db08414ae5..9421076cce 100644 --- a/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c +++ b/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c | |||
@@ -24,37 +24,198 @@ | |||
24 | #include "button.h" | 24 | #include "button.h" |
25 | #include "backlight.h" | 25 | #include "backlight.h" |
26 | 26 | ||
27 | extern void scrollwheel(unsigned wheel_value); | ||
28 | |||
29 | #ifdef HAS_BUTTON_HOLD | 27 | #ifdef HAS_BUTTON_HOLD |
30 | static bool hold_button = false; | 28 | static bool hold_button = false; |
31 | #endif | 29 | #endif |
30 | |||
31 | #ifdef HAVE_SCROLLWHEEL | ||
32 | #define SCROLLWHEEL_BITS (1<<7|1<<6) | ||
33 | /* TIMER units */ | ||
34 | #define TIMER_TICK (TIMER_FREQ/HZ) /* how long a tick lasts */ | ||
35 | #define TIMER_MS (TIMER_TICK/(1000/HZ))/* how long a ms lasts */ | ||
36 | |||
37 | #define WHEEL_REPEAT_INTERVAL (300*TIMER_MS) /* 300ms */ | ||
38 | #define WHEEL_FAST_ON_INTERVAL ( 20*TIMER_MS) /* 20ms */ | ||
39 | #define WHEEL_FAST_OFF_INTERVAL ( 60*TIMER_MS) /* 60ms */ | ||
40 | /* phsyical clicks per rotation * wheel value changes per phys click */ | ||
41 | #define WHEEL_CHANGES_PER_CLICK 4 | ||
42 | #define WHEELCLICKS_PER_ROTATION (12*WHEEL_CHANGES_PER_CLICK) | ||
43 | |||
44 | /* | ||
45 | * based on button-e200.c, adjusted to the AMS timers and fuzev2's | ||
46 | * scrollwheel and cleaned up a little | ||
47 | */ | ||
48 | static void scrollwheel(unsigned int wheel_value) | ||
49 | { | ||
50 | /* wheel values and times from the previous irq */ | ||
51 | static unsigned int old_wheel_value = 0; | ||
52 | static unsigned int wheel_repeat = BUTTON_NONE; | ||
53 | static long last_wheel_post = 0; | ||
54 | |||
55 | /* We only post every 4th action, as this matches better with the physical | ||
56 | * clicks of the wheel */ | ||
57 | static unsigned int wheel_click_count = 0; | ||
58 | /* number of items to skip in lists, 1 in slow mode */ | ||
59 | static unsigned int wheel_delta = 0; | ||
60 | /* accumulated wheel rotations per second */ | ||
61 | static unsigned long wheel_velocity = 0; | ||
62 | /* fast or slow mode? */ | ||
63 | static int wheel_fast_mode = 0; | ||
64 | |||
65 | /* Read wheel | ||
66 | * Bits 6 and 7 of GPIOA change as follows (Gray Code): | ||
67 | * Clockwise rotation 00 -> 01 -> 11 -> 10 -> 00 | ||
68 | * Counter-clockwise 00 -> 10 -> 11 -> 01 -> 00 | ||
69 | * | ||
70 | * For easy look-up, actual wheel values act as indicies also, | ||
71 | * which is why the table seems to be not ordered correctly | ||
72 | */ | ||
73 | static const unsigned char wheel_tbl[2][4] = | ||
74 | { | ||
75 | { 2, 0, 3, 1 }, /* Clockwise rotation */ | ||
76 | { 1, 3, 0, 2 }, /* Counter-clockwise */ | ||
77 | }; | ||
78 | |||
79 | unsigned int btn = BUTTON_NONE; | ||
80 | |||
81 | if (old_wheel_value == wheel_tbl[0][wheel_value]) | ||
82 | btn = BUTTON_SCROLL_FWD; | ||
83 | else if (old_wheel_value == wheel_tbl[1][wheel_value]) | ||
84 | btn = BUTTON_SCROLL_BACK; | ||
85 | |||
86 | if (btn == BUTTON_NONE) | ||
87 | { | ||
88 | old_wheel_value = wheel_value; | ||
89 | return; | ||
90 | } | ||
91 | |||
92 | int repeat = 1; /* assume repeat */ | ||
93 | long time = TIMER1_VALUE + current_tick*TIMER_TICK; /* to timer unit */ | ||
94 | long v = (time - last_wheel_post); | ||
95 | |||
96 | /* interpolate velocity in timer_freq/timer_unit == 1/s */ | ||
97 | if (v) v = TIMER_FREQ / v; | ||
98 | |||
99 | /* accumulate velocities over time with each v */ | ||
100 | wheel_velocity = (7*wheel_velocity + v) / 8; | ||
101 | |||
102 | if (btn != wheel_repeat) | ||
103 | { | ||
104 | /* direction reversals nullify all fast mode states */ | ||
105 | wheel_repeat = btn; | ||
106 | repeat = | ||
107 | wheel_velocity = | ||
108 | wheel_click_count = 0; | ||
109 | } | ||
110 | |||
111 | if (wheel_fast_mode != 0) | ||
112 | { | ||
113 | /* fast OFF happens immediately when velocity drops below | ||
114 | threshold */ | ||
115 | if (TIME_AFTER(time, | ||
116 | last_wheel_post + WHEEL_FAST_OFF_INTERVAL)) | ||
117 | { | ||
118 | /* moving out of fast mode */ | ||
119 | wheel_fast_mode = 0; | ||
120 | /* reset velocity */ | ||
121 | wheel_velocity = 0; | ||
122 | /* wheel_delta is always 1 in slow mode */ | ||
123 | wheel_delta = 1; | ||
124 | } | ||
125 | } | ||
126 | else | ||
127 | { | ||
128 | /* fast ON gets filtered to avoid inadvertent jumps to fast mode */ | ||
129 | if (repeat && wheel_velocity > TIMER_FREQ/WHEEL_FAST_ON_INTERVAL) | ||
130 | { | ||
131 | /* moving into fast mode */ | ||
132 | wheel_fast_mode = 1 << 31; | ||
133 | wheel_click_count = 0; | ||
134 | wheel_velocity = TIMER_FREQ/WHEEL_FAST_OFF_INTERVAL; | ||
135 | } | ||
136 | else if (++wheel_click_count < WHEEL_CHANGES_PER_CLICK) | ||
137 | { /* skip some wheel changes, so that 1 post represents | ||
138 | * 1 item in lists */ | ||
139 | btn = BUTTON_NONE; | ||
140 | } | ||
141 | |||
142 | /* wheel_delta is always 1 in slow mode */ | ||
143 | wheel_delta = 1; | ||
144 | } | ||
145 | |||
146 | if (btn != BUTTON_NONE) | ||
147 | { | ||
148 | wheel_click_count = 0; | ||
149 | |||
150 | /* generate repeats if quick enough */ | ||
151 | if (repeat && TIME_BEFORE(time, | ||
152 | last_wheel_post + WHEEL_REPEAT_INTERVAL)) | ||
153 | btn |= BUTTON_REPEAT; | ||
154 | |||
155 | last_wheel_post = time; | ||
156 | |||
157 | if (queue_empty(&button_queue)) | ||
158 | { | ||
159 | queue_post(&button_queue, btn, wheel_fast_mode | | ||
160 | (wheel_delta << 24) | wheel_velocity*360/WHEELCLICKS_PER_ROTATION); | ||
161 | /* message posted - reset delta and poke backlight on*/ | ||
162 | wheel_delta = 1; | ||
163 | backlight_on(); | ||
164 | buttonlight_on(); | ||
165 | } | ||
166 | else | ||
167 | { | ||
168 | /* skipped post - increment delta */ | ||
169 | if (++wheel_delta > 0x7f) | ||
170 | wheel_delta = 0x7f; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | old_wheel_value = wheel_value; | ||
175 | } | ||
176 | #endif | ||
177 | |||
32 | void button_init_device(void) | 178 | void button_init_device(void) |
33 | { | 179 | { |
180 | #if defined(HAVE_SCROLLWHEEL) | ||
34 | GPIOA_DIR &= ~(1<<6|1<<7); | 181 | GPIOA_DIR &= ~(1<<6|1<<7); |
35 | GPIOC_DIR = 0; | 182 | GPIOC_DIR = 0; |
36 | GPIOB_DIR |= (1<<4)|(1<<0); | 183 | GPIOB_DIR |= (1<<4)|(1<<0); |
37 | 184 | ||
38 | GPIOB_PIN(4) = 1<<4; /* activate the wheel */ | 185 | GPIOB_PIN(4) = 1<<4; /* activate the wheel */ |
39 | } | ||
40 | 186 | ||
41 | void get_scrollwheel(void) | 187 | /* setup scrollwheel isr */ |
42 | { | 188 | /* clear previous irq if any */ |
43 | #if defined(HAVE_SCROLLWHEEL) && !defined(BOOTLOADER) | 189 | GPIOA_IC = SCROLLWHEEL_BITS; |
44 | /* scroll wheel handling */ | 190 | /* enable edge detecting */ |
191 | GPIOA_IS &= ~SCROLLWHEEL_BITS; | ||
192 | /* detect both raising and falling edges */ | ||
193 | GPIOA_IBE |= SCROLLWHEEL_BITS; | ||
194 | /* lastly, enable the interrupt */ | ||
195 | GPIOA_IE |= SCROLLWHEEL_BITS; | ||
196 | #endif | ||
197 | } | ||
45 | 198 | ||
199 | /* read the 2 bits at the same time */ | ||
46 | #define GPIOA_PIN76_offset ((1<<(6+2)) | (1<<(7+2))) | 200 | #define GPIOA_PIN76_offset ((1<<(6+2)) | (1<<(7+2))) |
47 | #define GPIOA_PIN76 (*(volatile unsigned char*)(GPIOA_BASE+GPIOA_PIN76_offset)) | 201 | #define GPIOA_PIN76 (*(volatile unsigned char*)(GPIOA_BASE+GPIOA_PIN76_offset)) |
48 | scrollwheel(GPIOA_PIN76 >> 6); | ||
49 | 202 | ||
203 | void button_gpioa_isr(void) | ||
204 | { | ||
205 | #if defined(HAVE_SCROLLWHEEL) | ||
206 | /* scroll wheel handling */ | ||
207 | if (GPIOA_MIS & SCROLLWHEEL_BITS) | ||
208 | scrollwheel(GPIOA_PIN76 >> 6); | ||
209 | |||
210 | /* ack interrupt */ | ||
211 | GPIOA_IC = SCROLLWHEEL_BITS; | ||
50 | #endif | 212 | #endif |
51 | } | 213 | } |
52 | 214 | ||
215 | |||
53 | /* | 216 | /* |
54 | * Get button pressed from hardware | 217 | * Get button pressed from hardware |
55 | */ | 218 | */ |
56 | |||
57 | |||
58 | int button_read_device(void) | 219 | int button_read_device(void) |
59 | { | 220 | { |
60 | int btn = 0; | 221 | int btn = 0; |
@@ -62,12 +223,12 @@ int button_read_device(void) | |||
62 | static long power_counter = 0; | 223 | static long power_counter = 0; |
63 | unsigned gpiod6; | 224 | unsigned gpiod6; |
64 | 225 | ||
65 | |||
66 | /* if we don't wait for the fifo to empty, we'll see screen corruption | 226 | /* if we don't wait for the fifo to empty, we'll see screen corruption |
67 | * (the higher the CPU frequency the higher the corruption) */ | 227 | * (the higher the CPU frequency the higher the corruption) */ |
68 | while ((DBOP_STAT & (1<<10)) == 0); | 228 | while ((DBOP_STAT & (1<<10)) == 0); |
69 | 229 | ||
70 | get_scrollwheel(); | 230 | int delay = 30; |
231 | while(delay--) nop; | ||
71 | 232 | ||
72 | CCU_IO &= ~(1<<12); | 233 | CCU_IO &= ~(1<<12); |
73 | 234 | ||
@@ -77,6 +238,7 @@ int button_read_device(void) | |||
77 | gpiod6 = GPIOD_PIN(6); | 238 | gpiod6 = GPIOD_PIN(6); |
78 | 239 | ||
79 | GPIOB_PIN(0) = 0; | 240 | GPIOB_PIN(0) = 0; |
241 | |||
80 | udelay(1); | 242 | udelay(1); |
81 | 243 | ||
82 | if (GPIOC_PIN(1) & 1<<1) | 244 | if (GPIOC_PIN(1) & 1<<1) |
@@ -114,6 +276,12 @@ int button_read_device(void) | |||
114 | { | 276 | { |
115 | hold_button_old = hold_button; | 277 | hold_button_old = hold_button; |
116 | backlight_hold_changed(hold_button); | 278 | backlight_hold_changed(hold_button); |
279 | /* mask scrollwheel irq so we don't need to check for | ||
280 | * the hold button in the isr */ | ||
281 | if (hold_button) | ||
282 | GPIOA_IE &= ~SCROLLWHEEL_BITS; | ||
283 | else | ||
284 | GPIOA_IE |= SCROLLWHEEL_BITS; | ||
117 | } | 285 | } |
118 | #else | 286 | #else |
119 | (void)hold_button_old; | 287 | (void)hold_button_old; |