Fuzev2 LCD: replace delays by calls to lcd_delay() (delays not changed)
[kugel-rb.git] / firmware / target / arm / as3525 / sansa-fuzev2 / lcd-fuzev2.c
blob53d8f73904ed5120964c724f39fe15a21fe9957c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2008 by Dave Chapman
11 * Copyright (C) 2010 by Thomas Martitz
13 * LCD driver for the Sansa Fuze - controller unknown
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
24 #include "config.h"
26 #include "cpu.h"
27 #include "lcd.h"
28 #include "file.h"
29 #include "debug.h"
30 #include "system.h"
31 #include "clock-target.h"
33 /* The controller is unknown, but some registers appear to be the same as the
34 HD66789R */
35 static bool display_on = false; /* is the display turned on? */
37 /* register defines */
38 #define R_START_OSC 0x00
39 #define R_DRV_OUTPUT_CONTROL 0x01
40 #define R_DRV_WAVEFORM_CONTROL 0x02
41 #define R_ENTRY_MODE 0x03
42 #define R_COMPARE_REG1 0x04
43 #define R_COMPARE_REG2 0x05
45 #define R_DISP_CONTROL1 0x07
46 #define R_DISP_CONTROL2 0x08
47 #define R_DISP_CONTROL3 0x09
49 #define R_FRAME_CYCLE_CONTROL 0x0b
50 #define R_EXT_DISP_IF_CONTROL 0x0c
52 #define R_POWER_CONTROL1 0x10
53 #define R_POWER_CONTROL2 0x11
54 #define R_POWER_CONTROL3 0x12
55 #define R_POWER_CONTROL4 0x13
57 #define R_RAM_ADDR_SET 0x21
58 #define R_WRITE_DATA_2_GRAM 0x22
60 #define R_GAMMA_FINE_ADJ_POS1 0x30
61 #define R_GAMMA_FINE_ADJ_POS2 0x31
62 #define R_GAMMA_FINE_ADJ_POS3 0x32
63 #define R_GAMMA_GRAD_ADJ_POS 0x33
65 #define R_GAMMA_FINE_ADJ_NEG1 0x34
66 #define R_GAMMA_FINE_ADJ_NEG2 0x35
67 #define R_GAMMA_FINE_ADJ_NEG3 0x36
68 #define R_GAMMA_GRAD_ADJ_NEG 0x37
70 #define R_GAMMA_AMP_ADJ_RES_POS 0x38
71 #define R_GAMMA_AMP_AVG_ADJ_RES_NEG 0x39
73 #define R_GATE_SCAN_POS 0x40
74 #define R_VERT_SCROLL_CONTROL 0x41
75 #define R_1ST_SCR_DRV_POS 0x42
76 #define R_2ND_SCR_DRV_POS 0x43
77 #define R_HORIZ_RAM_ADDR_POS 0x44
78 #define R_VERT_RAM_ADDR_POS 0x45
80 /* Flip Flag */
81 #define R_ENTRY_MODE_HORZ_NORMAL 0x7030
82 #define R_ENTRY_MODE_HORZ_FLIPPED 0x7000
83 static unsigned short r_entry_mode = R_ENTRY_MODE_HORZ_NORMAL;
84 #define R_ENTRY_MODE_VERT 0x7038
85 #define R_ENTRY_MODE_SOLID_VERT 0x1038
86 #define R_ENTRY_MODE_VIDEO_NORMAL 0x7038
87 #define R_ENTRY_MODE_VIDEO_FLIPPED 0x7018
89 /* Reverse Flag */
90 #define R_DISP_CONTROL_NORMAL 0x0004
91 #define R_DISP_CONTROL_REV 0x0000
92 static unsigned short r_disp_control_rev = R_DISP_CONTROL_NORMAL;
94 static const int xoffset = 20;
96 static inline void lcd_delay(int x)
98 do {
99 asm volatile ("nop\n");
100 } while (x--);
103 static void as3525_dbop_init(void)
105 CCU_IO |= 1<<12;
106 CGU_DBOP |= (1<<4) | (1<<3) | AS3525_DBOP_DIV;
107 DBOP_TIMPOL_01 = 0xE12FE12F;
108 DBOP_TIMPOL_23 = 0xE12F0036;
109 DBOP_CTRL = 0x41004;
110 DBOP_TIMPOL_23 = 0x60036;
111 DBOP_CTRL = 0x51004;
112 DBOP_TIMPOL_01 = 0x60036;
113 DBOP_TIMPOL_23 = 0xA12FE037;
116 static inline void dbop_set_mode(int mode)
118 unsigned long ctrl = DBOP_CTRL;
119 int words = (ctrl >> 13) & 3; // bits 14:13
120 if (mode == 32 && words != 2)
121 DBOP_CTRL = (ctrl & ~(1<<13)) | (1<<14); // 4 serial words
122 else if (mode == 16 && words != 1)
123 DBOP_CTRL = (ctrl & ~(1<<14)) | (1<<13); // 2 serial words
124 else
125 return;
126 lcd_delay(10);
129 static void dbop_write_data(const int16_t* p_bytes, int count)
131 const int32_t *data;
132 if ((intptr_t)p_bytes & 0x3 || count == 1)
133 { /* need to do a single 16bit write beforehand if the address is
134 * not word aligned or count is 1, switch to 16bit mode if needed */
135 dbop_set_mode(16);
136 DBOP_DOUT16 = swap16(*p_bytes++);
137 if (!(--count))
138 return;
140 /* from here, 32bit transfers are save
141 * set it to transfer 4*(outputwidth) units at a time,
142 * if bit 12 is set it only does 2 halfwords though (we never set it)
143 * switch to 32bit output if needed */
144 dbop_set_mode(32);
145 data = (int32_t*)p_bytes;
146 while (count > 1)
148 int pixels = *data++;
149 pixels = (swap16(pixels >> 16) << 16) | (swap16(pixels & 0xffff));
150 DBOP_DOUT32 = pixels;
151 count -= 2;
153 /* Wait if push fifo is full */
154 while ((DBOP_STAT & (1<<6)) != 0);
156 /* While push fifo is not empty */
157 while ((DBOP_STAT & (1<<10)) == 0);
159 /* due to the 32bit alignment requirement or uneven count,
160 * we possibly need to do a 16bit transfer at the end also */
161 if (count > 0)
162 dbop_write_data((int16_t*)data, 1);
165 static void lcd_write_cmd(unsigned short cmd)
167 volatile int i;
168 lcd_delay(0x20);
170 DBOP_CTRL |= 1<<13;
171 DBOP_CTRL &= ~(1<<14); // 2 serial words
172 DBOP_CTRL &= ~(1<<12); // 8 bit data width
173 DBOP_TIMPOL_23 = 0xA12F0036;
174 DBOP_DOUT = swap16(cmd);
176 while ((DBOP_STAT & (1<<10)) == 0);
177 for(i=0;i<0x20;i++) asm volatile ("nop\n");
178 DBOP_TIMPOL_23 = 0xA12FE037;
181 static void lcd_write_reg(int reg, int value)
183 int16_t data = value;
184 lcd_write_cmd(reg);
185 dbop_write_data(&data, 1);
188 /*** hardware configuration ***/
190 void lcd_set_contrast(int val)
192 (void)val;
195 void lcd_set_invert_display(bool yesno)
197 r_disp_control_rev = yesno ? R_DISP_CONTROL_REV :
198 R_DISP_CONTROL_NORMAL;
200 if (display_on)
202 lcd_write_reg(R_DISP_CONTROL1, 0x0013 | r_disp_control_rev);
207 #ifdef HAVE_LCD_FLIP
208 static bool display_flipped = false;
210 /* turn the display upside down */
211 void lcd_set_flip(bool yesno)
213 display_flipped = yesno;
215 r_entry_mode = yesno ? R_ENTRY_MODE_HORZ_FLIPPED :
216 R_ENTRY_MODE_HORZ_NORMAL;
218 #endif
220 static void _display_on(void)
222 /* Initialise in the same way as the original firmare */
224 lcd_write_reg(R_DISP_CONTROL1, 0);
225 lcd_write_reg(R_POWER_CONTROL4, 0);
227 lcd_write_reg(R_POWER_CONTROL2, 0x3704);
228 lcd_write_reg(0x14, 0x1a1b);
229 lcd_write_reg(R_POWER_CONTROL1, 0x3860);
230 lcd_write_reg(R_POWER_CONTROL4, 0x40);
232 lcd_write_reg(R_POWER_CONTROL4, 0x60);
234 lcd_write_reg(R_POWER_CONTROL4, 0x70);
235 lcd_write_reg(R_DRV_OUTPUT_CONTROL, 277);
236 lcd_write_reg(R_DRV_WAVEFORM_CONTROL, (7<<8));
237 lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
238 lcd_write_reg(R_DISP_CONTROL2, 0x01);
239 lcd_write_reg(R_FRAME_CYCLE_CONTROL, (1<<10));
240 lcd_write_reg(R_EXT_DISP_IF_CONTROL, 0);
242 lcd_write_reg(R_GAMMA_FINE_ADJ_POS1, 0x40);
243 lcd_write_reg(R_GAMMA_FINE_ADJ_POS2, 0x0687);
244 lcd_write_reg(R_GAMMA_FINE_ADJ_POS3, 0x0306);
245 lcd_write_reg(R_GAMMA_GRAD_ADJ_POS, 0x104);
246 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG1, 0x0585);
247 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG2, 255+66);
248 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG3, 0x0687+128);
249 lcd_write_reg(R_GAMMA_GRAD_ADJ_NEG, 259);
250 lcd_write_reg(R_GAMMA_AMP_ADJ_RES_POS, 0);
251 lcd_write_reg(R_GAMMA_AMP_AVG_ADJ_RES_NEG, 0);
253 lcd_write_reg(R_1ST_SCR_DRV_POS, (LCD_WIDTH - 1));
254 lcd_write_reg(R_2ND_SCR_DRV_POS, 0);
255 lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (LCD_WIDTH - 1));
256 lcd_write_reg(R_VERT_RAM_ADDR_POS, 0);
257 lcd_write_reg(0x46, (((LCD_WIDTH - 1) + xoffset) << 8) | xoffset);
258 lcd_write_reg(0x47, (LCD_HEIGHT - 1));
259 lcd_write_reg(0x48, 0x0);
261 lcd_write_reg(R_DISP_CONTROL1, 0x11);
262 lcd_write_reg(R_DISP_CONTROL1, 0x13 | r_disp_control_rev);
264 display_on = true; /* must be done before calling lcd_update() */
265 lcd_update();
268 void lcd_init_device(void)
270 as3525_dbop_init();
272 GPIOA_DIR |= (0x20|0x1);
273 GPIOA_DIR &= ~(1<<3);
274 GPIOA_PIN(3) = 0;
275 GPIOA_PIN(0) = 1;
276 GPIOA_PIN(4) = 0;
278 GPIOB_DIR |= 0xf;
279 GPIOB_PIN(0) = 1<<0;
280 GPIOB_PIN(1) = 1<<1;
281 GPIOB_PIN(2) = 1<<2;
282 GPIOB_PIN(3) = 1<<3;
284 GPIOA_PIN(4) = 1<<4;
285 GPIOA_PIN(5) = 1<<5;
287 _display_on();
290 #if defined(HAVE_LCD_ENABLE)
291 void lcd_enable(bool on)
293 if (display_on == on)
294 return;
296 if(on)
298 lcd_write_reg(R_START_OSC, 1);
299 lcd_write_reg(R_POWER_CONTROL1, 0);
300 lcd_write_reg(R_POWER_CONTROL2, 0x3704);
301 lcd_write_reg(0x14, 0x1a1b);
302 lcd_write_reg(R_POWER_CONTROL1, 0x3860);
303 lcd_write_reg(R_POWER_CONTROL4, 0x40);
304 lcd_write_reg(R_POWER_CONTROL4, 0x60);
305 lcd_write_reg(R_POWER_CONTROL4, 112);
306 lcd_write_reg(R_DISP_CONTROL1, 0x11);
307 lcd_write_reg(R_DISP_CONTROL1, 0x13 | r_disp_control_rev);
308 display_on = true;
309 lcd_update(); /* Resync display */
310 send_event(LCD_EVENT_ACTIVATION, NULL);
311 sleep(0);
314 else
316 lcd_write_reg(R_DISP_CONTROL1, 0x22);
317 lcd_write_reg(R_DISP_CONTROL1, 0);
318 lcd_write_reg(R_POWER_CONTROL1, 1);
319 display_on = false;
322 #endif
324 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
325 bool lcd_active(void)
327 return display_on;
329 #endif
331 /*** update functions ***/
333 /* FIXME : find the datasheet for this RENESAS controller so we identify the
334 * registers used in windowing code (not present in HD66789R) */
336 /* Set horizontal window addresses */
337 static void lcd_window_x(int xmin, int xmax)
339 xmin += xoffset;
340 xmax += xoffset;
341 lcd_write_reg(R_HORIZ_RAM_ADDR_POS + 2, (xmax << 8) | xmin);
342 lcd_write_reg(R_RAM_ADDR_SET - 1, xmin);
345 /* Set vertical window addresses */
346 static void lcd_window_y(int ymin, int ymax)
348 lcd_write_reg(R_VERT_RAM_ADDR_POS + 2, ymax);
349 lcd_write_reg(R_VERT_RAM_ADDR_POS + 3, ymin);
350 lcd_write_reg(R_RAM_ADDR_SET, ymin);
353 static unsigned lcd_yuv_options = 0;
355 void lcd_yuv_set_options(unsigned options)
357 lcd_yuv_options = options;
360 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
361 extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
362 int width,
363 int stride);
364 extern void lcd_write_yuv420_lines_odither(unsigned char const * const src[3],
365 int width,
366 int stride,
367 int x_screen, /* To align dither pattern */
368 int y_screen);
370 /* Performance function to blit a YUV bitmap directly to the LCD
371 * src_x, src_y, width and height should be even
372 * x, y, width and height have to be within LCD bounds
374 void lcd_blit_yuv(unsigned char * const src[3],
375 int src_x, int src_y, int stride,
376 int x, int y, int width, int height)
378 unsigned char const * yuv_src[3];
379 off_t z;
381 /* Sorry, but width and height must be >= 2 or else */
382 width &= ~1;
383 height >>= 1;
385 z = stride*src_y;
386 yuv_src[0] = src[0] + z + src_x;
387 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
388 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
390 #ifdef HAVE_LCD_FLIP
391 lcd_write_reg(R_ENTRY_MODE,
392 display_flipped ? R_ENTRY_MODE_VIDEO_FLIPPED : R_ENTRY_MODE_VIDEO_NORMAL
394 #else
395 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_VIDEO_NORMAL);
396 #endif
398 lcd_window_x(x, x + width - 1);
400 if (lcd_yuv_options & LCD_YUV_DITHER)
404 lcd_window_y(y, y + 1);
406 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
408 lcd_write_yuv420_lines_odither(yuv_src, width, stride, x, y);
409 yuv_src[0] += stride << 1; /* Skip down two luma lines */
410 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
411 yuv_src[2] += stride >> 1;
412 y += 2;
414 while (--height > 0);
416 else
420 lcd_window_y(y, y + 1);
422 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
424 lcd_write_yuv420_lines(yuv_src, width, stride);
425 yuv_src[0] += stride << 1; /* Skip down two luma lines */
426 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
427 yuv_src[2] += stride >> 1;
428 y += 2;
430 while (--height > 0);
434 /* Update the display.
435 This must be called after all other LCD functions that change the display. */
436 void lcd_update(void)
438 if (!display_on)
439 return;
441 lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
443 lcd_window_x(0, LCD_WIDTH - 1);
444 lcd_window_y(0, LCD_HEIGHT - 1);
446 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
448 lcd_update_rect(0,0, LCD_WIDTH, LCD_HEIGHT);
451 /* Update a fraction of the display. */
452 void lcd_update_rect(int x, int y, int width, int height)
454 const fb_data *ptr;
456 if (!display_on)
457 return;
459 /* nothing to draw? */
460 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) ||
461 (y >= LCD_HEIGHT) || (x + width <= 0) || (y + height <= 0))
462 return;
464 if (x < 0)
465 { /* clip left */
466 width += x;
467 x = 0;
469 if (y < 0)
470 { /* clip top */
471 height += y;
472 y = 0;
474 if (x + width > LCD_WIDTH)
475 width = LCD_WIDTH - x; /* clip right */
476 if (y + height > LCD_HEIGHT)
477 height = LCD_HEIGHT - y; /* clip bottom */
479 lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
481 /* we need to make x and width even to enable 32bit transfers */
482 width = (width + (x & 1) + 1) & ~1;
483 x &= ~1;
485 lcd_window_x(x, x + width - 1);
486 lcd_window_y(y, y + height -1);
488 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
490 ptr = &lcd_framebuffer[y][x];
494 dbop_write_data(ptr, width);
495 ptr += LCD_WIDTH;
497 while (--height > 0);