1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
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
)
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 */
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 */
83 "eor.l %%d1, (%[gpo0]) \n" /* 1: Flip both CLK and DATA */
84 ".word 0x51fa \n" /* (trapf.w - shadow next insn) */
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.. */
91 "eor.l %%d1, (%[gpo0]) \n"
94 "eor.l %[cbit], (%[gpo0]) \n"
95 "eor.l %[cbit], (%[gpo0]) \n"
99 "eor.l %%d1, (%[gpo0]) \n"
102 "eor.l %[cbit], (%[gpo0]) \n"
103 "eor.l %[cbit], (%[gpo0]) \n"
105 "lsl.l #1,%[data] \n"
107 "eor.l %%d1, (%[gpo0]) \n"
110 "eor.l %[cbit], (%[gpo0]) \n"
111 "eor.l %[cbit], (%[gpo0]) \n"
113 "lsl.l #1,%[data] \n"
115 "eor.l %%d1, (%[gpo0]) \n"
118 "eor.l %[cbit], (%[gpo0]) \n"
119 "eor.l %[cbit], (%[gpo0]) \n"
121 "lsl.l #1,%[data] \n"
123 "eor.l %%d1, (%[gpo0]) \n"
126 "eor.l %[cbit], (%[gpo0]) \n"
127 "eor.l %[cbit], (%[gpo0]) \n"
129 "lsl.l #1,%[data] \n"
131 "eor.l %%d1, (%[gpo0]) \n"
134 "eor.l %[cbit], (%[gpo0]) \n"
135 "eor.l %[cbit], (%[gpo0]) \n"
137 "lsl.l #1,%[data] \n"
139 "eor.l %%d1, (%[gpo0]) \n"
142 "eor.l %[cbit], (%[gpo0]) \n"
143 "eor.l %[cbit], (%[gpo0]) \n"
147 [gpo0
]"a"(&GPIO_OUT
),
148 [cbit
]"d"(0x00004000),
149 [dbit
]"d"(0x00002000)
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
)
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"
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 */
180 "eor.l %[dbit], %%d0 \n" /* 1: Flip data bit */
181 "eor.l %[dbit], %%d1 \n" /* for both clock states */
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.. */
188 "eor.l %[dbit], %%d0 \n"
189 "eor.l %[dbit], %%d1 \n"
191 "move.l %%d1, (%[gpo0]) \n"
192 "move.l %%d0, (%[gpo0]) \n"
194 "lsl.l #1,%[data] \n"
196 "eor.l %[dbit], %%d0 \n"
197 "eor.l %[dbit], %%d1 \n"
199 "move.l %%d1, (%[gpo0]) \n"
200 "move.l %%d0, (%[gpo0]) \n"
202 "lsl.l #1,%[data] \n"
204 "eor.l %[dbit], %%d0 \n"
205 "eor.l %[dbit], %%d1 \n"
207 "move.l %%d1, (%[gpo0]) \n"
208 "move.l %%d0, (%[gpo0]) \n"
210 "lsl.l #1,%[data] \n"
212 "eor.l %[dbit], %%d0 \n"
213 "eor.l %[dbit], %%d1 \n"
215 "move.l %%d1, (%[gpo0]) \n"
216 "move.l %%d0, (%[gpo0]) \n"
218 "lsl.l #1,%[data] \n"
220 "eor.l %[dbit], %%d0 \n"
221 "eor.l %[dbit], %%d1 \n"
223 "move.l %%d1, (%[gpo0]) \n"
224 "move.l %%d0, (%[gpo0]) \n"
226 "lsl.l #1,%[data] \n"
228 "eor.l %[dbit], %%d0 \n"
229 "eor.l %[dbit], %%d1 \n"
231 "move.l %%d1, (%[gpo0]) \n"
232 "move.l %%d0, (%[gpo0]) \n"
234 "lsl.l #1,%[data] \n"
236 "eor.l %[dbit], %%d0 \n"
237 "eor.l %[dbit], %%d1 \n"
239 "move.l %%d1, (%[gpo0]) \n"
240 "move.l %%d0, (%[gpo0]) \n"
242 "move.w %%d3, %%sr \n" /* Restore interrupt level */
246 [gpo0
]"a"(&GPIO_OUT
),
247 [cbit
]"d"(0x00004000),
248 [dbit
]"d"(0x00002000)
250 "d0", "d1", "d2", "d3"
254 void lcd_remote_write_command(int cmd
)
262 void lcd_remote_write_command_ex(int cmd
, int data
)
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
);
278 if (cpu_frequency
< 50000000)
280 while (p_bytes
< p_end
)
281 _write_fast(*p_bytes
++);
285 while (p_bytes
< p_end
)
286 _write_byte(*p_bytes
++);
291 int lcd_remote_default_contrast(void)
293 return DEFAULT_REMOTE_CONTRAST_SETTING
;
296 void lcd_remote_powersave(bool on
)
298 if(remote_initialized
) {
300 lcd_remote_write_command(LCD_SET_POWER_SAVE
| 1);
302 lcd_remote_write_command(LCD_SET_POWER_SAVE
| 1);
306 void lcd_remote_set_contrast(int val
)
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)
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 */
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;
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);
379 /* Monitor remote hotswap */
380 static void remote_tick(void)
382 static bool last_status
= false;
383 static int countdown
= 0;
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;
396 /* Count down until it gets negative */
402 if (!(countdown
% 48))
404 queue_broadcast(SYS_REMOTE_PLUGGED
, 0);
411 queue_broadcast(SYS_REMOTE_UNPLUGGED
, 0);
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();
436 tick_add_task(remote_tick
);
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)
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
) {
466 /* The Y coordinates have to work on even 8 pixel rows */
467 ymax
= (y
+ height
-1) >> 3;
470 if(x
+ width
> LCD_REMOTE_WIDTH
)
471 width
= LCD_REMOTE_WIDTH
- x
;
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),
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
)
501 if(remote_initialized
) {
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);
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);