diff options
Diffstat (limited to 'firmware/target/arm/lcd-c200_c200v2.c')
-rw-r--r-- | firmware/target/arm/lcd-c200_c200v2.c | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/firmware/target/arm/lcd-c200_c200v2.c b/firmware/target/arm/lcd-c200_c200v2.c new file mode 100644 index 0000000000..b3e8f0eb4f --- /dev/null +++ b/firmware/target/arm/lcd-c200_c200v2.c | |||
@@ -0,0 +1,455 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by 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 | #include "config.h" | ||
22 | #include "cpu.h" | ||
23 | #include "lcd.h" | ||
24 | #include "kernel.h" | ||
25 | #include "system.h" | ||
26 | |||
27 | #ifdef SANSA_C200V2 | ||
28 | /* button driver needs to know if a lcd operation is in progress */ | ||
29 | static bool lcd_busy = false; | ||
30 | #endif | ||
31 | |||
32 | /* Display status */ | ||
33 | static unsigned lcd_yuv_options SHAREDBSS_ATTR = 0; | ||
34 | static bool is_lcd_enabled = true; | ||
35 | |||
36 | /* LCD command set for Samsung S6B33B2 */ | ||
37 | |||
38 | #define R_NOP 0x00 | ||
39 | #define R_OSCILLATION_MODE 0x02 | ||
40 | #define R_DRIVER_OUTPUT_MODE 0x10 | ||
41 | #define R_DCDC_SET 0x20 | ||
42 | #define R_BIAS_SET 0x22 | ||
43 | #define R_DCDC_CLOCK_DIV 0x24 | ||
44 | #define R_DCDC_AMP_ONOFF 0x26 | ||
45 | #define R_TEMP_COMPENSATION 0x28 | ||
46 | #define R_CONTRAST_CONTROL1 0x2a | ||
47 | #define R_CONTRAST_CONTROL2 0x2b | ||
48 | #define R_STANDBY_OFF 0x2c | ||
49 | #define R_STANDBY_ON 0x2d | ||
50 | #define R_DDRAM_BURST_OFF 0x2e | ||
51 | #define R_DDRAM_BURST_ON 0x2f | ||
52 | #define R_ADDRESSING_MODE 0x30 | ||
53 | #define R_ROW_VECTOR_MODE 0x32 | ||
54 | #define R_N_LINE_INVERSION 0x34 | ||
55 | #define R_FRAME_FREQ_CONTROL 0x36 | ||
56 | #define R_RED_PALETTE 0x38 | ||
57 | #define R_GREEN_PALETTE 0x3a | ||
58 | #define R_BLUE_PALETTE 0x3c | ||
59 | #define R_ENTRY_MODE 0x40 | ||
60 | #define R_X_ADDR_AREA 0x42 | ||
61 | #define R_Y_ADDR_AREA 0x43 | ||
62 | #define R_RAM_SKIP_AREA 0x45 | ||
63 | #define R_DISPLAY_OFF 0x50 | ||
64 | #define R_DISPLAY_ON 0x51 | ||
65 | #define R_SPEC_DISPLAY_PATTERN 0x53 | ||
66 | #define R_PARTIAL_DISPLAY_MODE 0x55 | ||
67 | #define R_PARTIAL_START_LINE 0x56 | ||
68 | #define R_PARTIAL_END_LINE 0x57 | ||
69 | #define R_AREA_SCROLL_MODE 0x59 | ||
70 | #define R_SCROLL_START_LINE 0x5a | ||
71 | #define R_DATA_FORMAT_SELECT 0x60 | ||
72 | |||
73 | #if defined(SANSA_C200) | ||
74 | /* wait for LCD */ | ||
75 | static inline void lcd_wait_write(void) | ||
76 | { | ||
77 | while (LCD1_CONTROL & LCD1_BUSY_MASK); | ||
78 | } | ||
79 | |||
80 | /* send LCD data */ | ||
81 | static void lcd_send_data(const fb_data *data, int width) | ||
82 | { | ||
83 | while(width--) | ||
84 | { | ||
85 | lcd_wait_write(); | ||
86 | LCD1_DATA = *data >> 8; | ||
87 | lcd_wait_write(); | ||
88 | LCD1_DATA = *data++ & 0xff; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | /* send LCD command */ | ||
93 | static void lcd_send_command(unsigned char cmd, unsigned char arg) | ||
94 | { | ||
95 | lcd_wait_write(); | ||
96 | LCD1_CMD = cmd; | ||
97 | /* if the argument is 0, we send a NOP (= 0) command */ | ||
98 | lcd_wait_write(); | ||
99 | LCD1_CMD = arg; | ||
100 | } | ||
101 | |||
102 | static inline void c200v1_lcd_init(void) | ||
103 | { | ||
104 | /* This is from the c200 of bootloader beginning at offset 0xbbf4 */ | ||
105 | outl(inl(0x70000010) & ~0xfc000000, 0x70000010); | ||
106 | outl(inl(0x70000010), 0x70000010); | ||
107 | |||
108 | DEV_INIT2 &= ~0x400; | ||
109 | udelay(10000); | ||
110 | |||
111 | LCD1_CONTROL &= ~0x4; | ||
112 | udelay(15); | ||
113 | |||
114 | LCD1_CONTROL |= 0x4; | ||
115 | udelay(10); | ||
116 | |||
117 | LCD1_CONTROL = 0x0084; /* bits (9,10) = 00 -> fastest setting */ | ||
118 | udelay(10000); | ||
119 | } | ||
120 | |||
121 | #define lcd_delay(delay) udelay((delay) * 1000) | ||
122 | |||
123 | #elif defined(SANSA_C200V2) | ||
124 | |||
125 | static inline void lcd_delay(int delay) | ||
126 | { //TUNEME : delay is in milliseconds | ||
127 | delay <<= 14; | ||
128 | while(delay--) ; | ||
129 | } | ||
130 | |||
131 | /* send LCD data */ | ||
132 | static void lcd_send_data(const fb_data *data, int width) | ||
133 | { | ||
134 | while(width--) | ||
135 | { | ||
136 | DBOP_DOUT = *data << 8 | *data >> 8; | ||
137 | data++; | ||
138 | while ((DBOP_STAT & (1<<10)) == 0); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | /* send LCD command */ | ||
143 | static void lcd_send_command(unsigned char cmd, unsigned char val) | ||
144 | { | ||
145 | DBOP_TIMPOL_23 = 0xa167006e; | ||
146 | |||
147 | DBOP_DOUT = cmd | val << 8; | ||
148 | |||
149 | while ((DBOP_STAT & (1<<10)) == 0); | ||
150 | |||
151 | DBOP_TIMPOL_23 = 0xa167e06f; | ||
152 | } | ||
153 | |||
154 | static inline void as3525_dbop_init(void) | ||
155 | { | ||
156 | CGU_DBOP = (1<<3) | AS3525_DBOP_DIV; | ||
157 | |||
158 | DBOP_TIMPOL_01 = 0xe167e167; | ||
159 | DBOP_TIMPOL_23 = 0xe167006e; | ||
160 | DBOP_CTRL = 0x40008; | ||
161 | |||
162 | GPIOB_AFSEL = 0xc; | ||
163 | GPIOC_AFSEL = 0xff; | ||
164 | |||
165 | DBOP_TIMPOL_23 = 0x6006e; | ||
166 | DBOP_CTRL = 0x52008; | ||
167 | DBOP_TIMPOL_01 = 0x6e167; | ||
168 | DBOP_TIMPOL_23 = 0xa167e06f; | ||
169 | |||
170 | lcd_delay(20); | ||
171 | } | ||
172 | |||
173 | /* we need to set the DBOP_DOUT pins high, for correct dbop reads */ | ||
174 | bool lcd_button_support(void) | ||
175 | { | ||
176 | const fb_data data = 0xffff; | ||
177 | |||
178 | if (lcd_busy) /* we can't use dbop for reading if we are in the */ | ||
179 | return false; /* middle of a write operation */ | ||
180 | |||
181 | /* use out of screen coordinates */ | ||
182 | lcd_send_command(R_X_ADDR_AREA, 0); | ||
183 | lcd_send_command(1, 0); | ||
184 | lcd_send_command(R_Y_ADDR_AREA, 0); | ||
185 | lcd_send_command(1, 0); | ||
186 | |||
187 | lcd_send_data(&data, 1); | ||
188 | |||
189 | return true; | ||
190 | } | ||
191 | #endif | ||
192 | |||
193 | /* LCD init */ | ||
194 | void lcd_init_device(void) | ||
195 | { | ||
196 | #if defined(SANSA_C200) | ||
197 | c200v1_lcd_init(); | ||
198 | #elif defined(SANSA_C200V2) | ||
199 | as3525_dbop_init(); | ||
200 | #endif | ||
201 | |||
202 | lcd_send_command(R_STANDBY_OFF, 0); | ||
203 | lcd_delay(20); | ||
204 | |||
205 | lcd_send_command(R_OSCILLATION_MODE, 0x01); | ||
206 | lcd_delay(20); | ||
207 | |||
208 | lcd_send_command(R_DCDC_AMP_ONOFF, 0x01); | ||
209 | lcd_delay(20); | ||
210 | |||
211 | lcd_send_command(R_DCDC_AMP_ONOFF, 0x09); | ||
212 | lcd_delay(20); | ||
213 | |||
214 | lcd_send_command(R_DCDC_AMP_ONOFF, 0x0b); | ||
215 | lcd_delay(20); | ||
216 | |||
217 | lcd_send_command(R_DCDC_AMP_ONOFF, 0x0f); | ||
218 | lcd_delay(20); | ||
219 | |||
220 | lcd_send_command(R_DRIVER_OUTPUT_MODE, 0x07); | ||
221 | |||
222 | lcd_send_command(R_DCDC_SET, 0x03); | ||
223 | |||
224 | lcd_send_command(R_DCDC_CLOCK_DIV, 0x03); | ||
225 | |||
226 | lcd_send_command(R_TEMP_COMPENSATION, 0x01); | ||
227 | |||
228 | lcd_send_command(R_CONTRAST_CONTROL1, 0x55); | ||
229 | |||
230 | lcd_send_command(R_ADDRESSING_MODE, 0x10); | ||
231 | |||
232 | lcd_send_command(R_ROW_VECTOR_MODE, 0x0e); | ||
233 | |||
234 | lcd_send_command(R_N_LINE_INVERSION, 0x0d); | ||
235 | |||
236 | lcd_send_command(R_FRAME_FREQ_CONTROL, 0); | ||
237 | |||
238 | lcd_send_command(R_ENTRY_MODE, 0x82); | ||
239 | |||
240 | /* vertical dimensions */ | ||
241 | lcd_send_command(R_Y_ADDR_AREA, 0x1a); /* y1 + 0x1a */ | ||
242 | lcd_send_command(LCD_HEIGHT - 1 + 0x1a, 0); /* y2 + 0x1a */ | ||
243 | |||
244 | /* horizontal dimensions */ | ||
245 | lcd_send_command(R_X_ADDR_AREA, 0); /* x1 */ | ||
246 | lcd_send_command(LCD_WIDTH - 1, 0); /* x2 */ | ||
247 | |||
248 | lcd_delay(100); | ||
249 | |||
250 | lcd_send_command(R_DISPLAY_ON, 0); | ||
251 | } | ||
252 | |||
253 | /*** hardware configuration ***/ | ||
254 | int lcd_default_contrast(void) | ||
255 | { | ||
256 | return DEFAULT_CONTRAST_SETTING; | ||
257 | } | ||
258 | |||
259 | void lcd_set_contrast(int val) | ||
260 | { | ||
261 | #ifdef SANSA_C200V2 | ||
262 | lcd_busy = true; | ||
263 | #endif | ||
264 | lcd_send_command(R_CONTRAST_CONTROL1, val); | ||
265 | #ifdef SANSA_C200V2 | ||
266 | lcd_busy = false; | ||
267 | #endif | ||
268 | } | ||
269 | |||
270 | void lcd_set_invert_display(bool yesno) | ||
271 | { | ||
272 | /* TODO: Implement lcd_set_invert_display() */ | ||
273 | (void)yesno; | ||
274 | } | ||
275 | |||
276 | #if defined(HAVE_LCD_ENABLE) | ||
277 | void lcd_enable(bool yesno) | ||
278 | { | ||
279 | if (yesno == is_lcd_enabled) | ||
280 | return; | ||
281 | |||
282 | #ifdef SANSA_C200V2 | ||
283 | lcd_busy = true; | ||
284 | #endif | ||
285 | if ((is_lcd_enabled = yesno)) | ||
286 | { | ||
287 | lcd_send_command(R_STANDBY_OFF, 0); | ||
288 | lcd_send_command(R_DISPLAY_ON, 0); | ||
289 | lcd_activation_call_hook(); | ||
290 | } | ||
291 | else | ||
292 | { | ||
293 | lcd_send_command(R_STANDBY_ON, 0); | ||
294 | } | ||
295 | #ifdef SANSA_C200V2 | ||
296 | lcd_busy = false; | ||
297 | #endif | ||
298 | } | ||
299 | #endif | ||
300 | |||
301 | #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP) | ||
302 | bool lcd_active(void) | ||
303 | { | ||
304 | return is_lcd_enabled; | ||
305 | } | ||
306 | #endif | ||
307 | |||
308 | |||
309 | /* turn the display upside down (call lcd_update() afterwards) */ | ||
310 | void lcd_set_flip(bool yesno) | ||
311 | { | ||
312 | #ifdef SANSA_C200V2 | ||
313 | lcd_busy = true; | ||
314 | #endif | ||
315 | lcd_send_command(R_DRIVER_OUTPUT_MODE, yesno ? 0x02 : 0x07); | ||
316 | #ifdef SANSA_C200V2 | ||
317 | lcd_busy = false; | ||
318 | #endif | ||
319 | } | ||
320 | |||
321 | /*** update functions ***/ | ||
322 | |||
323 | void lcd_yuv_set_options(unsigned options) | ||
324 | { | ||
325 | lcd_yuv_options = options; | ||
326 | } | ||
327 | |||
328 | /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */ | ||
329 | extern void lcd_write_yuv420_lines(unsigned char const * const src[3], | ||
330 | int width, | ||
331 | int stride); | ||
332 | extern void lcd_write_yuv420_lines_odither(unsigned char const * const src[3], | ||
333 | int width, | ||
334 | int stride, | ||
335 | int x_screen, /* To align dither pattern */ | ||
336 | int y_screen); | ||
337 | /* Performance function to blit a YUV bitmap directly to the LCD */ | ||
338 | void lcd_blit_yuv(unsigned char * const src[3], | ||
339 | int src_x, int src_y, int stride, | ||
340 | int x, int y, int width, int height) | ||
341 | { | ||
342 | unsigned char const * yuv_src[3]; | ||
343 | off_t z; | ||
344 | |||
345 | #ifdef SANSA_C200V2 | ||
346 | lcd_busy = true; | ||
347 | #endif | ||
348 | |||
349 | /* Sorry, but width and height must be >= 2 or else */ | ||
350 | width &= ~1; | ||
351 | height >>= 1; | ||
352 | |||
353 | y += 0x1a; | ||
354 | |||
355 | z = stride*src_y; | ||
356 | yuv_src[0] = src[0] + z + src_x; | ||
357 | yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1); | ||
358 | yuv_src[2] = src[2] + (yuv_src[1] - src[1]); | ||
359 | |||
360 | lcd_send_command(R_ENTRY_MODE, 0x80); | ||
361 | |||
362 | lcd_send_command(R_X_ADDR_AREA, x); | ||
363 | lcd_send_command(x + width - 1, 0); | ||
364 | |||
365 | if (lcd_yuv_options & LCD_YUV_DITHER) | ||
366 | { | ||
367 | do | ||
368 | { | ||
369 | lcd_send_command(R_Y_ADDR_AREA, y); | ||
370 | lcd_send_command(y + 1, 0); | ||
371 | |||
372 | #ifndef SANSA_C200V2 // TODO | ||
373 | lcd_write_yuv420_lines_odither(yuv_src, width, stride, x, y); | ||
374 | #endif | ||
375 | yuv_src[0] += stride << 1; /* Skip down two luma lines */ | ||
376 | yuv_src[1] += stride >> 1; /* Skip down one chroma line */ | ||
377 | yuv_src[2] += stride >> 1; | ||
378 | y += 2; | ||
379 | } | ||
380 | while (--height > 0); | ||
381 | } | ||
382 | else | ||
383 | { | ||
384 | do | ||
385 | { | ||
386 | lcd_send_command(R_Y_ADDR_AREA, y); | ||
387 | lcd_send_command(y + 1, 0); | ||
388 | |||
389 | #ifndef SANSA_C200V2 // TODO | ||
390 | lcd_write_yuv420_lines(yuv_src, width, stride); | ||
391 | #endif | ||
392 | yuv_src[0] += stride << 1; /* Skip down two luma lines */ | ||
393 | yuv_src[1] += stride >> 1; /* Skip down one chroma line */ | ||
394 | yuv_src[2] += stride >> 1; | ||
395 | y += 2; | ||
396 | } | ||
397 | while (--height > 0); | ||
398 | } | ||
399 | |||
400 | #ifdef SANSA_C200V2 | ||
401 | lcd_busy = false; | ||
402 | #endif | ||
403 | } | ||
404 | |||
405 | /* Update the display. | ||
406 | This must be called after all other LCD functions that change the display. */ | ||
407 | void lcd_update(void) | ||
408 | { | ||
409 | lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); | ||
410 | } | ||
411 | |||
412 | /* Update a fraction of the display. */ | ||
413 | void lcd_update_rect(int x, int y, int width, int height) | ||
414 | { | ||
415 | const fb_data *addr; | ||
416 | |||
417 | if (x + width >= LCD_WIDTH) | ||
418 | width = LCD_WIDTH - x; | ||
419 | if (y + height >= LCD_HEIGHT) | ||
420 | height = LCD_HEIGHT - y; | ||
421 | |||
422 | if ((width <= 0) || (height <= 0)) | ||
423 | return; /* Nothing left to do. */ | ||
424 | |||
425 | addr = &lcd_framebuffer[y][x]; | ||
426 | |||
427 | #ifdef SANSA_C200V2 | ||
428 | lcd_busy = true; | ||
429 | #endif | ||
430 | |||
431 | if (width <= 1) { | ||
432 | /* The X end address must be larger than the X start address, so we | ||
433 | * switch to vertical mode for single column updates and set the | ||
434 | * window width to 2 */ | ||
435 | lcd_send_command(R_ENTRY_MODE, 0x80); | ||
436 | lcd_send_command(R_X_ADDR_AREA, x); | ||
437 | lcd_send_command(x + 1, 0); | ||
438 | } else { | ||
439 | lcd_send_command(R_ENTRY_MODE, 0x82); | ||
440 | lcd_send_command(R_X_ADDR_AREA, x); | ||
441 | lcd_send_command(x + width - 1, 0); | ||
442 | } | ||
443 | |||
444 | lcd_send_command(R_Y_ADDR_AREA, y + 0x1a); | ||
445 | lcd_send_command(y + height - 1 + 0x1a, 0); | ||
446 | |||
447 | do { | ||
448 | lcd_send_data(addr, width); | ||
449 | addr += LCD_WIDTH; | ||
450 | } while (--height > 0); | ||
451 | |||
452 | #ifdef SANSA_C200V2 | ||
453 | lcd_busy = false; | ||
454 | #endif | ||
455 | } | ||