summaryrefslogtreecommitdiff
path: root/firmware/target/arm/olympus/mrobe-100/lcd-remote-mr100.c
diff options
context:
space:
mode:
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.c562
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
62bool remote_initialized = false;
63unsigned int rc_status = 0;
64unsigned char rc_buf[5];
65
66/* ================================================== */
67/* Remote thread functions */
68/* These functions are private to the remote thread */
69/* ================================================== */
70static struct wakeup rc_thread_wakeup;
71static unsigned int remote_thread_id;
72static int remote_stack[256/sizeof(int)];
73static const char * const remote_thread_name = "remote";
74
75static 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
112static 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
161static 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
197static 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
236void 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
265static 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
280static 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
293static 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
302static 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
314static 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
330static 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
342static 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
364static 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/* ============================================= */
449bool 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
465void 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) */
472void lcd_remote_set_flip(bool yesno)
473{
474 /* dummy function...need to introduce HAVE_LCD_REMOTE_FLIP */
475 (void)yesno;
476}
477
478int lcd_remote_default_contrast(void)
479{
480 return DEFAULT_REMOTE_CONTRAST_SETTING;
481}
482
483void 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
489void 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
499void 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
510void 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
522bool remote_detect(void)
523{
524 return (rc_status & RC_DETECTED);
525}
526
527void 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. */
548void lcd_remote_update(void)
549{
550 rc_status |= RC_UPDATE_LCD;
551}
552
553/* Update a fraction of the display. */
554void 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}