Make the LCD remote work in the Iriver H1x0 and H300 bootloaders as well (untested...
[Rockbox.git] / firmware / target / coldfire / iaudio / lcd-remote-iaudio.c
blob5a03dc6180401d2e3f2d7afd65383a38be738e3c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "config.h"
20 #include "system.h"
21 #include "file.h"
22 #include "lcd-remote.h"
23 #include "scroll_engine.h"
25 /* The LCD in the iAudio M3/M5/X5 remote control is a Tomato LSI 0350 */
27 #define LCD_SET_DUTY_RATIO 0x48
28 #define LCD_SELECT_ADC 0xa0
29 #define LCD_SELECT_SHL 0xc0
30 #define LCD_SET_COM0 0x44
31 #define LCD_OSC_ON 0xab
32 #define LCD_SELECT_DCDC 0x64
33 #define LCD_SELECT_RES 0x20
34 #define LCD_SET_VOLUME 0x81
35 #define LCD_SET_BIAS 0x50
36 #define LCD_CONTROL_POWER 0x28
37 #define LCD_DISPLAY_ON 0xae
38 #define LCD_SET_INITLINE 0x40
39 #define LCD_SET_COLUMN 0x10
40 #define LCD_SET_PAGE 0xb0
41 #define LCD_SET_GRAY 0x88
42 #define LCD_SET_PWM_FRC 0x90
43 #define LCD_SET_POWER_SAVE 0xa8
44 #define LCD_REVERSE 0xa6
46 #define CS_LO and_l(~0x00000020, &GPIO1_OUT)
47 #define CS_HI or_l(0x00000020, &GPIO1_OUT)
48 #define CLK_LO and_l(~0x00004000, &GPIO_OUT)
49 #define CLK_HI or_l(0x00004000, &GPIO_OUT)
50 #define DATA_LO and_l(~0x00002000, &GPIO_OUT)
51 #define DATA_HI or_l(0x00002000, &GPIO_OUT)
52 #define RS_LO and_l(~0x00008000, &GPIO_OUT)
53 #define RS_HI or_l(0x00008000, &GPIO_OUT)
55 /* cached settings values */
56 static bool cached_invert = false;
57 static bool cached_flip = false;
58 static int cached_contrast = DEFAULT_REMOTE_CONTRAST_SETTING;
60 bool remote_initialized = false;
63 /* Standard low-level byte writer. Requires CLK high on entry */
64 static inline void _write_byte(unsigned data)
66 asm volatile (
67 "move.l (%[gpo0]), %%d0 \n" /* Get current state of data line */
68 "and.l %[dbit], %%d0 \n"
69 "beq.s 1f \n" /* and set it as previous-state bit */
70 "bset #8, %[data] \n"
71 "1: \n"
72 "move.l %[data], %%d0 \n" /* Compute the 'bit derivative', i.e. a value */
73 "lsr.l #1, %%d0 \n" /* with 1's where the data changes from the */
74 "eor.l %%d0, %[data] \n" /* previous state, and 0's where it doesn't */
75 "swap %[data] \n" /* Shift data to upper byte */
76 "lsl.l #8, %[data] \n"
78 "move.l %[cbit], %%d1 \n" /* Prepare mask for flipping CLK */
79 "or.l %[dbit], %%d1 \n" /* and DATA at once */
81 "lsl.l #1,%[data] \n" /* Shift out MSB */
82 "bcc.s 1f \n"
83 "eor.l %%d1, (%[gpo0]) \n" /* 1: Flip both CLK and DATA */
84 ".word 0x51fa \n" /* (trapf.w - shadow next insn) */
85 "1: \n"
86 "eor.l %[cbit], (%[gpo0]) \n" /* else flip CLK only */
87 "eor.l %[cbit], (%[gpo0]) \n" /* Flip CLK again */
89 "lsl.l #1,%[data] \n" /* ..unrolled.. */
90 "bcc.s 1f \n"
91 "eor.l %%d1, (%[gpo0]) \n"
92 ".word 0x51fa \n"
93 "1: \n"
94 "eor.l %[cbit], (%[gpo0]) \n"
95 "eor.l %[cbit], (%[gpo0]) \n"
97 "lsl.l #1,%[data] \n"
98 "bcc.s 1f \n"
99 "eor.l %%d1, (%[gpo0]) \n"
100 ".word 0x51fa \n"
101 "1: \n"
102 "eor.l %[cbit], (%[gpo0]) \n"
103 "eor.l %[cbit], (%[gpo0]) \n"
105 "lsl.l #1,%[data] \n"
106 "bcc.s 1f \n"
107 "eor.l %%d1, (%[gpo0]) \n"
108 ".word 0x51fa \n"
109 "1: \n"
110 "eor.l %[cbit], (%[gpo0]) \n"
111 "eor.l %[cbit], (%[gpo0]) \n"
113 "lsl.l #1,%[data] \n"
114 "bcc.s 1f \n"
115 "eor.l %%d1, (%[gpo0]) \n"
116 ".word 0x51fa \n"
117 "1: \n"
118 "eor.l %[cbit], (%[gpo0]) \n"
119 "eor.l %[cbit], (%[gpo0]) \n"
121 "lsl.l #1,%[data] \n"
122 "bcc.s 1f \n"
123 "eor.l %%d1, (%[gpo0]) \n"
124 ".word 0x51fa \n"
125 "1: \n"
126 "eor.l %[cbit], (%[gpo0]) \n"
127 "eor.l %[cbit], (%[gpo0]) \n"
129 "lsl.l #1,%[data] \n"
130 "bcc.s 1f \n"
131 "eor.l %%d1, (%[gpo0]) \n"
132 ".word 0x51fa \n"
133 "1: \n"
134 "eor.l %[cbit], (%[gpo0]) \n"
135 "eor.l %[cbit], (%[gpo0]) \n"
137 "lsl.l #1,%[data] \n"
138 "bcc.s 1f \n"
139 "eor.l %%d1, (%[gpo0]) \n"
140 ".word 0x51fa \n"
141 "1: \n"
142 "eor.l %[cbit], (%[gpo0]) \n"
143 "eor.l %[cbit], (%[gpo0]) \n"
144 : /* outputs */
145 [data]"+d"(data)
146 : /* inputs */
147 [gpo0]"a"(&GPIO_OUT),
148 [cbit]"d"(0x00004000),
149 [dbit]"d"(0x00002000)
150 : /* clobbers */
151 "d0", "d1"
155 /* Fast low-level byte writer. Don't use with high CPU clock.
156 * Requires CLK high on entry */
157 static inline void _write_fast(unsigned data)
159 asm volatile (
160 "move.w %%sr,%%d3 \n" /* Get current interrupt level */
161 "move.w #0x2700,%%sr \n" /* Disable interrupts */
163 "move.l (%[gpo0]), %%d0 \n" /* Get current state of data port */
164 "move.l %%d0, %%d1 \n"
165 "and.l %[dbit], %%d1 \n" /* Check current state of data line */
166 "beq.s 1f \n" /* and set it as previous-state bit */
167 "bset #8, %[data] \n"
168 "1: \n"
169 "move.l %[data], %%d1 \n" /* Compute the 'bit derivative', i.e. a value */
170 "lsr.l #1, %%d1 \n" /* with 1's where the data changes from the */
171 "eor.l %%d1, %[data] \n" /* previous state, and 0's where it doesn't */
172 "swap %[data] \n" /* Shift data to upper byte */
173 "lsl.l #8, %[data] \n"
175 "move.l %%d0, %%d1 \n" /* precalculate opposite state of clock line */
176 "eor.l %[cbit], %%d1 \n"
178 "lsl.l #1,%[data] \n" /* Shift out MSB */
179 "bcc.s 1f \n"
180 "eor.l %[dbit], %%d0 \n" /* 1: Flip data bit */
181 "eor.l %[dbit], %%d1 \n" /* for both clock states */
182 "1: \n"
183 "move.l %%d1, (%[gpo0]) \n" /* Output new state and set CLK */
184 "move.l %%d0, (%[gpo0]) \n" /* reset CLK */
186 "lsl.l #1,%[data] \n" /* ..unrolled.. */
187 "bcc.s 1f \n"
188 "eor.l %[dbit], %%d0 \n"
189 "eor.l %[dbit], %%d1 \n"
190 "1: \n"
191 "move.l %%d1, (%[gpo0]) \n"
192 "move.l %%d0, (%[gpo0]) \n"
194 "lsl.l #1,%[data] \n"
195 "bcc.s 1f \n"
196 "eor.l %[dbit], %%d0 \n"
197 "eor.l %[dbit], %%d1 \n"
198 "1: \n"
199 "move.l %%d1, (%[gpo0]) \n"
200 "move.l %%d0, (%[gpo0]) \n"
202 "lsl.l #1,%[data] \n"
203 "bcc.s 1f \n"
204 "eor.l %[dbit], %%d0 \n"
205 "eor.l %[dbit], %%d1 \n"
206 "1: \n"
207 "move.l %%d1, (%[gpo0]) \n"
208 "move.l %%d0, (%[gpo0]) \n"
210 "lsl.l #1,%[data] \n"
211 "bcc.s 1f \n"
212 "eor.l %[dbit], %%d0 \n"
213 "eor.l %[dbit], %%d1 \n"
214 "1: \n"
215 "move.l %%d1, (%[gpo0]) \n"
216 "move.l %%d0, (%[gpo0]) \n"
218 "lsl.l #1,%[data] \n"
219 "bcc.s 1f \n"
220 "eor.l %[dbit], %%d0 \n"
221 "eor.l %[dbit], %%d1 \n"
222 "1: \n"
223 "move.l %%d1, (%[gpo0]) \n"
224 "move.l %%d0, (%[gpo0]) \n"
226 "lsl.l #1,%[data] \n"
227 "bcc.s 1f \n"
228 "eor.l %[dbit], %%d0 \n"
229 "eor.l %[dbit], %%d1 \n"
230 "1: \n"
231 "move.l %%d1, (%[gpo0]) \n"
232 "move.l %%d0, (%[gpo0]) \n"
234 "lsl.l #1,%[data] \n"
235 "bcc.s 1f \n"
236 "eor.l %[dbit], %%d0 \n"
237 "eor.l %[dbit], %%d1 \n"
238 "1: \n"
239 "move.l %%d1, (%[gpo0]) \n"
240 "move.l %%d0, (%[gpo0]) \n"
242 "move.w %%d3, %%sr \n" /* Restore interrupt level */
243 : /* outputs */
244 [data]"+d"(data)
245 : /* inputs */
246 [gpo0]"a"(&GPIO_OUT),
247 [cbit]"d"(0x00004000),
248 [dbit]"d"(0x00002000)
249 : /* clobbers */
250 "d0", "d1", "d2", "d3"
254 void lcd_remote_write_command(int cmd)
256 RS_LO;
257 CS_LO;
258 _write_byte(cmd);
259 CS_HI;
262 void lcd_remote_write_command_ex(int cmd, int data)
264 RS_LO;
265 CS_LO;
266 _write_byte(cmd);
267 _write_byte(data);
268 CS_HI;
271 void lcd_remote_write_data(const fb_remote_data *p_words, int count)
273 const unsigned char *p_bytes = (const unsigned char *)p_words;
274 const unsigned char *p_end = (const unsigned char *)(p_words + count);
276 RS_HI;
277 CS_LO;
278 if (cpu_frequency < 50000000)
280 while (p_bytes < p_end)
281 _write_fast(*p_bytes++);
283 else
285 while (p_bytes < p_end)
286 _write_byte(*p_bytes++);
288 CS_HI;
291 int lcd_remote_default_contrast(void)
293 return DEFAULT_REMOTE_CONTRAST_SETTING;
296 void lcd_remote_powersave(bool on)
298 if(remote_initialized) {
299 if (on)
300 lcd_remote_write_command(LCD_SET_POWER_SAVE | 1);
301 else
302 lcd_remote_write_command(LCD_SET_POWER_SAVE | 1);
306 void lcd_remote_set_contrast(int val)
308 if (val < 0)
309 val = 0;
310 else if (val > 63)
311 val = 63;
313 cached_contrast = val;
314 if(remote_initialized)
315 lcd_remote_write_command_ex(LCD_SET_VOLUME, val);
318 bool remote_detect(void)
320 return (GPIO_READ & 0x01000000)?false:true;
323 void lcd_remote_on(void)
325 CS_HI;
326 CLK_HI;
327 sleep(HZ/100);
329 lcd_remote_write_command(LCD_SET_DUTY_RATIO);
330 lcd_remote_write_command(0x70); /* 1/128 */
332 lcd_remote_write_command(LCD_OSC_ON);
334 lcd_remote_write_command(LCD_SELECT_DCDC | 2); /* DC/DC 5xboost */
336 lcd_remote_write_command(LCD_SELECT_RES | 7); /* Regulator resistor: 7.2 */
338 lcd_remote_write_command(LCD_SET_BIAS | 6); /* 1/11 */
340 lcd_remote_write_command(LCD_CONTROL_POWER | 7); /* All circuits ON */
342 sleep(3*HZ/100);
344 lcd_remote_write_command_ex(LCD_SET_GRAY | 0, 0x00);
345 lcd_remote_write_command_ex(LCD_SET_GRAY | 1, 0x00);
346 lcd_remote_write_command_ex(LCD_SET_GRAY | 2, 0x0c);
347 lcd_remote_write_command_ex(LCD_SET_GRAY | 3, 0x00);
348 lcd_remote_write_command_ex(LCD_SET_GRAY | 4, 0xcc);
349 lcd_remote_write_command_ex(LCD_SET_GRAY | 5, 0x00);
350 lcd_remote_write_command_ex(LCD_SET_GRAY | 6, 0xcc);
351 lcd_remote_write_command_ex(LCD_SET_GRAY | 7, 0x0c);
353 lcd_remote_write_command(LCD_SET_PWM_FRC | 6); /* 4FRC + 12PWM */
355 lcd_remote_write_command(LCD_DISPLAY_ON | 1); /* display on */
357 remote_initialized = true;
359 lcd_remote_set_flip(cached_flip);
360 lcd_remote_set_contrast(cached_contrast);
361 lcd_remote_set_invert_display(cached_invert);
364 void lcd_remote_off(void)
366 remote_initialized = false;
367 CS_HI;
368 RS_HI;
371 void lcd_remote_poweroff(void)
373 /* Set power save -> Power OFF (VDD - VSS) .. that's it */
374 if (remote_initialized)
375 lcd_remote_write_command(LCD_SET_POWER_SAVE | 1);
378 #ifndef BOOTLOADER
379 /* Monitor remote hotswap */
380 static void remote_tick(void)
382 static bool last_status = false;
383 static int countdown = 0;
384 bool current_status;
386 current_status = remote_detect();
388 /* Only report when the status has changed */
389 if (current_status != last_status)
391 last_status = current_status;
392 countdown = current_status ? 20*HZ : 1;
394 else
396 /* Count down until it gets negative */
397 if (countdown >= 0)
398 countdown--;
400 if (current_status)
402 if (!(countdown % 48))
404 queue_broadcast(SYS_REMOTE_PLUGGED, 0);
407 else
409 if (countdown == 0)
411 queue_broadcast(SYS_REMOTE_UNPLUGGED, 0);
416 #endif
418 void lcd_remote_init_device(void)
420 or_l(0x0000e000, &GPIO_OUT);
421 or_l(0x0000e000, &GPIO_ENABLE);
422 or_l(0x0000e000, &GPIO_FUNCTION);
424 or_l(0x00000020, &GPIO1_OUT);
425 or_l(0x00000020, &GPIO1_ENABLE);
426 or_l(0x00000020, &GPIO1_FUNCTION);
428 and_l(~0x01000000, &GPIO_OUT);
429 and_l(~0x01000000, &GPIO_ENABLE);
430 or_l(0x01000000, &GPIO_FUNCTION);
432 lcd_remote_clear_display();
433 if (remote_detect())
434 lcd_remote_on();
435 #ifndef BOOTLOADER
436 tick_add_task(remote_tick);
437 #endif
440 /* Update the display.
441 This must be called after all other LCD functions that change the display. */
442 void lcd_remote_update(void) ICODE_ATTR;
443 void lcd_remote_update(void)
445 int y;
446 if(remote_initialized) {
447 for(y = 0;y < LCD_REMOTE_FBHEIGHT;y++) {
448 /* Copy display bitmap to hardware.
449 The COM48-COM63 lines are not connected so we have to skip
450 them. Further, the column address doesn't wrap, so we
451 have to update one page at a time. */
452 lcd_remote_write_command(LCD_SET_PAGE | (y>5?y+2:y));
453 lcd_remote_write_command_ex(LCD_SET_COLUMN | 0, 0);
454 lcd_remote_write_data(lcd_remote_framebuffer[y], LCD_REMOTE_WIDTH);
459 /* Update a fraction of the display. */
460 void lcd_remote_update_rect(int, int, int, int) ICODE_ATTR;
461 void lcd_remote_update_rect(int x, int y, int width, int height)
463 if(remote_initialized) {
464 int ymax;
466 /* The Y coordinates have to work on even 8 pixel rows */
467 ymax = (y + height-1) >> 3;
468 y >>= 3;
470 if(x + width > LCD_REMOTE_WIDTH)
471 width = LCD_REMOTE_WIDTH - x;
472 if (width <= 0)
473 return; /* nothing left to do, 0 is harmful to lcd_write_data() */
474 if(ymax >= LCD_REMOTE_FBHEIGHT)
475 ymax = LCD_REMOTE_FBHEIGHT-1;
477 /* Copy specified rectangle bitmap to hardware
478 COM48-COM63 are not connected, so we need to skip those */
479 for (; y <= ymax; y++)
481 lcd_remote_write_command(LCD_SET_PAGE |
482 ((y > 5?y + 2:y) & 0xf));
483 lcd_remote_write_command_ex(LCD_SET_COLUMN | ((x >> 4) & 0xf),
484 x & 0xf);
486 lcd_remote_write_data(&lcd_remote_framebuffer[y][x], width);
491 void lcd_remote_set_invert_display(bool yesno)
493 cached_invert = yesno;
494 if(remote_initialized)
495 lcd_remote_write_command(LCD_REVERSE | yesno);
498 void lcd_remote_set_flip(bool yesno)
500 cached_flip = yesno;
501 if(remote_initialized) {
502 if(yesno) {
503 lcd_remote_write_command(LCD_SELECT_ADC | 0);
504 lcd_remote_write_command(LCD_SELECT_SHL | 0);
505 lcd_remote_write_command_ex(LCD_SET_COM0, 16);
506 } else {
507 lcd_remote_write_command(LCD_SELECT_ADC | 1);
508 lcd_remote_write_command(LCD_SELECT_SHL | 8);
509 lcd_remote_write_command_ex(LCD_SET_COM0, 0);