Fuzev2: write pixel swapping in assembly for a some speed up
[kugel-rb.git] / firmware / target / arm / as3525 / sansa-fuzev2 / lcd-fuzev2.c
blob06a1b7f4dd64f9dc2872cc9e2b50a1062278a59e
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;
147 const unsigned int mask = 0xff00ff;
148 while (count > 1)
150 register unsigned int pixels = *data++;
151 register unsigned int tmp;
153 /* pixels == ABCD */
154 asm(
155 "and %[tmp], %[pixels], %[mask] \n" /* tmp = 0B0D */
156 "and %[pixels], %[pixels], %[mask], lsl #8\n" /* %[pixels] = A0C0 */
157 "mov %[pixels], %[pixels], lsr #8 \n" /* %[pixels] = 0A0C */
158 "orr %[pixels], %[pixels], %[tmp], lsl #8 \n" /* %[pixels] = BADC */
159 : [pixels]"+r"(pixels), [tmp]"=r"(tmp) /* output */
160 : [mask]"r"(mask) /* input */
163 DBOP_DOUT32 = pixels;
164 count -= 2;
166 /* Wait if push fifo is full */
167 while ((DBOP_STAT & (1<<6)) != 0);
169 /* While push fifo is not empty */
170 while ((DBOP_STAT & (1<<10)) == 0);
172 /* due to the 32bit alignment requirement or uneven count,
173 * we possibly need to do a 16bit transfer at the end also */
174 if (count > 0)
175 dbop_write_data((int16_t*)data, 1);
178 static void lcd_write_cmd(unsigned short cmd)
180 volatile int i;
181 lcd_delay(0x20);
183 DBOP_CTRL |= 1<<13;
184 DBOP_CTRL &= ~(1<<14); // 2 serial words
185 DBOP_CTRL &= ~(1<<12); // 8 bit data width
186 DBOP_TIMPOL_23 = 0xA12F0036;
187 DBOP_DOUT = swap16(cmd);
189 while ((DBOP_STAT & (1<<10)) == 0);
190 for(i=0;i<0x20;i++) asm volatile ("nop\n");
191 DBOP_TIMPOL_23 = 0xA12FE037;
194 static void lcd_write_reg(int reg, int value)
196 int16_t data = value;
197 lcd_write_cmd(reg);
198 dbop_write_data(&data, 1);
201 /*** hardware configuration ***/
203 void lcd_set_contrast(int val)
205 (void)val;
208 void lcd_set_invert_display(bool yesno)
210 r_disp_control_rev = yesno ? R_DISP_CONTROL_REV :
211 R_DISP_CONTROL_NORMAL;
213 if (display_on)
215 lcd_write_reg(R_DISP_CONTROL1, 0x0013 | r_disp_control_rev);
220 #ifdef HAVE_LCD_FLIP
221 static bool display_flipped = false;
223 /* turn the display upside down */
224 void lcd_set_flip(bool yesno)
226 display_flipped = yesno;
228 r_entry_mode = yesno ? R_ENTRY_MODE_HORZ_FLIPPED :
229 R_ENTRY_MODE_HORZ_NORMAL;
231 #endif
233 static void _display_on(void)
235 /* Initialise in the same way as the original firmare */
237 lcd_write_reg(R_DISP_CONTROL1, 0);
238 lcd_write_reg(R_POWER_CONTROL4, 0);
240 lcd_write_reg(R_POWER_CONTROL2, 0x3704);
241 lcd_write_reg(0x14, 0x1a1b);
242 lcd_write_reg(R_POWER_CONTROL1, 0x3860);
243 lcd_write_reg(R_POWER_CONTROL4, 0x40);
245 lcd_write_reg(R_POWER_CONTROL4, 0x60);
247 lcd_write_reg(R_POWER_CONTROL4, 0x70);
248 lcd_write_reg(R_DRV_OUTPUT_CONTROL, 277);
249 lcd_write_reg(R_DRV_WAVEFORM_CONTROL, (7<<8));
250 lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
251 lcd_write_reg(R_DISP_CONTROL2, 0x01);
252 lcd_write_reg(R_FRAME_CYCLE_CONTROL, (1<<10));
253 lcd_write_reg(R_EXT_DISP_IF_CONTROL, 0);
255 lcd_write_reg(R_GAMMA_FINE_ADJ_POS1, 0x40);
256 lcd_write_reg(R_GAMMA_FINE_ADJ_POS2, 0x0687);
257 lcd_write_reg(R_GAMMA_FINE_ADJ_POS3, 0x0306);
258 lcd_write_reg(R_GAMMA_GRAD_ADJ_POS, 0x104);
259 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG1, 0x0585);
260 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG2, 255+66);
261 lcd_write_reg(R_GAMMA_FINE_ADJ_NEG3, 0x0687+128);
262 lcd_write_reg(R_GAMMA_GRAD_ADJ_NEG, 259);
263 lcd_write_reg(R_GAMMA_AMP_ADJ_RES_POS, 0);
264 lcd_write_reg(R_GAMMA_AMP_AVG_ADJ_RES_NEG, 0);
266 lcd_write_reg(R_1ST_SCR_DRV_POS, (LCD_WIDTH - 1));
267 lcd_write_reg(R_2ND_SCR_DRV_POS, 0);
268 lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (LCD_WIDTH - 1));
269 lcd_write_reg(R_VERT_RAM_ADDR_POS, 0);
270 lcd_write_reg(0x46, (((LCD_WIDTH - 1) + xoffset) << 8) | xoffset);
271 lcd_write_reg(0x47, (LCD_HEIGHT - 1));
272 lcd_write_reg(0x48, 0x0);
274 lcd_write_reg(R_DISP_CONTROL1, 0x11);
275 lcd_write_reg(R_DISP_CONTROL1, 0x13 | r_disp_control_rev);
277 display_on = true; /* must be done before calling lcd_update() */
278 lcd_update();
281 void lcd_init_device(void)
283 as3525_dbop_init();
285 GPIOA_DIR |= (0x20|0x1);
286 GPIOA_DIR &= ~(1<<3);
287 GPIOA_PIN(3) = 0;
288 GPIOA_PIN(0) = 1;
289 GPIOA_PIN(4) = 0;
291 GPIOB_DIR |= 0xf;
292 GPIOB_PIN(0) = 1<<0;
293 GPIOB_PIN(1) = 1<<1;
294 GPIOB_PIN(2) = 1<<2;
295 GPIOB_PIN(3) = 1<<3;
297 GPIOA_PIN(4) = 1<<4;
298 GPIOA_PIN(5) = 1<<5;
300 _display_on();
303 #if defined(HAVE_LCD_ENABLE)
304 void lcd_enable(bool on)
306 if (display_on == on)
307 return;
309 if(on)
311 lcd_write_reg(R_START_OSC, 1);
312 lcd_write_reg(R_POWER_CONTROL1, 0);
313 lcd_write_reg(R_POWER_CONTROL2, 0x3704);
314 lcd_write_reg(0x14, 0x1a1b);
315 lcd_write_reg(R_POWER_CONTROL1, 0x3860);
316 lcd_write_reg(R_POWER_CONTROL4, 0x40);
317 lcd_write_reg(R_POWER_CONTROL4, 0x60);
318 lcd_write_reg(R_POWER_CONTROL4, 112);
319 lcd_write_reg(R_DISP_CONTROL1, 0x11);
320 lcd_write_reg(R_DISP_CONTROL1, 0x13 | r_disp_control_rev);
321 display_on = true;
322 lcd_update(); /* Resync display */
323 send_event(LCD_EVENT_ACTIVATION, NULL);
324 sleep(0);
327 else
329 lcd_write_reg(R_DISP_CONTROL1, 0x22);
330 lcd_write_reg(R_DISP_CONTROL1, 0);
331 lcd_write_reg(R_POWER_CONTROL1, 1);
332 display_on = false;
335 #endif
337 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
338 bool lcd_active(void)
340 return display_on;
342 #endif
344 /*** update functions ***/
346 /* FIXME : find the datasheet for this RENESAS controller so we identify the
347 * registers used in windowing code (not present in HD66789R) */
349 /* Set horizontal window addresses */
350 static void lcd_window_x(int xmin, int xmax)
352 xmin += xoffset;
353 xmax += xoffset;
354 lcd_write_reg(R_HORIZ_RAM_ADDR_POS + 2, (xmax << 8) | xmin);
355 lcd_write_reg(R_RAM_ADDR_SET - 1, xmin);
358 /* Set vertical window addresses */
359 static void lcd_window_y(int ymin, int ymax)
361 lcd_write_reg(R_VERT_RAM_ADDR_POS + 2, ymax);
362 lcd_write_reg(R_VERT_RAM_ADDR_POS + 3, ymin);
363 lcd_write_reg(R_RAM_ADDR_SET, ymin);
366 static unsigned lcd_yuv_options = 0;
368 void lcd_yuv_set_options(unsigned options)
370 lcd_yuv_options = options;
373 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
374 extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
375 int width,
376 int stride);
377 extern void lcd_write_yuv420_lines_odither(unsigned char const * const src[3],
378 int width,
379 int stride,
380 int x_screen, /* To align dither pattern */
381 int y_screen);
383 /* Performance function to blit a YUV bitmap directly to the LCD
384 * src_x, src_y, width and height should be even
385 * x, y, width and height have to be within LCD bounds
387 void lcd_blit_yuv(unsigned char * const src[3],
388 int src_x, int src_y, int stride,
389 int x, int y, int width, int height)
391 unsigned char const * yuv_src[3];
392 off_t z;
394 /* Sorry, but width and height must be >= 2 or else */
395 width &= ~1;
396 height >>= 1;
398 z = stride*src_y;
399 yuv_src[0] = src[0] + z + src_x;
400 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
401 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
403 #ifdef HAVE_LCD_FLIP
404 lcd_write_reg(R_ENTRY_MODE,
405 display_flipped ? R_ENTRY_MODE_VIDEO_FLIPPED : R_ENTRY_MODE_VIDEO_NORMAL
407 #else
408 lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_VIDEO_NORMAL);
409 #endif
411 lcd_window_x(x, x + width - 1);
413 if (lcd_yuv_options & LCD_YUV_DITHER)
417 lcd_window_y(y, y + 1);
419 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
421 lcd_write_yuv420_lines_odither(yuv_src, width, stride, x, y);
422 yuv_src[0] += stride << 1; /* Skip down two luma lines */
423 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
424 yuv_src[2] += stride >> 1;
425 y += 2;
427 while (--height > 0);
429 else
433 lcd_window_y(y, y + 1);
435 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
437 lcd_write_yuv420_lines(yuv_src, width, stride);
438 yuv_src[0] += stride << 1; /* Skip down two luma lines */
439 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
440 yuv_src[2] += stride >> 1;
441 y += 2;
443 while (--height > 0);
447 /* Update the display.
448 This must be called after all other LCD functions that change the display. */
449 void lcd_update(void)
451 if (!display_on)
452 return;
454 lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
456 lcd_window_x(0, LCD_WIDTH - 1);
457 lcd_window_y(0, LCD_HEIGHT - 1);
459 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
461 lcd_update_rect(0,0, LCD_WIDTH, LCD_HEIGHT);
464 /* Update a fraction of the display. */
465 void lcd_update_rect(int x, int y, int width, int height)
467 const fb_data *ptr;
469 if (!display_on)
470 return;
472 /* nothing to draw? */
473 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) ||
474 (y >= LCD_HEIGHT) || (x + width <= 0) || (y + height <= 0))
475 return;
477 if (x < 0)
478 { /* clip left */
479 width += x;
480 x = 0;
482 if (y < 0)
483 { /* clip top */
484 height += y;
485 y = 0;
487 if (x + width > LCD_WIDTH)
488 width = LCD_WIDTH - x; /* clip right */
489 if (y + height > LCD_HEIGHT)
490 height = LCD_HEIGHT - y; /* clip bottom */
492 lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
494 /* we need to make x and width even to enable 32bit transfers */
495 width = (width + (x & 1) + 1) & ~1;
496 x &= ~1;
498 lcd_window_x(x, x + width - 1);
499 lcd_window_y(y, y + height -1);
501 lcd_write_cmd(R_WRITE_DATA_2_GRAM);
503 ptr = &lcd_framebuffer[y][x];
507 dbop_write_data(ptr, width);
508 ptr += LCD_WIDTH;
510 while (--height > 0);