如果只是简单的显示图片或字符,smdkv210 uboot增加LCD显示(一)中的方法就可以了,但是要使用console的话
就有另一种方法,主要参考u-boot-2010.09 for mini6410 (add LCD support )
http://blog.chinaunix.net/uid-20543672-id-94391.html
还有u-boot-2009.08在mini2440上的移植(七)---增加LCD显示功能
http://blog.163.com/liuqiang_mail@126/blog/static/10996887520117622235857/
其实要在U-boot中添加LCD支持其实很简单,只要你的CPU支持类似framebuffer的机制,就只要添加一个初始化LCD 控制器和一个GraphicDevice *pGD结构体的代码就基本可以了。
下图是LCD驱动软件分层执行流程示意。可以看到LCD在执行时最终调用的是底层的board_video_init()函数,其主要作用是对LCD控制寄存器进行初始化。可由用户根据实际LCD硬件编写。
(1)在/drivers/video/下添加一个驱动文件名为s5pc110_fb.c,将下面内容粘贴进去:
#include <common.h>
#if defined(CONFIG_VIDEO_S5PC110)
#include <video_fb.h>
#include "videomodes.h"
#include <regs-fb.h>
#include <s5pc110.h>
/*
* Export Graphic Device
*/
GraphicDevice smi;
#define VIDEO_MEM_SIZE 0x200000 /*NEC 4.3 inches: 480x272x16bit = 0x3FC00 bytes 7 inches:800*480*4bytes = 0x177000 bytes*/
//CPU: S5pc110@1000MHz
// Fclk = 1000MHz, Hclk = 200MHz, Pclk = 100MHz (ASYNC Mode)
extern void board_video_init(GraphicDevice *pGD);
/*******************************************************************************
*
* Init video chip with common Linux graphic modes (lilo)
*/
void *video_hw_init (void)
{
// s3c64xx_fb * const fb = s3c64xx_get_base_fb();
GraphicDevice *pGD = (GraphicDevice *)&smi;
int videomode;
unsigned long t1, hsynch, vsynch;
char *penv;
int tmp, i, bits_per_pixel;
struct ctfb_res_modes *res_mode;
struct ctfb_res_modes var_mode;
int clkval;
// unsigned char videoout;
/* Search for video chip */
printf("Video: ");
tmp = 0;
videomode = CONFIG_SYS_DEFAULT_VIDEO_MODE;
/* get video mode via environment */
if ((penv = getenv ("videomode")) != NULL) {
/* deceide if it is a string */
if (penv[0] <= '9') {
videomode = (int) simple_strtoul (penv, NULL, 16);
tmp = 1;
}
} else {
tmp = 1;
}
if (tmp) {
/* parameter are vesa modes */
/* search params */
for (i = 0; i < VESA_MODES_COUNT; i++) {
if (vesa_modes[i].vesanr == videomode)
break;
}
if (i == VESA_MODES_COUNT) {
printf ("no VESA Mode found, switching to mode 0x%x ", CONFIG_SYS_DEFAULT_VIDEO_MODE);
i = 0;
}
res_mode =
(struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].
resindex];
bits_per_pixel = vesa_modes[i].bits_per_pixel;
} else {
res_mode = (struct ctfb_res_modes *) &var_mode;
bits_per_pixel = video_get_params (res_mode, penv);
}
/* calculate hsynch and vsynch freq (info only) */
t1 = (res_mode->left_margin + res_mode->xres +
res_mode->right_margin + res_mode->hsync_len) / 8;
t1 *= 8;
t1 *= res_mode->pixclock;
t1 /= 1000;
hsynch = 1000000000L / t1;
t1 *=
(res_mode->upper_margin + res_mode->yres +
res_mode->lower_margin + res_mode->vsync_len);
t1 /= 1000;
vsynch = 1000000000L / t1;
/* fill in Graphic device struct */
sprintf (pGD->modeIdent, "%dx%dx%d %ldkHz %ldHz", res_mode->xres,
res_mode->yres, bits_per_pixel, (hsynch / 1000),
(vsynch / 1000));
printf ("%s\n", pGD->modeIdent);
pGD->winSizeX = res_mode->xres;
pGD->winSizeY = res_mode->yres;
pGD->plnSizeX = res_mode->xres;
pGD->plnSizeY = res_mode->yres;
switch (bits_per_pixel) {
case 8:
pGD->gdfBytesPP = 1;
pGD->gdfIndex = GDF__8BIT_INDEX;
break;
case 15:
pGD->gdfBytesPP = 2;
pGD->gdfIndex = GDF_15BIT_555RGB;
break;
case 16:
pGD->gdfBytesPP = 2;
pGD->gdfIndex = GDF_16BIT_565RGB;
break;
case 24:
pGD->gdfBytesPP = 3;
pGD->gdfIndex = GDF_24BIT_888RGB;
break;
case 32:
pGD->gdfBytesPP = 4;
pGD->gdfIndex = GDF_32BIT_X888RGB;
break;
}
#if 0
/* statically configure settings for debug*/
pGD->winSizeX = pGD->plnSizeX = 480;
pGD->winSizeY = pGD->plnSizeY = 272;
pGD->gdfBytesPP = 2;
pGD->gdfIndex = GDF_16BIT_565RGB;
#endif
pGD->frameAdrs = LCD_VIDEO_ADDR; //in config file :include/configs/mini6410.h
pGD->memSize = VIDEO_MEM_SIZE;
/* Clear video memory */
memset((void *)pGD->frameAdrs, 0x00, pGD->memSize);
board_video_init(pGD); //in board init file :board/samsung/mini6410/mini6410.c for gpio etc.
t1 = res_mode->pixclock;
t1 /= 1000;
t1 = 1000000000L / t1;
clkval = (CONFIG_SYS_VIDEO_VCLOCK_HZ / t1) - 1;
// clkval = 1; //for debug
VIDCON0 = ( VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB_P |
VIDCON0_CLKVALUP_ALWAYS | VIDCON0_CLKVAL_F(clkval)|
VIDCON0_CLKDIR_DIVIDED| VIDCON0_CLKSEL_HCLK );
VIDCON1 = ( VIDCON1_IVSYNC_INVERT | VIDCON1_IHSYNC_INVERT);
VIDTCON0 = ( VIDTCON0_VBPD(res_mode->upper_margin) |
VIDTCON0_VFPD(res_mode->lower_margin) |
VIDTCON0_VSPW(res_mode->vsync_len));
VIDTCON1 = ( VIDTCON1_HBPD(res_mode->left_margin) |
VIDTCON1_HFPD(res_mode->right_margin) |
VIDTCON1_HSPW(res_mode->hsync_len));
VIDTCON2 = (VIDTCON2_LINEVAL(pGD->winSizeY - 1) | VIDTCON2_HOZVAL(pGD->winSizeX - 1));
#if defined(LCD_VIDEO_BACKGROUND)
WINCON0 = (WINCON_BPPMODE_16BPP_565 | WINCON_ENWIN_ENABLE | WINCON_HAWSWP_ENABLE);
VIDOSD0A = (VIDOSD_LEFT_X(0) | VIDOSD_TOP_Y(0));
VIDOSD0B = (VIDOSD_RIGHT_X(pGD->winSizeX - 1) | VIDOSD_BOTTOM_Y(pGD->winSizeY - 1));
VIDOSD0C = VIDOSD_SIZE(pGD->winSizeY * pGD->winSizeX);
#endif
WINCON1 = (WINCON_BPPMODE_16BPP_565 | WINCON_ENWIN_ENABLE | WINCON_HAWSWP_ENABLE);
VIDOSD1A = (VIDOSD_LEFT_X(0) | VIDOSD_TOP_Y(0));
VIDOSD1B = (VIDOSD_RIGHT_X(pGD->winSizeX - 1) | VIDOSD_BOTTOM_Y(pGD->winSizeY - 1));
#if defined(LCD_VIDEO_BACKGROUND)
VIDOSD1C = (VIDOSD_ALPHA0_R(LCD_VIDEO_BACKGROUND_ALPHA) |
VIDOSD_ALPHA0_G(LCD_VIDEO_BACKGROUND_ALPHA) |
VIDOSD_ALPHA0_B(LCD_VIDEO_BACKGROUND_ALPHA) );
#endif
VIDOSD1D = VIDOSD_SIZE(pGD->winSizeY * pGD->winSizeX);
WINSHMAP = WINSHMAP_CH_ENABLE(1); //Enables Channel 1
#if defined(LCD_VIDEO_BACKGROUND)
/* config Display framebuffer addr for background*/
VIDW00ADD0B0 = LCD_VIDEO_BACKGROUND_ADDR;
/* This marks the end of the frame buffer. */
VIDW00ADD1B0 = (VIDW00ADD0B0 &0xffffffff) + (pGD->winSizeX+0) * pGD->winSizeY * (pGD->gdfBytesPP);
VIDW00ADD2= ((pGD->winSizeX * pGD->gdfBytesPP) & 0x1fff);
#endif
/* config Display framebuffer addr for console*/
VIDW01ADD0B0 = pGD->frameAdrs;
/* This marks the end of the frame buffer. */
VIDW01ADD1B0 = (VIDW01ADD0B0 &0xffffffff) + (pGD->winSizeX+0) * pGD->winSizeY * (pGD->gdfBytesPP);
VIDW01ADD2= ((pGD->winSizeX * pGD->gdfBytesPP) & 0x1fff);
/* Enable Display */
VIDCON0 |= (VIDCON0_ENVID_ENABLE | VIDCON0_ENVID_F_ENABLE); /* ENVID = 1 ENVID_F = 1*/
TRIGCON = (TRGMODE_I80 | SWTRGCMD_I80); //TRIGCON = 3
/* clear background
u16 p,q;
u32* pBuffer = (u32*)pGD->frameAdrs;
for (p=0; p < pGD->winSizeX; p++)
{
for (q=0; q < pGD->winSizeY; q++)
{
*pBuffer++ = 0x555555;
}
} */
printf("Video: video_hw_init complete \n");
return ((void*)&smi);
}
void
video_set_lut (unsigned int index, /* color number */
unsigned char r, /* red */
unsigned char g, /* green */
unsigned char b /* blue */
)
{
}
#endif /* CONFIG_VIDEO_S5PC110 */
(2)打开/drivers/video/Makefile,定位到
COBJS-y += videomodes.o
COBJS-y +=s5pc110_fb.o
COBJS := $(COBJS-y)
(3)修改/drivers/video/cfb_console.c,我直接使用了tekkaman ninja的源码
(4)打开/drivers/video/videomodes.c,修改如下:
{0x31A, RES_MODE_1280x1024, 16},
{0x31B, RES_MODE_1280x1024, 24},
{0x211, RES_MODE_240x320, 16},
{0x212, RES_MODE_800x480, 16},
};
const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = {
/* x y pixclk le ri up lo hs vs s vmode */
{640, 480, 39721, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED},
{800, 600, 27778, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED},
{1024, 768, 15384, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED},
{960, 720, 13100, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED},
{1152, 864, 12004, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED},
{1280, 1024, 9090, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED},
{240, 320, 90000, 1, 4, 1, 1, 30, 4, 0, FB_VMODE_NONINTERLACED},
{800, 480, 15151, 16, 48, 6, 4, 32, 0, 0, FB_VMODE_NONINTERLACED},
};
(5)打开/drivers/video/videomodes.h,修改如下:
//#ifndef CONFIG_SYS_DEFAULT_VIDEO_MODE
//#define CONFIG_SYS_DEFAULT_VIDEO_MODE 0x301
#ifndef CFG_SYS_DEFAULT_VIDEO_MODE
#define CFG_SYS_DEFAULT_VIDEO_MODE 0x212
#endif
#define RES_MODE_1280x1024 5
#define RES_MODE_240x320 6
#define RES_MODE_800x480 7
#define RES_MODES_COUNT 8
//#define VESA_MODES_COUNT 19
#define VESA_MODES_COUNT 21
extern const struct ctfb_vesa_modes vesa_modes[];
(6)打开board/samsung/smdkc110/smdkc110.c,修改如下
int board_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
// S5PC11X_GPIO * const gpio = S5PC11X_GetBase_GPIO();
//init gpio func for LCD
writel(0x10000000, GPBCON); //GPBCON [31:28]:output [27:0]:input
writel(0x1555,GPBPUD);//GPBPUD GPBPUD[7]:Pull-up/ down disabled GPBPUD[6:0]:Pull-down enabled
writel(0xc000,GPBDRV_SR);//GPBDRV GPBDRV[7]:4x GPBDRV[6:0]:1x
writel(0x10010000,GPBCON);//GPBCON [31:28],[19:16]:output [27:20],[15:0]:input
writel(0x1455,GPBPUD);//GPBPUD GPBPUD[7],[4]:Pull-up/ down disabled ,GPBPUD[6:5][3:0]:Pull-down enabled
writel(0xc300,GPBDRV_SR);//GPBDRV GPBDRV[7],[4]:4x GPBDRV[6:5][3:0]:1x
writel(0x10110000,GPBCON);//GPBCON [31:28],[23:20],[19:16]:output [27:24],[15:0]:input
writel(0x1055,GPBPUD);//GPBPUD GPBPUD[7],[5][4]:Pull-up/ down disabled ,GPBPUD[6][3:0]:Pull-down enabled
writel(0xcf00,GPBDRV_SR);//GPBDRV GPBDRV[7],[5],[4]:4x GPBDRV[6][3:0]:1x
writel(0x1,GPD1CON);//GPD1CON [23:4]:input [3:0]:output
writel(0x54,GPD1PUD);//GPD1PUD GPD1PUD[5:4],[0]:Pull-up/ down disabled ,GPBPUD[3:1]:Pull-down enabled
writel(0x3,GPD1DRV);//GPD1DRV GPD1DRV[0]:4x GPBDRV[5:1]:1x
writel(0x11,GPD1CON);//GPD1CON [23:8]:input [7:0]:output
writel(0x50,GPD1PUD);//GPD1PUD GPD1PUD[5:4],[1:0]:Pull-up/ down disabled ,GPBPUD[3:2]:Pull-down enabled
writel(0xf,GPD1DRV);//GPD1DRV GPD1DRV[1:0]:4x GPBDRV[5:2]:1x
writel(0x1001,GPD0CON);//GPD0CON GPD0CON[3],[0]:output GPD0CON[2:1]:input
writel(0x15,GPD0PUD);//GPD0PUD GPD0PUD[3]:Pull-up/ down disabled,GPD0PUD[2:0]:Pull-down enabled
writel(0xc0,GPD0DRV);//GPD0DRV GPD0DRV[3]:4x,GPD0DRV[2:0]:1x
writel(0x1000010,GPH0CON);// GPH0CON GPH0CON[6],[1]:output,GPH0CON[7],[5:2],[0]:input
writel(0x4455,GPH0PUD);// GPH0PUD GPH0PUD[6],[4]:Pull-up/ down disabled GPH0PUD[7],[5],[4:0]:Pull-down enabled
writel(0x3000,GPH0DRV);// GPH0DRV GPH0DRV[6]:4x GPH0DRV[7],[5:0]:1x
writel(0x11110000,GPBCON);//GPBCON [31:16]:output [15:0]:input
writel(0x55,GPBPUD);//GPBPUD GPBPUD[7:4]:Pull-up/ down disabled GPBPUD[3:0]:Pull-down enabled
writel(0xff00,GPBDRV_SR);//GPBDRV GPBDRV[7:4]:4x GPBDRV[3:0]:1x
writel(0x11110100,GPBCON);//GPBCON [31:16],[11:8]:output [15:12],[7:0]:input
writel(0x55,GPBPUD);//GPBPUD GPBPUD[7:4]:Pull-up/ down disabled GPBPUD[3:0]:Pull-down enabled
writel(0xff00,GPBDRV_SR);//GPBDRV GPBDRV[7:4]:4x GPBDRV[3:0]:1x
writel(0x80,GPBDAT);//GPBDAT GPBDAT[7]=1,GPBDAT[6:0]=0
writel(0x98,GPBDAT);//GPBDAT GPBDAT[7],[4:3]=1,GPBDAT[6:5],[2:0]=0
writel(0xb9,GPBDAT);//GPBDAT GPBDAT[7],[5:3],[0]=1,GPBDAT[6],[2:1]=0
writel(0xbb,GPBDAT);//GPBDAT GPBDAT[7],[5:3],[1:0]=1,GPBDAT[6],[2]=0
writel(0xbb,GPBDAT);//GPBDAT GPBDAT[7],[5:3],[1:0]=1,GPBDAT[6],[2]=0
writel(0xd,GPD0DAT);//GPD0DAT GPD0DAT[3:2],[0]=1,GPD0DAT[1]=0
writel(0xd1,GPH0DAT);//GPH0DAT[7:6],[4],[0]=1,GPH0DAT[5],[3:1]=0
writel(0xfb,GPBDAT);//GPBDAT GPBDAT[7:3],[1:0]=1,GPBDAT[2]=0
writel(0xff,GPBDAT);//GPBDAT GPBDAT[7:0]=1
writel(0x91,GPH0DAT);//GPH0DAT[7],[4],[0]=1,GPH0DAT[6:5],[3:1]=0
writel(0xd1,GPH0DAT);//GPH0DAT[7:6],[4],[0]=1,GPH0DAT[5],[3:1]=0
writel(0xd3,GPH0DAT);//GPH0DAT[7:6],[4],[1:0]=1,GPH0DAT[5],[3:2]=0
writel(0x22222222,GPF0CON);//GPF0CON set GPF0[0:7] as HSYNC,VSYNC,VDEN,VCLK,VD[0:3]
writel(0x0,GPF0PUD);//GPF0PUD set pull-up,down disable
writel(0x22222222,GPF1CON);//set GPF1CON[7:0] as VD[11:4]
writel(0x0,GPF1PUD);//GPF1PUD set pull-up,down disable
writel(0x22222222,GPF2CON);//set GPF2CON[7:0] as VD[19:12]
writel(0x0,GPF2PUD);//GPF2PUD set pull-up,down disable
writel(0x00002222,GPF3CON);//set GPF3CON[3:0] as VD[23:20]
writel(0x0,GPF3PUD);//GPF3PUD set pull-up,down disable
//--------- S5PC110 EVT0 needs MAX drive strength---------//
writel(0xffffffff,GPF0DRV);//set GPF0DRV drive strength max by WJ.KIM(09.07.17)
writel(0xffffffff,GPF1DRV);//set GPF1DRV drive strength max by WJ.KIM(09.07.17)
writel(0xffffffff,GPF2DRV);//set GPF2DRV drive strength max by WJ.KIM(09.07.17)
writel(0x3ff,GPF3DRV);//set GPF3DRV drive strength max by WJ.KIM(09.07.17)
//init gpio func for MMC
......
}
//just init some reg for enable LCD
void board_video_init(GraphicDevice *pGD)
{
DISPLAY_CONTROL_REG = 0x2;//DISPLAY_CONTROL output path RGB=FIMD I80=FIMD ITU=FIMD
CLK_SRC1_REG = 0x700000; //CLK_SRC1 fimdclk = EPLL
}
(7)修改配置文件/include/configs/smdkv210.h如下:
#if 1
//enable LCD display
#define CONFIG_CMD_BMP
#define CONFIG_VIDEO
#define CONFIG_VIDEO_S5PC110
#define CONFIG_VIDEO_LOGO
#define VIDEO_FB_16BPP_WORD_SWAP //for BMP logo
#define CONFIG_VIDEO_SW_CURSOR
#define CONFIG_VIDEO_BMP_LOGO
//#define CONFIG_CONSOLE_EXTRA_INFO
//#define CONFIG_CONSOLE_CURSOR
//#define CONFIG_CONSOLE_TIME
#define CONFIG_CFB_CONSOLE
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
//#define CFG_CONSOLE_INFO_QUIET
//#define VIDEO_FB_LITTLE_ENDIAN
#define CONFIG_SPLASH_SCREEN
#define CFG_VIDEO_LOGO_MAX_SIZE (1024*768+1024+100) /* 100 = slack */
#define CONFIG_SYS_VIDEO_LOGO_MAX_SIZE (1024*768+1024+100) /* 100 = slack */
#define CONFIG_VIDEO_BMP_GZIP
#define CONFIG_CMD_UNZIP
#define LCD_VIDEO_ADDR (0x48000000)//0x57a00000
//#define LCD_VIDEO_BACKGROUND
#define CONFIG_VGA_AS_SINGLE_DEVICE
#define DEBUG_CFB_CONSOLE
#if defined(LCD_VIDEO_BACKGROUND)
#define LCD_VIDEO_BACKGROUND_ADDR (0x57900000)
#define LCD_VIDEO_BACKGROUND_LOADADDR (0x57700000)
#define LCD_VIDEO_BACKGROUND_LOADSIZE (0x40000)
#define LCD_VIDEO_BACKGROUND_ALPHA (0xa)
#define LCD_VIDEO_BACKGROUND_IN_NAND
#define LCD_VIDEO_BACKGROUND_FLASH_ADDR (0xa0000)
#endif
#define CONFIG_SYS_VIDEO_VCLOCK_HZ (133000000)
//RAM_TEXT = 0x57e00000
#endif /*enable LCD display*/
还有寄存器声明文件s5pc110.h的修改和lcd寄存器配置的文件reg-fb.h代码就不列出来了。
reg-fb.h是从内核代码中拷过来的linux/arch/arm/plat-s5p/include/plat/regs-fb.h。