Reduce power consumed on the F/X by as much as 12% when the LCD is disabled. Timing...
[kugel-rb.git] / firmware / target / arm / s3c2440 / gigabeat-fx / lcd-meg-fx.c
blob5c666b43f1cf6d19120125e9fe9c11a2c85fd311
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Greg White
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 "cpu.h"
22 #include "string.h"
23 #include "lcd.h"
24 #include "kernel.h"
25 #include "lcd-target.h"
27 #define LCDADDR(x, y) (&lcd_framebuffer[(y)][(x)])
29 static volatile bool lcd_on = true;
30 volatile bool lcd_poweroff = false;
31 static unsigned lcd_yuv_options = 0;
33 ** This is imported from lcd-16bit.c
35 extern struct viewport* current_vp;
37 /* Copies a rectangle from one framebuffer to another. Can be used in
38 single transfer mode with width = num pixels, and height = 1 which
39 allows a full-width rectangle to be copied more efficiently. */
40 extern void lcd_copy_buffer_rect(fb_data *dst, const fb_data *src,
41 int width, int height);
43 bool lcd_enabled()
45 return lcd_on;
48 unsigned int LCDBANK(unsigned int address)
50 return ((address >> 22) & 0xff);
53 unsigned int LCDBASEU(unsigned int address)
55 return (address & ((1 << 22)-1)) >> 1;
58 unsigned int LCDBASEL(unsigned int address)
60 address += 320*240*2;
61 return (address & ((1 << 22)-1)) >> 1;
64 inline void delay_cycles(volatile int delay)
66 while(delay>0) delay--;
69 void LCD_CTRL_setup(void)
71 /* ENVID = 0, BPPMODE = 16 bpp, PNRMODE = TFT, MMODE = Each Frame, CLKVAL = 8 */
72 LCDCON1 = 0x878;
74 /* VCPW = 1, VFPD = 5, LINEVAL = 319, VBPD = 7 */
75 LCDCON2 = 0x74FC141;
77 /* HFPD = 9, HOZVAL = 239, HBPD = 7 */
78 LCDCON3 = 0x38EF09;
80 /* HSPW = 7 */
81 LCDCON4 = 7;
83 /* HWSWP = 1, INVVFRAM = 1, INVVLINE = 1, FRM565 = 1, All others = 0 */
84 LCDCON5 = 0xB01;
86 LCDSADDR1 = (LCDBANK((unsigned)FRAME) << 21) | (LCDBASEU((unsigned)FRAME));
87 LCDSADDR2 = LCDBASEL((unsigned)FRAME);
88 LCDSADDR3 = 0x000000F0;
91 void LCD_CTRL_clock(bool onoff)
93 if(onoff)
95 GPCCON &= ~0xFFF000FC;
96 GPDCON &= ~0xFFF0FFF0;
98 GPCCON |= 0xAAA000A8;
99 GPCUP |= 0xFC0E;
101 GPDCON |= 0xAAA0AAA0;
102 GPDUP |= 0xFCFC;
104 s3c_regset(&CLKCON, 0x20); /* enable LCD clock */
105 LCDCON1 |=0x01;
107 else
109 GPCCON &= ~0xFFF000FC;
110 GPCUP &= ~0xFC0E;
112 GPDCON &= ~0xFFF0FFF0;
113 GPDUP &= ~0xFCFC;
115 LCDCON1 &= ~1; /* Must diable first or bus may freeze */
116 s3c_regclr(&CLKCON, 0x20); /* disable LCD clock */
120 void reset_LCD(bool reset)
122 GPBCON&=~0xC000;
123 GPBCON|=0x4000;
124 if(reset)
125 GPBDAT|=0x80;
126 else
127 GPBDAT&=~0x80;
130 void LCD_SPI_send(const unsigned char *array, int count)
132 while (count--)
134 while ((SPSTA0&0x01)==0){};
135 SPTDAT0=*array++;
139 void LCD_SPI_SS(bool select)
141 delay_cycles(0x4FFF);
143 GPBCON&=~0x30000;
144 GPBCON|=0x10000;
146 if(select)
147 GPBDAT|=0x100;
148 else
149 GPBDAT&=~0x100;
152 void LCD_SPI_start(void)
154 s3c_regset(&CLKCON, 0x40000); /* enable SPI clock */
155 LCD_SPI_SS(false);
156 SPCON0=0x3E;
157 SPPRE0=24;
159 reset_LCD(true);
160 LCD_SPI_SS(true);
163 void LCD_SPI_stop(void)
165 LCD_SPI_SS(false);
167 SPCON0 &= ~0x10;
168 s3c_regclr(&CLKCON, 0x40000); /* disable SPI clock */
171 void LCD_SPI_powerdown(void)
173 const unsigned char powerdncmd[] =
175 0,0x04,1,0x00
178 LCD_SPI_start();
180 LCD_SPI_send(powerdncmd, sizeof(powerdncmd));
182 LCD_SPI_stop();
184 reset_LCD(false); /* This makes a big difference on power */
185 LCD_CTRL_clock(false);
188 void LCD_SPI_powerup(void)
190 const unsigned char powerupcmd[] =
192 0,0x04,1,0x01
195 LCD_CTRL_clock(true);
197 LCD_SPI_start();
199 LCD_SPI_send(powerupcmd, sizeof(powerupcmd));
201 LCD_SPI_stop();
204 void LCD_SPI_init(void)
206 /* SPI data - Right now we are not sure what each of these SPI writes is
207 * actually telling the lcd. Many thanks to Alex Gerchanovsky for
208 * discovering them.
210 * This looks like a register, data combination, 0 denoting a register
211 * address, 1 denoting data. Addr 0x04 is used more than once is
212 * an enable.
214 const unsigned char initbuf[] =
216 0,0x0F,1,0x01,
217 0,0x09,1,0x06,
218 0,0x16,1,0xA6,
219 0,0x1E,1,0x49,
220 0,0x1F,1,0x26,
221 0,0x0B,1,0x2F,
222 0,0x0C,1,0x2B,
223 0,0x19,1,0x5E,
224 0,0x1A,1,0x15,
225 0,0x1B,1,0x15,
226 0,0x1D,1,0x01,
227 0,0x00,1,0x03,
228 0,0x01,1,0x10,
229 0,0x02,1,0x0A,
230 0,0x06,1,0x04,
231 0,0x08,1,0x2E,
232 0,0x24,1,0x12,
233 0,0x25,1,0x3F,
234 0,0x26,1,0x0B,
235 0,0x27,1,0x00,
236 0,0x28,1,0x00,
237 0,0x29,1,0xF6,
238 0,0x2A,1,0x03,
239 0,0x2B,1,0x0A,
240 0,0x04,1,0x01,
243 LCD_CTRL_clock(true);
245 LCD_SPI_start();
247 LCD_SPI_send(initbuf, sizeof(initbuf));
249 LCD_SPI_stop();
252 /* LCD init */
253 void lcd_init_device(void)
255 /* Set pins up */
257 GPHUP &= 0x600;
259 GPECON |= 0x0A800000;
260 GPEUP |= 0x3800;
262 GPBUP |= 0x181;
264 s3c_regset(&CLKCON, 0x20); /* enable LCD clock */
266 LCD_CTRL_setup();
267 LCD_SPI_init();
270 void lcd_enable(bool state)
272 if(state)
274 if(!lcd_on)
276 lcd_on = true;
277 lcd_update();
278 LCD_SPI_powerup();
281 else
283 if(lcd_on) {
284 lcd_on = false;
285 LCD_SPI_powerdown();
290 /* Update a fraction of the display. */
291 void lcd_update_rect(int x, int y, int width, int height)
293 fb_data *dst, *src;
295 if (!lcd_on)
296 return;
298 if (x + width > LCD_WIDTH)
299 width = LCD_WIDTH - x; /* Clip right */
300 if (x < 0)
301 width += x, x = 0; /* Clip left */
302 if (width <= 0)
303 return; /* nothing left to do */
305 if (y + height > LCD_HEIGHT)
306 height = LCD_HEIGHT - y; /* Clip bottom */
307 if (y < 0)
308 height += y, y = 0; /* Clip top */
309 if (height <= 0)
310 return; /* nothing left to do */
312 /* TODO: It may be faster to swap the addresses of lcd_driver_framebuffer
313 * and lcd_framebuffer */
314 dst = (fb_data *)FRAME + LCD_WIDTH*y + x;
315 src = &lcd_framebuffer[y][x];
317 /* Copy part of the Rockbox framebuffer to the second framebuffer */
318 if (width < LCD_WIDTH)
320 /* Not full width - do line-by-line */
321 lcd_copy_buffer_rect(dst, src, width, height);
323 else
325 /* Full width - copy as one line */
326 lcd_copy_buffer_rect(dst, src, LCD_WIDTH*height, 1);
330 /* Update the display.
331 This must be called after all other LCD functions that change the display. */
332 void lcd_update(void)
334 if (!lcd_on)
335 return;
337 lcd_copy_buffer_rect((fb_data *)FRAME, &lcd_framebuffer[0][0],
338 LCD_WIDTH*LCD_HEIGHT, 1);
341 void lcd_bitmap_transparent_part(const fb_data *src, int src_x, int src_y,
342 int stride, int x, int y, int width,
343 int height)
345 int w, px;
346 fb_data *dst;
348 if (x + width > current_vp->width)
349 width = current_vp->width - x; /* Clip right */
350 if (x < 0)
351 width += x, x = 0; /* Clip left */
352 if (width <= 0)
353 return; /* nothing left to do */
355 if (y + height > current_vp->height)
356 height = current_vp->height - y; /* Clip bottom */
357 if (y < 0)
358 height += y, y = 0; /* Clip top */
359 if (height <= 0)
360 return; /* nothing left to do */
362 src += stride * src_y + src_x; /* move starting point */
363 dst = &lcd_framebuffer[current_vp->y+y][current_vp->x+x];
365 asm volatile (
366 ".rowstart: \r\n"
367 "mov %[w], %[width] \r\n" /* Load width for inner loop */
368 ".nextpixel: \r\n"
369 "ldrh %[px], [%[s]], #2 \r\n" /* Load src pixel */
370 "add %[d], %[d], #2 \r\n" /* Uncoditionally increment dst */
371 "cmp %[px], %[fgcolor] \r\n" /* Compare to foreground color */
372 "streqh %[fgpat], [%[d], #-2] \r\n" /* Store foregroud if match */
373 "cmpne %[px], %[transcolor] \r\n" /* Compare to transparent color */
374 "strneh %[px], [%[d], #-2] \r\n" /* Store dst if not transparent */
375 "subs %[w], %[w], #1 \r\n" /* Width counter has run down? */
376 "bgt .nextpixel \r\n" /* More in this row? */
377 "add %[s], %[s], %[sstp], lsl #1 \r\n" /* Skip over to start of next line */
378 "add %[d], %[d], %[dstp], lsl #1 \r\n"
379 "subs %[h], %[h], #1 \r\n" /* Height counter has run down? */
380 "bgt .rowstart \r\n" /* More rows? */
381 : [w]"=&r"(w), [h]"+&r"(height), [px]"=&r"(px),
382 [s]"+&r"(src), [d]"+&r"(dst)
383 : [width]"r"(width),
384 [sstp]"r"(stride - width),
385 [dstp]"r"(LCD_WIDTH - width),
386 [transcolor]"r"(TRANSPARENT_COLOR),
387 [fgcolor]"r"(REPLACEWITHFG_COLOR),
388 [fgpat]"r"(current_vp->fg_pattern)
392 void lcd_yuv_set_options(unsigned options)
394 lcd_yuv_options = options;
397 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
398 extern void lcd_write_yuv420_lines(fb_data *dst,
399 unsigned char const * const src[3],
400 int width,
401 int stride);
402 extern void lcd_write_yuv420_lines_odither(fb_data *dst,
403 unsigned char const * const src[3],
404 int width,
405 int stride,
406 int x_screen, /* To align dither pattern */
407 int y_screen);
408 /* Performance function to blit a YUV bitmap directly to the LCD */
409 /* For the Gigabeat - show it rotated */
410 /* So the LCD_WIDTH is now the height */
411 void lcd_blit_yuv(unsigned char * const src[3],
412 int src_x, int src_y, int stride,
413 int x, int y, int width, int height)
415 /* Caches for chroma data so it only need be recaculated every other
416 line */
417 unsigned char const * yuv_src[3];
418 off_t z;
420 if (!lcd_on)
421 return;
423 /* Sorry, but width and height must be >= 2 or else */
424 width &= ~1;
425 height >>= 1;
427 y = LCD_WIDTH - 1 - y;
428 fb_data *dst = (fb_data*)FRAME + x * LCD_WIDTH + y;
430 z = stride*src_y;
431 yuv_src[0] = src[0] + z + src_x;
432 yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
433 yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
435 if (lcd_yuv_options & LCD_YUV_DITHER)
439 lcd_write_yuv420_lines_odither(dst, yuv_src, width, stride, y, x);
440 yuv_src[0] += stride << 1; /* Skip down two luma lines */
441 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
442 yuv_src[2] += stride >> 1;
443 dst -= 2;
444 y -= 2;
446 while (--height > 0);
448 else
452 lcd_write_yuv420_lines(dst, yuv_src, width, stride);
453 yuv_src[0] += stride << 1; /* Skip down two luma lines */
454 yuv_src[1] += stride >> 1; /* Skip down one chroma line */
455 yuv_src[2] += stride >> 1;
456 dst -= 2;
458 while (--height > 0);
462 void lcd_set_contrast(int val) {
463 (void) val;
464 // TODO:
467 void lcd_set_invert_display(bool yesno) {
468 (void) yesno;
469 // TODO:
472 void lcd_set_flip(bool yesno) {
473 (void) yesno;
474 // TODO: