1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2008 by Maurus Cuelenaere
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <sys/types.h>
28 #include "string-extra.h" /* memset16() */
31 #include "spi-target.h"
32 #include "lcd-target.h"
35 /* Power and display status */
36 static bool display_on
= false; /* Is the display turned on? */
37 static bool direct_fb_access
= false; /* Does the DM320 has direct access to the FB? */
39 /* Copies a rectangle from one framebuffer to another. Can be used in
40 single transfer mode with width = num pixels, and height = 1 which
41 allows a full-width rectangle to be copied more efficiently. */
42 extern void lcd_copy_buffer_rect(fb_data
*dst
, const fb_data
*src
,
43 int width
, int height
);
45 int lcd_default_contrast(void)
50 void lcd_set_contrast(int val
)
52 /* find S6F2002 controller datasheet first */
56 void lcd_set_invert_display(bool yesno
) {
61 void lcd_set_flip(bool yesno
) {
66 static void enable_venc(bool enable
)
71 IO_CLK_MOD1
&= ~(CLK_MOD1_VENC
| CLK_MOD1_OSD
); /* disable OSD clock and VENC clock */
74 IO_CLK_OSEL
&= ~CLK_OSEL_O2SEL(0xF); /* reset 'General purpose clock output (GIO26, GIO34)' and */
75 IO_CLK_OSEL
|= CLK_OSEL_O2SEL(4); /* set to 'PLLIN clock' */
77 IO_CLK_SEL1
|= (CLK_SEL1_OSD
| CLK_SEL1_VENC(7)); /* set to 'GP clock output 2 (GIO26, GIO34)' and turn on 'VENC clock' */
78 IO_CLK_MOD1
|= (CLK_MOD1_VENC
| CLK_MOD1_OSD
); /* enable OSD clock and VENC clock */
82 /* Disable video encoder */
83 IO_VID_ENC_VMOD
&= ~VENC_VMOD_VENC
;
84 /* Disable clock for power saving */
85 IO_CLK_MOD1
&= ~(CLK_MOD1_VENC
| CLK_MOD1_OSD
);
89 /* LTV250QV panel functions */
90 static void lcd_write_reg(unsigned char reg
, unsigned short val
)
92 unsigned char block
[3] = {LTV_OPC_INDEX
, 0, reg
| 0xFF};
93 spi_block_transfer(SPI_target_LTV250QV
, block
, sizeof(block
), NULL
, 0);
94 block
[0] = LTV_OPC_DATA
;
96 block
[2] = val
& 0xFF;
97 spi_block_transfer(SPI_target_LTV250QV
, block
, sizeof(block
), NULL
, 0);
100 static void sleep_ms(unsigned int ms
)
105 static void lcd_display_on(bool reset
)
107 /* Enable main power */
108 IO_GIO_BITSET2
|= (1 << 3);
110 /* power on sequence as per the ZVM firmware */
112 IO_GIO_BITSET1
= (1 << 13);
114 IO_GIO_BITSET2
= (1 << 5);
115 IO_GIO_BITSET2
= (1 << 8);
118 /*Init SPI here... */
121 IO_GIO_BITSET2
= (1 << 0);
123 IO_GIO_BITSET2
= (1 << 7);
125 IO_GIO_BITSET2
= (1 << 4);
127 IO_GIO_BITCLR2
= (1 << 8);
128 /*TODO: figure out what OF does after this... */
129 IO_GIO_BITSET2
= (1 << 8);
132 lcd_write_reg(LTV_IFCTL
, LTV_NL(29));
133 lcd_write_reg(LTV_DATACTL
, 0);
134 lcd_write_reg(LTV_ENTRY_MODE
,0);
135 lcd_write_reg(LTV_GATECTL1
, 0);
136 lcd_write_reg(LTV_GATECTL2
, (LTV_NW_INV_1LINE
| LTV_FHN
| LTV_FTI(2) | LTV_FWI(3)));
137 lcd_write_reg(LTV_VBP
, 0);
138 lcd_write_reg(LTV_HBP
, 0);
139 lcd_write_reg(LTV_SOTCTL
, 0);
140 lcd_write_reg(LTV_PWRCTL1
, 0);
141 lcd_write_reg(LTV_PWRCTL2
, 0);
142 lcd_write_reg(LTV_GAMMA(0), 0);
143 lcd_write_reg(LTV_GAMMA(1), 0);
144 lcd_write_reg(LTV_GAMMA(2), 0);
145 lcd_write_reg(LTV_GAMMA(3), 0);
146 lcd_write_reg(LTV_GAMMA(4), 0);
147 lcd_write_reg(LTV_GAMMA(5), 0);
148 lcd_write_reg(LTV_GAMMA(6), 0);
149 lcd_write_reg(LTV_GAMMA(7), 0);
150 lcd_write_reg(LTV_GAMMA(8), 0);
151 lcd_write_reg(LTV_GAMMA(9), 0);
154 lcd_write_reg(LTV_PWRCTL1
, (LTV_VCOM_DISABLE
| LTV_DRIVE_CURRENT(5) | LTV_SUPPLY_CURRENT(5)));
155 lcd_write_reg(LTV_PWRCTL2
, 0);
158 lcd_write_reg(LTV_PWRCTL2
, LTV_VCOML_ENABLE
);
161 lcd_write_reg(LTV_IFCTL
, (LTV_NMD
| LTV_NL(29)));
162 lcd_write_reg(LTV_DATACTL
, (LTV_DS_SAME
| LTV_CHS_480
| LTV_DF_RGB
| LTV_RGB_BGR
));
163 lcd_write_reg(LTV_ENTRY_MODE
,(LTV_VSPL_ACTIVE_LOW
| LTV_HSPL_ACTIVE_LOW
| LTV_DPL_SAMPLE_RISING
| LTV_EPL_ACTIVE_LOW
| LTV_SS_RIGHT_TO_LEFT
));
164 lcd_write_reg(LTV_GATECTL1
, LTV_CLW(1));
165 lcd_write_reg(LTV_GATECTL2
, (LTV_NW_INV_1LINE
| LTV_DSC
| LTV_FTI(3) | LTV_FWI(3)));
166 lcd_write_reg(LTV_VBP
, 0x5);
167 lcd_write_reg(LTV_HBP
, 0x1B);
168 lcd_write_reg(LTV_SOTCTL
, LTV_SDT(2));
169 lcd_write_reg(LTV_GAMMA(0), 0x203);
170 lcd_write_reg(LTV_GAMMA(1), 0x302);
171 lcd_write_reg(LTV_GAMMA(2), 0xC08);
172 lcd_write_reg(LTV_GAMMA(3), 0xC08);
173 lcd_write_reg(LTV_GAMMA(4), 0x707);
174 lcd_write_reg(LTV_GAMMA(5), 0x707);
175 lcd_write_reg(LTV_GAMMA(6), 0x104);
176 lcd_write_reg(LTV_GAMMA(7), 0x306);
177 lcd_write_reg(LTV_GAMMA(8), 0);
178 lcd_write_reg(LTV_GAMMA(9), 0);
181 lcd_write_reg(LTV_PWRCTL1
, (LTV_VCOMOUT_ENABLE
| LTV_POWER_ON
| LTV_DRIVE_CURRENT(5) | LTV_SUPPLY_CURRENT(5)));
182 lcd_write_reg(LTV_PWRCTL2
, (LTV_VCOML_VOLTAGE(17) | LTV_VCOMH_VOLTAGE(26))); /* VCOML=0,0625V VCOMH=1,21875V */
188 /* Re-enable video encoder */
189 IO_VID_ENC_VMOD
|= VENC_VMOD_VENC
;
191 /* tell that we're on now */
195 #ifdef HAVE_LCD_ENABLE
196 static void lcd_display_off(void)
198 /* LQV shutdown sequence */
199 lcd_write_reg(LTV_PWRCTL1
, (LTV_VCOMOUT_ENABLE
| LTV_DRIVE_CURRENT(5) | LTV_SUPPLY_CURRENT(5)));
202 lcd_write_reg(LTV_PWRCTL1
, (LTV_DRIVE_CURRENT(5) | LTV_SUPPLY_CURRENT(5)));
203 lcd_write_reg(LTV_GATECTL2
, (LTV_NW_INV_1LINE
| LTV_FTI(3) | LTV_FWI(3)));
204 lcd_write_reg(LTV_PWRCTL2
, 0);
207 lcd_write_reg(LTV_PWRCTL1
, 0);
209 unsigned char temp
[1];
211 spi_block_transfer(SPI_target_LTV250QV
, temp
, sizeof(temp
), NULL
, 0);
213 IO_GIO_BITCLR2
= (1 << 4);
215 IO_GIO_BITCLR2
= (1 << 7);
217 IO_GIO_BITCLR2
= (1 << 0);
219 IO_GIO_BITCLR2
= (1 << 8);
220 IO_GIO_BITCLR2
= (1 << 5);
222 /* Disable main power */
223 IO_GIO_BITCLR2
|= (1 << 3);
230 void lcd_enable(bool on
)
232 /* Disabled until properly working */
234 if (on
== display_on
)
239 lcd_display_on(false); /* Turn on display */
240 lcd_update(); /* Resync display */
241 send_event(LCD_EVENT_ACTIVATION
, NULL
);
245 lcd_display_off(); /* Turn off display */
250 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
251 bool lcd_active(void)
257 void lcd_set_direct_fb(bool yes
)
260 direct_fb_access
= yes
;
262 addr
= ((unsigned int)&lcd_framebuffer
-CONFIG_SDRAM_START
) / 32;
264 addr
= ((unsigned int)FRAME
-CONFIG_SDRAM_START
) / 32;
265 IO_OSD_OSDWINADH
= addr
>> 16;
266 IO_OSD_OSDWIN0ADL
= addr
& 0xFFFF;
269 bool lcd_get_direct_fb(void)
271 return direct_fb_access
;
274 static bool _lcd_enabled(void)
276 /* Needed to detect if VENC/LCD already is initialized... */
277 if(IO_VID_ENC_VDCTL
& VENC_VDCTL_VCLKE
)
279 else if(!(IO_VID_ENC_VDCTL
& VENC_VDCTL_YCDC
))
281 else if(IO_CLK_MOD1
& CLK_MOD1_VENC
)
283 else if(IO_CLK_MOD1
& CLK_MOD1_OSD
)
289 void lcd_init_device(void)
293 lcd_display_on(true);
297 /* Set LCD values in Video Encoder */
298 IO_VID_ENC_VMOD
&= 0x8800; /* Clear all values */
299 IO_VID_ENC_VMOD
|= (VENC_VMOD_DACPD
| VENC_VMOD_VMD
| VENC_VMOD_ITLC
| VENC_VMOD_VDMD(2)); /* set mode to RGB666 parallel 16 bit */
300 IO_VID_ENC_VDCTL
&= 0x8FE8; /* Clear all values */
301 IO_VID_ENC_VDCTL
|= (VENC_VDCTL_VCLKP
| VENC_VDCTL_DOMD(2)),
302 IO_VID_ENC_VDPRO
= VENC_VDPRO_PFLTR
;
303 IO_VID_ENC_SYNCTL
&= 0xE000; /* Clear all values */
304 IO_VID_ENC_SYNCTL
|= (VENC_SYNCTL_VPL
| VENC_SYNCTL_HPL
);
305 IO_VID_ENC_HSDLY
= 0;
306 IO_VID_ENC_HSPLS
= 0x12;
307 IO_VID_ENC_HSTART
= 0x1B;
308 IO_VID_ENC_HVALID
= 0x140;
309 IO_VID_ENC_HINT
= 0x168;
310 IO_VID_ENC_VSDLY
= 0;
311 IO_VID_ENC_VSPLS
= 3;
312 IO_VID_ENC_VSTART
= 5;
313 IO_VID_ENC_VVALID
= 0xF0;
314 IO_VID_ENC_VINT
= 0x118;
315 IO_VID_ENC_RGBCTL
&= 0x088; /* Clear all values */
316 IO_VID_ENC_RGBCTL
|= VENC_RGBCTL_DFLTR
;
317 IO_VID_ENC_RGBCLP
= VENC_RGBCLP_UCLIP(0xFF);
318 IO_VID_ENC_LCDOUT
&= 0xFE00; /* Clear all values */
319 IO_VID_ENC_LCDOUT
|= (VENC_LCDOUT_OEE
| VENC_LCDOUT_FIDS
);
320 IO_VID_ENC_DCLKCTL
&= 0xC0C0; /* Clear all values */
321 IO_VID_ENC_DCLKCTL
|= VENC_DCLKCTL_DCKEC
;
322 IO_VID_ENC_DCLKPTN0
= 1;
323 DM320_REG(0x0864) = 0; /* ???? */
328 /* Based on lcd-mr500.c from Catalin Patulea */
329 /* Clear the Frame */
330 memset16(FRAME
, 0x0000, LCD_WIDTH
*LCD_HEIGHT
);
332 IO_OSD_MODE
= 0x00ff;
333 IO_OSD_VIDWINMD
= 0x0002;
334 IO_OSD_OSDWINMD0
= 0x2001;
335 IO_OSD_OSDWINMD1
= 0x0002;
336 IO_OSD_ATRMD
= 0x0000;
337 IO_OSD_RECTCUR
= 0x0000;
340 IO_OSD_OSDWIN0OFST
= (LCD_WIDTH
*16) / 256;
341 addr
= ((unsigned int)FRAME
-CONFIG_SDRAM_START
) / 32;
342 IO_OSD_OSDWINADH
= addr
>> 16;
343 IO_OSD_OSDWIN0ADL
= addr
& 0xFFFF;
353 IO_OSD_OSDWIN0XP
= 0;
354 IO_OSD_OSDWIN0YP
= 0;
355 IO_OSD_OSDWIN0XL
= LCD_WIDTH
;
356 IO_OSD_OSDWIN0YL
= LCD_HEIGHT
;
358 IO_VID_ENC_VDCTL
|= VENC_VDCTL_VCLKE
; /* Enable VCLK */
359 IO_VID_ENC_VMOD
|= VENC_VMOD_VENC
; /* Enable video encoder */
360 IO_VID_ENC_SYNCTL
|= VENC_SYNCTL_SYE
; /* Enable sync output */
361 IO_VID_ENC_VDCTL
&= ~VENC_VDCTL_DOMD(3); /* Normal digital data output */
365 /*** Update functions ***/
367 /* Update a fraction of the display. */
368 void lcd_update_rect(int x
, int y
, int width
, int height
)
370 register fb_data
*dst
, *src
;
372 if (!display_on
|| direct_fb_access
)
375 if (x
+ width
> LCD_WIDTH
)
376 width
= LCD_WIDTH
- x
; /* Clip right */
378 width
+= x
, x
= 0; /* Clip left */
380 return; /* nothing left to do */
382 if (y
+ height
> LCD_HEIGHT
)
383 height
= LCD_HEIGHT
- y
; /* Clip bottom */
385 height
+= y
, y
= 0; /* Clip top */
387 return; /* nothing left to do */
389 #if CONFIG_ORIENTATION == SCREEN_PORTRAIT
390 dst
= (fb_data
*)FRAME
+ LCD_WIDTH
*y
+ x
;
391 src
= &lcd_framebuffer
[y
][x
];
393 /* Copy part of the Rockbox framebuffer to the second framebuffer */
394 if (width
< LCD_WIDTH
)
396 /* Not full width - do line-by-line */
397 lcd_copy_buffer_rect(dst
, src
, width
, height
);
401 /* Full width - copy as one line */
402 lcd_copy_buffer_rect(dst
, src
, LCD_WIDTH
*height
, 1);
405 src
= &lcd_framebuffer
[y
][x
];
408 register fb_data
*start
=FRAME
+ LCD_HEIGHT
*(LCD_WIDTH
-x
-1) + y
+ 1;
410 for(yc
=0;yc
<height
;yc
++)
413 for(xc
=0; xc
<width
; xc
++)
423 /* Update the display.
424 This must be called after all other LCD functions that change the display. */
425 void lcd_update(void)
427 if (!display_on
|| direct_fb_access
)
429 #if CONFIG_ORIENTATION == SCREEN_PORTRAIT
430 lcd_copy_buffer_rect((fb_data
*)FRAME
, &lcd_framebuffer
[0][0],
431 LCD_WIDTH
*LCD_HEIGHT
, 1);
433 lcd_update_rect(0, 0, LCD_WIDTH
, LCD_HEIGHT
);
437 /* Line write helper function for lcd_yuv_blit. Write two lines of yuv420. */
438 extern void lcd_write_yuv420_lines(fb_data
*dst
,
439 unsigned char chroma_buf
[LCD_HEIGHT
/2*3],
440 unsigned char const * const src
[3],
443 /* Performance function to blit a YUV bitmap directly to the LCD */
444 /* For the Gigabeat - show it rotated */
445 /* So the LCD_WIDTH is now the height */
446 void lcd_blit_yuv(unsigned char * const src
[3],
447 int src_x
, int src_y
, int stride
,
448 int x
, int y
, int width
, int height
)
450 /* Caches for chroma data so it only need be recalculated every other
452 unsigned char chroma_buf
[LCD_HEIGHT
/2*3]; /* 480 bytes */
453 unsigned char const * yuv_src
[3];
456 if (!display_on
|| direct_fb_access
)
459 /* Sorry, but width and height must be >= 2 or else */
463 fb_data
*dst
= (fb_data
*)FRAME
+ x
* LCD_WIDTH
+ (LCD_WIDTH
- y
) - 1;
466 yuv_src
[0] = src
[0] + z
+ src_x
;
467 yuv_src
[1] = src
[1] + (z
>> 2) + (src_x
>> 1);
468 yuv_src
[2] = src
[2] + (yuv_src
[1] - src
[1]);
472 lcd_write_yuv420_lines(dst
, chroma_buf
, yuv_src
, width
,
475 yuv_src
[0] += stride
<< 1; /* Skip down two luma lines */
476 yuv_src
[1] += stride
>> 1; /* Skip down one chroma line */
477 yuv_src
[2] += stride
>> 1;
480 while (--height
> 0);