diff options
Diffstat (limited to 'firmware/target/arm/olympus/mrobe-100/lcd-remote-mr100.c')
-rw-r--r-- | firmware/target/arm/olympus/mrobe-100/lcd-remote-mr100.c | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/firmware/target/arm/olympus/mrobe-100/lcd-remote-mr100.c b/firmware/target/arm/olympus/mrobe-100/lcd-remote-mr100.c new file mode 100644 index 0000000000..036b6ae71c --- /dev/null +++ b/firmware/target/arm/olympus/mrobe-100/lcd-remote-mr100.c | |||
@@ -0,0 +1,562 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 Mark Arigo | ||
11 | * | ||
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. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "cpu.h" | ||
23 | #include "kernel.h" | ||
24 | #include "thread.h" | ||
25 | #include "system.h" | ||
26 | #include "lcd-remote.h" | ||
27 | #include "button.h" | ||
28 | #include "button-target.h" | ||
29 | |||
30 | /* Temporary defines until we sort out why the gui stuff doesn't like this size | ||
31 | (I believe the status bar isn't doing sanity checks and is writing outside | ||
32 | the frame buffer size). */ | ||
33 | #define RC_WIDTH 79 | ||
34 | #define RC_HEIGHT 16 | ||
35 | |||
36 | #define RC_CONTRAST_MASK 0x000000ff | ||
37 | #define RC_SCREEN_ON 0x00000100 | ||
38 | #define RC_BACKLIGHT_ON 0x00000200 | ||
39 | |||
40 | #define RC_DEV_INIT 0x00001000 | ||
41 | #define RC_DETECTED 0x00002000 | ||
42 | #define RC_AWAKE 0x00004000 | ||
43 | #define RC_POWER_OFF 0x00008000 | ||
44 | |||
45 | #define RC_UPDATE_LCD 0x00010000 | ||
46 | #define RC_UPDATE_CONTROLLER 0x00020000 | ||
47 | #define RC_UPDATE_ICONS 0x00040000 | ||
48 | #define RC_UPDATE_MASK (RC_UPDATE_LCD|RC_UPDATE_CONTROLLER|RC_UPDATE_ICONS) | ||
49 | |||
50 | #define RC_TX_ERROR 0x00100000 | ||
51 | #define RC_RX_ERROR 0x00200000 | ||
52 | #define RC_TIMEOUT_ERROR 0x00400000 | ||
53 | #define RC_ERROR_MASK (RC_TX_ERROR|RC_RX_ERROR|RC_TIMEOUT_ERROR) | ||
54 | |||
55 | #define RC_FORCE_DETECT 0x80000000 | ||
56 | |||
57 | #define RX_READY 0x01 | ||
58 | #define TX_READY 0x20 | ||
59 | |||
60 | #define POLL_TIMEOUT 50000 | ||
61 | |||
62 | bool remote_initialized = false; | ||
63 | unsigned int rc_status = 0; | ||
64 | unsigned char rc_buf[5]; | ||
65 | |||
66 | /* ================================================== */ | ||
67 | /* Remote thread functions */ | ||
68 | /* These functions are private to the remote thread */ | ||
69 | /* ================================================== */ | ||
70 | static struct wakeup rc_thread_wakeup; | ||
71 | static unsigned int remote_thread_id; | ||
72 | static int remote_stack[256/sizeof(int)]; | ||
73 | static const char * const remote_thread_name = "remote"; | ||
74 | |||
75 | static bool remote_wait_ready(int ready_mask) | ||
76 | { | ||
77 | unsigned long current; | ||
78 | unsigned long start = USEC_TIMER; | ||
79 | unsigned long timeout = start + POLL_TIMEOUT; | ||
80 | |||
81 | rc_status &= ~RC_TIMEOUT_ERROR; | ||
82 | |||
83 | if (start <= timeout) | ||
84 | { | ||
85 | do | ||
86 | { | ||
87 | if (SER1_LSR & ready_mask) | ||
88 | return true; | ||
89 | |||
90 | //~ sleep(1); | ||
91 | |||
92 | current = USEC_TIMER; | ||
93 | } while (current < timeout); | ||
94 | } | ||
95 | else | ||
96 | { | ||
97 | do | ||
98 | { | ||
99 | if (SER1_LSR & ready_mask) | ||
100 | return true; | ||
101 | |||
102 | //~ sleep(1); | ||
103 | |||
104 | current = USEC_TIMER - POLL_TIMEOUT; | ||
105 | } while (current < start); | ||
106 | } | ||
107 | |||
108 | rc_status |= RC_TIMEOUT_ERROR; | ||
109 | return false; | ||
110 | } | ||
111 | |||
112 | static bool remote_rx(void) | ||
113 | { | ||
114 | int i; | ||
115 | unsigned char chksum[2]; | ||
116 | |||
117 | rc_status &= ~RC_RX_ERROR; | ||
118 | |||
119 | for (i = 0; i < 5; i++) | ||
120 | { | ||
121 | if (!remote_wait_ready(RX_READY)) | ||
122 | { | ||
123 | rc_status |= RC_RX_ERROR; | ||
124 | return false; | ||
125 | } | ||
126 | |||
127 | rc_buf[i] = SER1_RBR; | ||
128 | } | ||
129 | |||
130 | /* check opcode */ | ||
131 | if ((rc_buf[0] & 0xf0) != 0xf0) | ||
132 | { | ||
133 | rc_status |= RC_RX_ERROR; | ||
134 | return false; | ||
135 | } | ||
136 | |||
137 | /* verify the checksums */ | ||
138 | chksum[0] = chksum[1] = 0; | ||
139 | for (i = 0; i < 3; i++) | ||
140 | { | ||
141 | chksum[0] ^= rc_buf[i]; | ||
142 | chksum[1] += rc_buf[i]; | ||
143 | } | ||
144 | |||
145 | if ((chksum[0] != rc_buf[3]) && (chksum[1] != rc_buf[4])) | ||
146 | { | ||
147 | rc_status |= RC_RX_ERROR; | ||
148 | return false; | ||
149 | } | ||
150 | |||
151 | /* reception error */ | ||
152 | if ((rc_buf[0] & 0x1) || (rc_buf[0] & 0x2) || (rc_buf[0] & 0x4)) | ||
153 | { | ||
154 | rc_status |= RC_RX_ERROR; | ||
155 | return false; | ||
156 | } | ||
157 | |||
158 | return true; | ||
159 | } | ||
160 | |||
161 | static bool remote_tx(unsigned char *data, int len) | ||
162 | { | ||
163 | int i; | ||
164 | unsigned char chksum[2]; | ||
165 | |||
166 | rc_status &= ~RC_TX_ERROR; | ||
167 | |||
168 | chksum[0] = chksum[1] = 0; | ||
169 | |||
170 | for (i = 0; i < len; i++) | ||
171 | { | ||
172 | if (!remote_wait_ready(TX_READY)) | ||
173 | { | ||
174 | rc_status |= RC_TX_ERROR; | ||
175 | return false; | ||
176 | } | ||
177 | |||
178 | SER1_THR = data[i]; | ||
179 | chksum[0] ^= data[i]; | ||
180 | chksum[1] += data[i]; | ||
181 | } | ||
182 | |||
183 | for (i = 0; i < 2; i++) | ||
184 | { | ||
185 | if (!remote_wait_ready(TX_READY)) | ||
186 | { | ||
187 | rc_status |= RC_TX_ERROR; | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | SER1_THR = chksum[i]; | ||
192 | } | ||
193 | |||
194 | return remote_rx(); | ||
195 | } | ||
196 | |||
197 | static void remote_dev_enable(bool enable) | ||
198 | { | ||
199 | if (enable) | ||
200 | { | ||
201 | outl(inl(0x70000018) | 0xaa000, 0x70000018); | ||
202 | DEV_INIT2 &= ~0x800; | ||
203 | |||
204 | GPIO_CLEAR_BITWISE(GPIOL_OUTPUT_VAL, 0x80); | ||
205 | GPIO_SET_BITWISE(GPIOL_OUTPUT_EN, 0x80); | ||
206 | |||
207 | DEV_EN |= DEV_SER1; | ||
208 | |||
209 | SER1_RBR; | ||
210 | SER1_LCR = 0x80; | ||
211 | SER1_DLL = 0x50; | ||
212 | SER1_DLM = 0x00; | ||
213 | SER1_LCR = 0x03; | ||
214 | SER1_FCR = 0x07; | ||
215 | |||
216 | rc_status |= RC_DEV_INIT; | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | outl(inl(0x70000018) & ~0xaa000, 0x70000018); | ||
221 | DEV_INIT2 &= ~0x800; | ||
222 | |||
223 | GPIO_SET_BITWISE(GPIOL_OUTPUT_VAL, 0x80); | ||
224 | GPIO_SET_BITWISE(GPIOL_OUTPUT_EN, 0x80); | ||
225 | |||
226 | DEV_RS |= DEV_SER1; | ||
227 | nop; | ||
228 | DEV_RS &= ~DEV_SER1; | ||
229 | |||
230 | DEV_EN &= ~DEV_SER1; | ||
231 | |||
232 | rc_status &= ~RC_DEV_INIT; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | void remote_update_lcd(void) | ||
237 | { | ||
238 | int x, y, draw_now; | ||
239 | unsigned char data[RC_WIDTH + 7]; | ||
240 | |||
241 | /* If the draw_now bit is set, the draw occurs directly on the LCD. | ||
242 | Otherwise, the data is stored in an off-screen buffer and displayed | ||
243 | the next time a draw operation is executed with this flag set. */ | ||
244 | draw_now = 0; | ||
245 | |||
246 | for (y = 0; y < 2; y++) | ||
247 | { | ||
248 | data[0] = 0x51; | ||
249 | data[1] = draw_now << 7; | ||
250 | data[2] = RC_WIDTH; /* width */ | ||
251 | data[3] = 0; /* x1 */ | ||
252 | data[4] = y << 3; /* y1 */ | ||
253 | data[5] = RC_WIDTH; /* x2 */ | ||
254 | data[6] = (y + 1) << 3; /* y2 */ | ||
255 | |||
256 | for (x = 0; x < RC_WIDTH; x++) | ||
257 | data[x + 7] = lcd_remote_framebuffer[y][x]; | ||
258 | |||
259 | remote_tx(data, RC_WIDTH + 7); | ||
260 | |||
261 | draw_now = 1; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | static void remote_update_controller(void) | ||
266 | { | ||
267 | unsigned char data[3]; | ||
268 | |||
269 | data[0] = 0x31; | ||
270 | data[1] = 0x00; | ||
271 | if (rc_status & RC_SCREEN_ON) | ||
272 | data[1] |= 0x80; | ||
273 | if (rc_status & RC_BACKLIGHT_ON) | ||
274 | data[1] |= 0x40; | ||
275 | data[2] = (unsigned char)(rc_status & RC_CONTRAST_MASK); | ||
276 | remote_tx(data, 3); | ||
277 | } | ||
278 | |||
279 | #if 0 | ||
280 | static void remote_update_icons(unsigned char symbols) | ||
281 | { | ||
282 | unsigned char data[2]; | ||
283 | |||
284 | if (!(rc_status & RC_AWAKE) && !(rc_status & RC_SCREEN_ON)) | ||
285 | return; | ||
286 | |||
287 | data[0] = 0x41; | ||
288 | data[1] = symbols; | ||
289 | remote_tx(data, 2); | ||
290 | } | ||
291 | #endif | ||
292 | |||
293 | static bool remote_nop(void) | ||
294 | { | ||
295 | unsigned char val[2]; | ||
296 | |||
297 | val[0] = 0x11; | ||
298 | val[1] = 0x30; | ||
299 | return remote_tx(val, 2); | ||
300 | } | ||
301 | |||
302 | static void remote_wake(void) | ||
303 | { | ||
304 | if (remote_nop()) | ||
305 | { | ||
306 | rc_status |= RC_AWAKE; | ||
307 | return; | ||
308 | } | ||
309 | |||
310 | rc_status &= ~RC_AWAKE; | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | static void remote_sleep(void) | ||
315 | { | ||
316 | unsigned char data[2]; | ||
317 | |||
318 | if (rc_status & RC_AWAKE) | ||
319 | { | ||
320 | data[0] = 0x71; | ||
321 | data[1] = 0x30; | ||
322 | remote_tx(data, 2); | ||
323 | |||
324 | udelay(25000); | ||
325 | } | ||
326 | |||
327 | rc_status &= ~RC_AWAKE; | ||
328 | } | ||
329 | |||
330 | static void remote_off(void) | ||
331 | { | ||
332 | if (rc_status & RC_AWAKE) | ||
333 | remote_sleep(); | ||
334 | |||
335 | if (rc_status & RC_DEV_INIT) | ||
336 | remote_dev_enable(false); | ||
337 | |||
338 | rc_status &= ~(RC_DETECTED | RC_ERROR_MASK | RC_UPDATE_MASK); | ||
339 | remote_initialized = false; | ||
340 | } | ||
341 | |||
342 | static void remote_on(void) | ||
343 | { | ||
344 | if (!(rc_status & RC_DEV_INIT)) | ||
345 | remote_dev_enable(true); | ||
346 | |||
347 | remote_wake(); | ||
348 | |||
349 | if (rc_status & RC_AWAKE) | ||
350 | { | ||
351 | rc_status |= RC_DETECTED; | ||
352 | remote_initialized = true; | ||
353 | |||
354 | rc_status |= (RC_UPDATE_MASK | RC_SCREEN_ON | RC_BACKLIGHT_ON); | ||
355 | //~ remote_update_icons(0xf0); /* show battery */ | ||
356 | } | ||
357 | else | ||
358 | { | ||
359 | rc_status &= ~RC_DETECTED; | ||
360 | remote_initialized = false; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | static void remote_thread(void) | ||
365 | { | ||
366 | int rc_thread_sleep_count = 10; | ||
367 | int rc_thread_wait_timeout = TIMEOUT_BLOCK; | ||
368 | |||
369 | while (1) | ||
370 | { | ||
371 | wakeup_wait(&rc_thread_wakeup, rc_thread_wait_timeout); | ||
372 | |||
373 | /* Error handling (most likely due to remote not present) */ | ||
374 | if (rc_status & RC_ERROR_MASK) | ||
375 | { | ||
376 | if (--rc_thread_sleep_count == 0) | ||
377 | rc_status |= RC_POWER_OFF; | ||
378 | } | ||
379 | |||
380 | /* Power-off (thread sleeps) */ | ||
381 | if (rc_status & RC_POWER_OFF) | ||
382 | { | ||
383 | remote_off(); | ||
384 | |||
385 | rc_thread_sleep_count = 10; | ||
386 | rc_thread_wait_timeout = TIMEOUT_BLOCK; | ||
387 | |||
388 | continue; | ||
389 | } | ||
390 | |||
391 | /* Detection */ | ||
392 | if (!(rc_status & RC_DETECTED)) | ||
393 | { | ||
394 | rc_thread_wait_timeout = HZ; | ||
395 | |||
396 | if (headphones_inserted()) | ||
397 | { | ||
398 | remote_on(); | ||
399 | |||
400 | if (rc_status & RC_AWAKE) | ||
401 | { | ||
402 | rc_thread_sleep_count = 10; | ||
403 | rc_thread_wait_timeout = HZ/20; /* ~50ms for updates */ | ||
404 | } | ||
405 | } | ||
406 | else | ||
407 | { | ||
408 | if (--rc_thread_sleep_count == 0) | ||
409 | rc_status &= ~RC_POWER_OFF; | ||
410 | } | ||
411 | |||
412 | continue; | ||
413 | } | ||
414 | |||
415 | /* Update the remote (one per wakeup cycle) */ | ||
416 | if (headphones_inserted() && (rc_status & RC_AWAKE)) | ||
417 | { | ||
418 | if (rc_status & RC_SCREEN_ON) | ||
419 | { | ||
420 | /* In order of importance */ | ||
421 | if (rc_status & RC_UPDATE_CONTROLLER) | ||
422 | { | ||
423 | remote_update_controller(); | ||
424 | rc_status &= ~RC_UPDATE_CONTROLLER; | ||
425 | } | ||
426 | else if (rc_status & RC_UPDATE_LCD) | ||
427 | { | ||
428 | remote_update_lcd(); | ||
429 | rc_status &= ~RC_UPDATE_LCD; | ||
430 | } | ||
431 | else | ||
432 | { | ||
433 | remote_nop(); | ||
434 | } | ||
435 | } | ||
436 | else | ||
437 | { | ||
438 | remote_nop(); | ||
439 | } | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | |||
444 | /* ============================================= */ | ||
445 | /* Public functions */ | ||
446 | /* These should only set the update flags that */ | ||
447 | /* will be executed in the remote thread. */ | ||
448 | /* ============================================= */ | ||
449 | bool lcd_remote_read_device(unsigned char *data) | ||
450 | { | ||
451 | if (!(rc_status & RC_AWAKE) || (rc_status & RC_ERROR_MASK)) | ||
452 | return false; | ||
453 | |||
454 | /* Return the most recent data. While the remote is plugged, | ||
455 | this is updated ~50ms */ | ||
456 | data[0] = rc_buf[0]; | ||
457 | data[1] = rc_buf[1]; | ||
458 | data[2] = rc_buf[2]; | ||
459 | data[3] = rc_buf[3]; | ||
460 | data[4] = rc_buf[4]; | ||
461 | |||
462 | return true; | ||
463 | } | ||
464 | |||
465 | void lcd_remote_set_invert_display(bool yesno) | ||
466 | { | ||
467 | /* dummy function...need to introduce HAVE_LCD_REMOTE_INVERT */ | ||
468 | (void)yesno; | ||
469 | } | ||
470 | |||
471 | /* turn the display upside down (call lcd_remote_update() afterwards) */ | ||
472 | void lcd_remote_set_flip(bool yesno) | ||
473 | { | ||
474 | /* dummy function...need to introduce HAVE_LCD_REMOTE_FLIP */ | ||
475 | (void)yesno; | ||
476 | } | ||
477 | |||
478 | int lcd_remote_default_contrast(void) | ||
479 | { | ||
480 | return DEFAULT_REMOTE_CONTRAST_SETTING; | ||
481 | } | ||
482 | |||
483 | void lcd_remote_set_contrast(int val) | ||
484 | { | ||
485 | rc_status = (rc_status & ~RC_CONTRAST_MASK) | (val & RC_CONTRAST_MASK); | ||
486 | rc_status |= RC_UPDATE_CONTROLLER; | ||
487 | } | ||
488 | |||
489 | void lcd_remote_backlight(bool on) | ||
490 | { | ||
491 | if (on) | ||
492 | rc_status |= RC_BACKLIGHT_ON; | ||
493 | else | ||
494 | rc_status &= ~RC_BACKLIGHT_ON; | ||
495 | |||
496 | rc_status |= RC_UPDATE_CONTROLLER; | ||
497 | } | ||
498 | |||
499 | void lcd_remote_off(void) | ||
500 | { | ||
501 | /* should only be used to power off at shutdown */ | ||
502 | rc_status |= RC_POWER_OFF; | ||
503 | wakeup_signal(&rc_thread_wakeup); | ||
504 | |||
505 | /* wait until the things are powered off */ | ||
506 | while (rc_status & RC_DEV_INIT) | ||
507 | sleep(HZ/10); | ||
508 | } | ||
509 | |||
510 | void lcd_remote_on(void) | ||
511 | { | ||
512 | /* Only wake the remote thread if it's in the blocked state. */ | ||
513 | struct thread_entry *rc_thread = thread_id_entry(remote_thread_id); | ||
514 | if (rc_thread->state == STATE_BLOCKED || (rc_status & RC_FORCE_DETECT)) | ||
515 | { | ||
516 | rc_status &= ~RC_FORCE_DETECT; | ||
517 | rc_status &= ~RC_POWER_OFF; | ||
518 | wakeup_signal(&rc_thread_wakeup); | ||
519 | } | ||
520 | } | ||
521 | |||
522 | bool remote_detect(void) | ||
523 | { | ||
524 | return (rc_status & RC_DETECTED); | ||
525 | } | ||
526 | |||
527 | void lcd_remote_init_device(void) | ||
528 | { | ||
529 | /* reset */ | ||
530 | remote_dev_enable(false); | ||
531 | rc_status |= RC_FORCE_DETECT; /* force detection at startup */ | ||
532 | |||
533 | /* unknown */ | ||
534 | GPIO_SET_BITWISE(GPIOL_ENABLE, 0x80); | ||
535 | GPIO_CLEAR_BITWISE(GPIOL_OUTPUT_VAL, 0x80); | ||
536 | GPIO_SET_BITWISE(GPIOL_OUTPUT_EN, 0x80); | ||
537 | |||
538 | /* a thread is required to poll & update the remote */ | ||
539 | wakeup_init(&rc_thread_wakeup); | ||
540 | remote_thread_id = create_thread(remote_thread, remote_stack, | ||
541 | sizeof(remote_stack), 0, remote_thread_name | ||
542 | IF_PRIO(, PRIORITY_SYSTEM) | ||
543 | IF_COP(, CPU)); | ||
544 | } | ||
545 | |||
546 | /* Update the display. | ||
547 | This must be called after all other LCD functions that change the display. */ | ||
548 | void lcd_remote_update(void) | ||
549 | { | ||
550 | rc_status |= RC_UPDATE_LCD; | ||
551 | } | ||
552 | |||
553 | /* Update a fraction of the display. */ | ||
554 | void lcd_remote_update_rect(int x, int y, int width, int height) | ||
555 | { | ||
556 | (void)x; | ||
557 | (void)y; | ||
558 | (void)width; | ||
559 | (void)height; | ||
560 | |||
561 | rc_status |= RC_UPDATE_LCD; | ||
562 | } | ||