1. 修改配置头文件,是整个工程支持LCD。
在修改include/configs/smdk6410.h 增加一行
#define CONFIG_LCD
这行会把common/lcd.c加入编译,同时影响lib_arm/board.c,lib_arm/armlinux.c。在文件cpu/s3c64xx/cpu.c中,还有进入linux内核前的关于lcd控制的准备工作lcd_disable(),lcd_panel_disable()。
2. 修改common/lcd.c
在lcd.c中准备函数calc_fbsize(),这个函数会在armlinux.c中调用,用于计算lcd显示缓冲需要的内存。注意,如果我们使用的现实缓冲比较大,就不能使用这个方法来分配。uboot中malloc函数最大分配的内存,不可能超过uboot使用的总内存512K。如果显示分辨率只有320x240,这个占用内存只有153K,这个方法还是可行的。
先定义一个LCD的显示控制结构,用于各种函数的计算。
typedef struct vidinfo {
ushort vl_col; /*每行点数,如320*/
ushort vl_row; /*整屏行数,如340 */
ushort vl_width; /* 虚拟屏宽度,我们简化,不用虚拟屏*/
ushort vl_height; /* 虚拟屏高度*/
u_char vl_bpix; /*每点位数,如16*/
} vidinfo_t;
我们使用宏结构定义各种参数:
//1376 //1178
#define S3CFB_HFP 52//40 /*前沿点数*/
#define S3CFB_HSW 50//48 /* hsync脉冲宽度 */
#define S3CFB_HBP 200//240//40 /* 后沿点数 */
//805 //807
#define S3CFB_VFP 13//13 /* 前沿行数 */
#define S3CFB_VSW 1//3 /* vsync脉冲宽度 */
#define S3CFB_VBP 25//29 /* 后沿行数 */
#define S3CFB_HRES 1024 /* x方向分辨率*/
#define S3CFB_VRES 768 /* y方向分辨率*/
#define S3CFB_VFRAME_FREQ 60 /* 帧频率*/
#define PIXELBITS 16 /*每点使用位数*/
#define S3CFB_IVCLK CFG_LOW /*VCLK极性控制*/
#define S3CFB_IHSYNC CFG_HIGH /*HSYNC极性控制*/
#define S3CFB_IVSYNC CFG_HIGH /*VSYNC极性控制*/
#define S3CFB_IVDEN CFG_LOW /*DE 数据有效极性控制*/
#define S3CFB_PIXEL_CLOCK (S3CFB_VFRAME_FREQ * (S3CFB_HFP + S3CFB_HSW + S3CFB_HBP + S3CFB_HRES) * (S3CFB_VFP + S3CFB_VSW + S3CFB_VBP + S3CFB_VRES))
我们使用的LCD比较大,我们直接指定显示缓冲区。
#define LCD_FRAMEBUFFER (TEXT_BASE - 0x300000)
准备参数结构
vidinfo_t panel_info = {
S3CFB_HRES,
S3CFB_VRES,
0,
0,
PIXELBITS,
};
需要在uboot中,通过计算分配显示缓冲,需要加入下面的宏:
DECLARE_GLOBAL_DATA_PTR;
现在我们完成calc_fbsize函数
ulong calc_fbsize (void)
{
ulong size;
int line_length = (panel_info.vl_col * panel_info.vl_bpix) / 8;
size = line_length * panel_info.vl_row;
#ifdef LCD_FRAMEBUFFER
size = 4096; //直接给一个固定值
#endif
return size;
}
3. 继续修改lcd.c,准备函数lcd_setmem,给board.c调用
ulong lcd_setmem (ulong addr)
{
ulong size;
int line_length = (panel_info.vl_col * panel_info.vl_bpix) / 8;
debug ("LCD panel info: %d x %d, %d bit/pix\n",
panel_info.vl_col, panel_info.vl_row, NBITS (panel_info.vl_bpix) );
size = line_length * panel_info.vl_row;
#ifdef LCD_FRAMEBUFFER
size = 4096; //直接给一个固定值
#endif
/* Round up to nearest full page */
size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
/* Allocate pages for the frame buffer. */
addr -= size;
debug ("Reserving %ldk for LCD Framebuffer at: %08lx\n", size>>10, addr);
return (addr);
}
4. 为了简化我们的工作,我们使用linux内核中的头文件,来替换我们重新定义寄存器。
从linux2.6.28文件包中,提取arch/arm/plat-s3c/include/plat/regs-lcd.h
开头部分用下面替换
#include <common.h>
#include <regs.h>
/***************************************************************************/
/* LCD Registers for S3C2443/2450/S3C6400/6410 */
#define S3C_LCDREG(x) __REG((x) + ELFIN_LCD_BASE)
/* LCD control registers */
#define S3C_VIDCON0 S3C_LCDREG(0x00) /* Video control 0 register */
#define S3C_VIDCON1 S3C_LCDREG(0x04) /* Video control 1 register */
增加MIFPCON_REG 和SPCON_REG寄存器的定义
#define MIFPCON_REG __REG(0x7410800C)#define SEL_BYPASS_MASK 0x00000008
#define SPCON_REG __REG(ELFIN_GPIO_BASE + SPCON_OFFSET)
#define LCD_SEL_MASK 0x00000003
#define RGB_IF_STYLE_MASK 0x00000001
5. 增加函数lcd_disable,lcd_panel_disable用于进入linux内核的准备工作
void lcd_disable (void)
{
S3C_VIDCON0 &= (~(S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE));
}
void lcd_Enable (void)
{
S3C_VIDCON0 |= (S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE);
}
void lcd_panel_disable(void)
{
MIFPCON_REG |= SEL_BYPASS_MASK;
}
6. 准备驱动函数
/************************************************************************/
/* ** GENERIC Initialization Routines */
/************************************************************************/
int drv_lcd_init (void)
{
device_t lcddev;
int rc;
lcd_base = (void *)(gd->fb_base);
#ifdef LCD_FRAMEBUFFER
lcd_base = (void*)LCD_FRAMEBUFFER;
#endif
lcd_line_length = (panel_info.vl_col * panel_info.vl_bpix) / 8;
lcd_color_fg = 0xffff;
lcd_color_bg = 0x0000;
lcd_init (lcd_base); /* LCD initialization */
//画线,测试一下显示效果
lcd_vline(0,0,S3CFB_HRES,0x001f);
lcd_vline(0,S3CFB_VRES-1,S3CFB_HRES,0x07e0);
lcd_hline(0,0,S3CFB_VRES,0xf800);
lcd_hline(S3CFB_HRES-1,0,S3CFB_VRES,0xffff);
#ifdef CONFIG_NO_VIDEO_CONSOLE
return 0;
#endif
/* Device initialization */
memset (&lcddev, 0, sizeof (lcddev));
strcpy (lcddev.name, "lcd");
lcddev.ext = 0; /* No extensions */
lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */
lcddev.putc = lcd_putc; /* 'putc' function */
lcddev.puts = lcd_puts; /* 'puts' function */
rc = device_register (&lcddev); //如果需要uboot的打印信息使用LCD,需要登记设备。
return (rc == 0) ? 1 : rc;
}
7. 准备lcd_init 初始化屏显位置信息,图片等(为简化略过显示图片)
static int lcd_init (void *lcdbase)
{
/* Initialize the lcd controller */
debug ("[LCD] Initializing LCD frambuffer at %p\n", lcdbase);
lcd_ctrl_init (lcdbase);
//lcd_clear (NULL, 1, 1, NULL); /* dummy args */
//lcd_enable ();
/* Initialize the console */
console_col = 0;
#ifdef CONFIG_LCD_INFO_BELOW_LOGO
console_row = 7 + BMP_LOGO_HEIGHT / VIDEO_FONT_HEIGHT;
#else
console_row = 1; /* leave 1 blank line below logo */
#endif
lcd_is_enabled = 1;
return 0;
}
8 . 寄存器相关的初始化
static int lcd_ctrl_init(void *lcdbase)
{
ulong freq_lcdclk;
ulong freq_Hclk;
ulong fb_size;
unsigned char nn;
unsigned short *pp;
int i;
//初始化管脚分配
GPICON_REG = 0xaaaaaaaa;
GPIPUD_REG = 0xaaaaaaaa;
GPJCON_REG = 0xaaaaaaaa;
GPJPUD_REG = 0xaaaaaaaa;
lcd_disable();
S3C_WINCON0 &= (~(S3C_WINCONx_ENWIN_F_ENABLE));
按照手册初始化寄存器
// (1)MOFPCON:SEL_BYPASS[3] value@0x7410800C 必须设置为0
MIFPCON_REG &= (~SEL_BYPASS_MASK);
//(2)SPCON:LCD_SEL[1:0]value@0x74F0081A0 选择RGB I/F类型
SPCON_REG &= (~LCD_SEL_MASK);
SPCON_REG |= (RGB_IF_STYLE_MASK);
//(3)VIDCON0:配置视频输出格式
freq_lcdclk = S3CFB_PIXEL_CLOCK;
freq_Hclk = get_HCLK();
nn = (unsigned char)(freq_Hclk / freq_lcdclk) - 1;
if(freq_lcdclk < freq_Hclk/2)
{
S3C_VIDCON0 = S3C_VIDCON0_INTERLACE_F_PROGRESSIVE + S3C_VIDCON0_VIDOUT_RGB_IF + \
S3C_VIDCON0_PNRMODE_RGB_P + S3C_VIDCON0_CLKVALUP_ST_FRM + S3C_VIDCON0_CLKVAL_F(nn) + \
S3C_VIDCON0_CLKDIR_DIVIDED + S3C_VIDCON0_CLKSEL_F_HCLK;
}else
{
S3C_VIDCON0 = S3C_VIDCON0_INTERLACE_F_PROGRESSIVE + S3C_VIDCON0_VIDOUT_RGB_IF + \
S3C_VIDCON0_PNRMODE_RGB_P + S3C_VIDCON0_CLKVALUP_ST_FRM + S3C_VIDCON0_CLKVAL_F(0) + \
S3C_VIDCON0_CLKDIR_DIRECTED + S3C_VIDCON0_CLKSEL_F_HCLK;
}
//(4)VIDCON1:RGB I/F 控制信号
nn = 0;
if(S3CFB_IVCLK)
{
nn += S3C_VIDCON1_IVCLK_RISE_EDGE;
}
if(S3CFB_IHSYNC)
{
nn += S3C_VIDCON1_IHSYNC_INVERT;
}
if(S3CFB_IVSYNC)
{
nn += S3C_VIDCON1_IVSYNC_INVERT;
}
if(S3CFB_IVDEN)
{
nn += S3C_VIDCON1_IVDEN_INVERT;
}
S3C_VIDCON1 = (unsigned int)nn;
S3C_VIDCON2 = 0;
//(5)I80IFCONx:i80系统I/F信号控制
//(6)ITUIFCON0:ITU(BT.601)接口控制
//(7)VIDTCONx:视频输出时序和显示尺寸
S3C_VIDTCON0 = S3C_VIDTCON0_VBPD(S3CFB_VBP - 1) | S3C_VIDTCON0_VFPD(S3CFB_VFP - 1) | S3C_VIDTCON0_VSPW(S3CFB_VSW - 1);
S3C_VIDTCON1 = S3C_VIDTCON1_HBPD(S3CFB_HBP - 1) | S3C_VIDTCON1_HFPD(S3CFB_HFP -1) | S3C_VIDTCON1_HSPW(S3CFB_HSW - 1);
S3C_VIDTCON2 = S3C_VIDTCON2_LINEVAL(S3CFB_VRES - 1) | S3C_VIDTCON2_HOZVAL(S3CFB_HRES - 1);
//(8)WINCONx:窗口格式控制,为简化只使用窗口0,不使用双缓冲
S3C_WINCON0 = S3C_WINCONx_BPPMODE_F_16BPP_565;
S3C_WINCON1 = 0;
S3C_WINCON2 = 0;
S3C_WINCON3 = 0;
S3C_WINCON4 = 0;
//(9)VIDOSDxA ,VIDOSDxB:窗口位置控制
S3C_VIDOSD0A = S3C_VIDOSDxA_OSD_LTX_F(0) + S3C_VIDOSDxA_OSD_LTY_F(0);
S3C_VIDOSD0B = S3C_VIDOSDxB_OSD_RBX_F(S3CFB_HRES - 1) | S3C_VIDOSDxB_OSD_RBY_F(S3CFB_VRES - 1);
S3C_VIDOSD0C = S3C_VIDOSD0C_OSDSIZE(S3CFB_HRES*S3CFB_VRES);
S3C_VIDOSD1A = 0;
S3C_VIDOSD1B = 0;
S3C_VIDOSD1C = 0;
S3C_VIDOSD1D = 0;
S3C_VIDOSD2A = 0;
S3C_VIDOSD2B = 0;
S3C_VIDOSD2C = 0;
S3C_VIDOSD2D = 0;
S3C_VIDOSD3A = 0;
S3C_VIDOSD3B = 0;
S3C_VIDOSD3C = 0;
S3C_VIDOSD4A = 0;
S3C_VIDOSD4B = 0;
S3C_VIDOSD4C = 0;
//(10)VIDOSDxC:alpha 值设置
//(11)VIDWxxADDx:源图像地址设置
fb_size = calc_fbsize();
#ifdef LCD_FRAMEBUFFER
fb_size = (panel_info.vl_col * panel_info.vl_bpix) / 8 * panel_info.vl_row;
#endif
// S3C_VIDW00ADD0B0 = (unsigned int)(lcdbase) | S3C_VIDWxxADD0_VBANK_F((unsigned int)lcdbase);
S3C_VIDW00ADD0B0 = virt_to_phys(lcdbase);
S3C_VIDW00ADD0B1 = 0;
S3C_VIDW01ADD0B0 = 0;
S3C_VIDW01ADD0B1 = 0;
S3C_VIDW02ADD0 = 0;
S3C_VIDW03ADD0 = 0;
S3C_VIDW04ADD0 = 0;
// S3C_VIDW00ADD1B0 = S3C_VIDWxxADD1_VBASEL_F((unsigned int)(lcdbase) + fb_size);
S3C_VIDW00ADD1B0 = virt_to_phys((unsigned int)(lcdbase) + fb_size);
S3C_VIDW00ADD1B1 = 0;
S3C_VIDW01ADD1B0 = 0;
S3C_VIDW01ADD1B1 = 0;
S3C_VIDW02ADD1 = 0;
S3C_VIDW03ADD1 = 0;
S3C_VIDW04ADD1 = 0;
S3C_VIDW00ADD2 = S3C_VIDWxxADD2_OFFSIZE_F(0) | (S3C_VIDWxxADD2_PAGEWIDTH_F(panel_info.vl_col*panel_info.vl_bpix/8));
S3C_VIDW01ADD2 = 0;
S3C_VIDW02ADD2 = 0;
S3C_VIDW03ADD2 = 0;
S3C_VIDW04ADD2 = 0;
//(12)WxKEYCONx:色键寄存器
//(13)WINxMAP:窗口颜色控制
//(14)WPALCON:调色板控制
//(15)WxPDATAxx:调色板数据
#if 1
memset(lcdbase,0x55,fb_size);//增加一个底色
#else
pp = lcdbase;
for(i=0;i< S3CFB_HRES * S3CFB_VRES;i++)
{
*pp = 0xf100;
pp++;
}
#endif
lcd_Enable();
S3C_WINCON0 |= S3C_WINCONx_ENWIN_F_ENABLE;
return (0);
}
这样屏上应该有显示了,下面仔细根据屏的类型调整宏机构就可以了。(完)