uboot下有现成的LCD驱动模板,原来是用于MPC823和PXA250的,我们需要在s3c6410这个ARM11的uboot中增加LCD的驱动,可以在这个基础上修改。具体分以下几步: 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 #include /***************************************************************************//* 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 { 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 { *pp = 0xf100; pp++; }#endif lcd_Enable(); S3C_WINCON0 |= S3C_WINCONx_ENWIN_F_ENABLE; return (0);} 这样屏上应该有显示了,下面仔细根据屏的类型调整宏机构就可以了。