修改三星平台的fb支持我们的单板上的fb,只需要很少的修改。
1.6个时序参数修改
下面仔细说一下fb_videomode各个成员的意义:
名称 | 在数据手册中的简称 | 中文名 | 意义 | 备注 |
name | No | 名字 | 液晶屏名字(可选) | No |
refresh | No | 刷新频率 | 刷新频率(内核中很多例子都赋值为60) | No |
xres | No | 行宽 | 每行的像素个数 | No |
yres | No | 屏幕高度 | 屏幕的行数 | No |
pixclock | No | 像素时钟 | 每个像素时钟周期的长度,单位是皮秒(10的负12次方分之1秒) | No |
left_margin | HBP (Horizontal Back Porch) | 水平后沿 | 在每行或每列的象素数据开始输出时要插入的象 素时钟周期数 |
No |
right_margin | HFP (Horizontal Front Porch ) | 水平前沿 | 在每行或每列的象素结束到LCD 行时钟输出脉冲 之间的象素时钟数 |
No |
upper_margin | VBP (Vertical Back Porch) | 垂直后沿 | 在垂直同步周期之后帧开头时的无效行数 | No |
lower_margin | VFP (Vertical Front Porch) | 垂直前沿 | 本帧数据输出结束到下一帧垂直同步周期开始之 前的无效行数 |
No |
hsync_len | HPW (HSYNC plus width) | 行同步脉宽 | 单位:像素时钟周期 | 也有手册简称为HWH(HSYNC width) |
vsync_len | VPW (VSYNC width) | 垂直同步脉宽 | 单位:显示一行的时间th | 也有手册简称为VWH(VSYNC width) |
sync | No | 同步极性设置 | 可以根据需要设置FB_SYNC_HOR_HIGH_ACT(水平同步高电平有效)和FB_SYNC_VERT_HIGH_ACT(垂直同步高电平有效) | No |
vmode | No | No | 在内核中的大多数示例都直接置为FB_VMODE_NONINTERLACED。interlaced的意思是交错[隔行]扫描,电视中使用2:1的交错率, 即每帧分两场,垂直扫描两次,一场扫描奇数行,另一场扫描偶数行。很显然LCD目前不是这种模式。 | No |
flag | No | No | 目前没有看到用法 | No |
说明:
(1)Linux对LCD的抽象是以图像为中心的,而LCD手册则以同步信号为中心,所以内核中的left_margin是指在每一行之前(前面自然对应左边)的空闲周期数,而它对应LCD数据手册中的水平后沿(HBP Horizontal Back Porch),是指在行同步信号之后的空闲周期。参照物不同而已,但是说的是同一个东西。
(2)水平同步信号有时也成为行同步型号,垂直同步信号有人称为场同步信号。
(3)对于LCD的frambuffer抽象模型请参考内核中的文档:Documention/fb/frambuffer.txt。
会议一下我们上一节的代码
/**
* s3c_fb_set_rgb_timing() - set video timing for rgb interface.
* @sfb: The base resources for the hardware.
*
* Set horizontal and vertical lcd rgb interface timing.
*/
static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb)
{
struct fb_videomode *vmode = sfb->pdata->vtiming;
void __iomem *regs = sfb->regs;
int clkdiv;
u32 data;
if (!vmode->pixclock)
s3c_fb_missing_pixclock(vmode);
clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock);
data = sfb->pdata->vidcon0;
data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
if (clkdiv > 1)
data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
else
data &= ~VIDCON0_CLKDIR; /* 1:1 clock */
if (sfb->variant.is_2443)
data |= (1 << 5);
writel(data, regs + VIDCON0);
data = VIDTCON0_VBPD(vmode->upper_margin - 1) |
VIDTCON0_VFPD(vmode->lower_margin - 1) |
VIDTCON0_VSPW(vmode->vsync_len - 1);
writel(data, regs + sfb->variant.vidtcon);
data = VIDTCON1_HBPD(vmode->left_margin - 1) |
VIDTCON1_HFPD(vmode->right_margin - 1) |
VIDTCON1_HSPW(vmode->hsync_len - 1);
writel(data, regs + sfb->variant.vidtcon + 4);
data = VIDTCON2_LINEVAL(vmode->yres - 1) |
VIDTCON2_HOZVAL(vmode->xres - 1) |
VIDTCON2_LINEVAL_E(vmode->yres - 1) |
VIDTCON2_HOZVAL_E(vmode->xres - 1);
writel(data, regs + sfb->variant.vidtcon + 8);
}
注意上面
VBPD对应upper_margin
VFPD对应lower_margin
VSPW对应>vsync_len
HBPD对应left_margin
HFPD对应right_margin
HSPW对应hsync_len
再查看我们的lcd的手册
即可得到我们需要的时间参数:
修改arch/arm/mach-s5pv210/mach-smdkv210.c
static struct s3c_fb_pd_win smdkv210_fb_win0 = {
.max_bpp = 32,
.default_bpp = 24,
.xres = 1024,
.yres = 600,
};
static struct fb_videomode smdkv210_lcd_timing = {
.left_margin = 140,
.right_margin = 160,
.upper_margin = 20,
.lower_margin = 12,
.hsync_len = 20,
.vsync_len = 3,
.xres = 1024,
.yres = 600,
};
接下来看我们的四个时钟极性参数
首先是我们SOC的默认NORMAL情况下的时钟极性
VDEN在高电平传输数据
HSYNC在第电平传输一行H数据
VSYNC在低电平传输以屏V数据
VCLK对比我的话的线,可以看到,数据传输是在时钟的下降沿传输(上升沿改变)
接下来看一下LCD的芯片手册上的时序
DE信号是在高电平期间传输数据(对应SOC的VDEN)
HSYNC信号在高电平期间传输一行数据
VSYNC信号在高电平期间传输一屏数据
DCLK信号在上升沿传输数据(对应SOC的VCLK)见下图
对比发现,HSYNC、VSYNC、DCLK都是需要反转的。
但后面我经过几天的调试,后来发现DCLK不需要反转,反转了反而不能显示,不反转才可以显示。
最终的极性参数如下:(在vidcon1中设置)
因为我的是RGB的lcd屏,所以vidcon0的参数不用做改动。
static struct s3c_fb_platdata smdkv210_lcd0_pdata __initdata = {
.win[0] = &smdkv210_fb_win0,
.vtiming = &smdkv210_lcd_timing,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = s5pv210_fb_gpio_setup_24bpp,
};
配置支持三星的fb驱动
make menuconfig
Device Drivers --->
Graphics support --->
Frame buffer Devices --->
<*> Support for frame buffer devices --->
<*> Samsung S3C framebuffer support
[*] Backlight & LCD device support --->
<*> Lowlevel LCD controls
<*> Platform LCD controls
<*> Lowlevel Backlight controls
<*> Generic (aka Sharp Corgi) Backlight Driver
< > Generic GPIO based Backlight Driver
支持背光和三星的驱动
上一节我们简单看了lcd的背光支持,这里再把原始代码列出来看一下。
static void smdkv210_lte480wv_set_power(struct plat_lcd_data *pd,
unsigned int power)
{
if (power) {
#if !defined(CONFIG_BACKLIGHT_PWM) /* 没配置PWM调光才用GPIO作背光控制 */
gpio_request_one(S5PV210_GPD0(3), GPIOF_OUT_INIT_HIGH, "GPD0");
gpio_free(S5PV210_GPD0(3));
#endif
/* fire nRESET on power up */
gpio_request_one(S5PV210_GPH0(6), GPIOF_OUT_INIT_HIGH, "GPH0");
gpio_set_value(S5PV210_GPH0(6), 0);
mdelay(10);
gpio_set_value(S5PV210_GPH0(6), 1);
mdelay(10);
gpio_free(S5PV210_GPH0(6));
} else {
#if !defined(CONFIG_BACKLIGHT_PWM)
gpio_request_one(S5PV210_GPD0(3), GPIOF_OUT_INIT_LOW, "GPD0");
gpio_free(S5PV210_GPD0(3));
#endif
}
}
static struct plat_lcd_data smdkv210_lcd_lte480wv_data = {
.set_power = smdkv210_lte480wv_set_power, /* 背光设置函数 */
};
static struct platform_device smdkv210_lcd_lte480wv = {
.name = "platform-lcd",
.dev.parent = &s3c_device_fb.dev, /* 背光依赖于lcd */
.dev.platform_data = &smdkv210_lcd_lte480wv_data,
};
下面看一下我们的原理图,所用的背光引脚
我们是GPD0_0引脚,同时我们是低电平点亮(划重点),
三星默认的是GPD0_3引脚,高电平点亮,所以我们要修改掉。
同时我们的LCD也没有引出复位引脚,所以三星的LCD相关的复位操作我们就可以注释掉。
最终代码如下:(加入了两行调试代码,确定背光引脚的电平)
static void smdkv210_lte480wv_set_power(struct plat_lcd_data *pd,
unsigned int power)
{
if (power) {
#if !defined(CONFIG_BACKLIGHT_PWM)
gpio_request_one(S5PV210_GPD0(0), GPIOF_OUT_INIT_LOW, "GPD0");
gpio_free(S5PV210_GPD0(0));
printk(KERN_INFO"GPD0_LOW###################################\n");
#endif
#if 0
/* fire nRESET on power up */
gpio_request_one(S5PV210_GPH0(6), GPIOF_OUT_INIT_HIGH, "GPH0");
gpio_set_value(S5PV210_GPH0(6), 0);
mdelay(10);
gpio_set_value(S5PV210_GPH0(6), 1);
mdelay(10);
gpio_free(S5PV210_GPH0(6));
#endif
} else {
#if !defined(CONFIG_BACKLIGHT_PWM)
gpio_request_one(S5PV210_GPD0(0), GPIOF_OUT_INIT_HIGH, "GPD0");
gpio_free(S5PV210_GPD0(0));
printk(KERN_INFO"GPD0_HIGH###################################\n");
#endif
}
}
下载后,背光引脚拉低了,但屏幕上无任何反应(背光都没亮起来)
因为背光和时序之类无关,所以肯定是硬件那里没设置对。
再次检查原理图,发现还有一个DISP显示相关的引脚没初始化。
查看数据手册,可以看到SYS_OE是输出使能。查lcd手册,确定到底是高电平,还是低电平使能显示。
查看LCD的原理图
SYS_OE接到来LCD的L_DISP接口。
最终可以看法,它最终是作为LCD背光芯片的是能引脚,高电平有效。所以我们要设置它为SYS_OE模式或初始化为高电平。
因为三星并没有初始化中引脚,所以我在上面是能背光的地方初始化这个引脚。(这里我们删掉了原来没用到的复位,添加了我们现在的背光芯片使能)
static void smdkv210_lte480wv_set_power(struct plat_lcd_data *pd,
unsigned int power)
{
if (power) {
#if !defined(CONFIG_BACKLIGHT_PWM)
gpio_request_one(S5PV210_GPD0(0), GPIOF_OUT_INIT_LOW, "GPD0");
gpio_free(S5PV210_GPD0(0));
printk(KERN_INFO"GPD0_LOW###################################\n");
#endif
/* backlight enable pin */
// gpio_request_one(S5PV210_GPF3(5), GPIOF_OUT_INIT_LOW, "GPF3_5");
//gpio_free(S5PV210_GPF3(5));
s3c_gpio_cfgpin(S5PV210_GPF3(5), S3C_GPIO_SFN(3));
s5p_gpio_set_drvstr(S5PV210_GPF3(5), S5P_GPIO_DRVSTR_LV4);
} else {
#if !defined(CONFIG_BACKLIGHT_PWM)
gpio_request_one(S5PV210_GPD0(0), GPIOF_OUT_INIT_HIGH, "GPD0");
gpio_free(S5PV210_GPD0(0));
printk(KERN_INFO"GPD0_HIGH###################################\n");
#endif
}
}
此时重新编译,下载,背光就亮起来了。
接下来我们在里面随便写一些数据,看能都显示内容。因为fb,占用空间比较大,我们就写我们的uImage本身。
发现是有花屏显示,说明我们的lcd至少驱动成功了。
我们计算一下刷满一屏需要多少字节数据。
1024*600*4 = 2457600字节 = 2.3M
因为uImage大小为2M大,所以不能t填充满一屏。
接下来看一下我们能不能正常显示字符等。
这里我们就显示linux自带的logo图片。
Device Drivers --->
Graphics support --->
Frame buffer Devices --->
<*> Support for frame buffer devices --->
<*> Samsung S3C framebuffer support
[*] Backlight & LCD device support --->
<*> Lowlevel LCD controls
<*> Platform LCD controls
<*> Lowlevel Backlight controls
<*> Generic (aka Sharp Corgi) Backlight Driver
< > Generic GPIO based Backlight Driver
Console display driver support --->
<*> Framebuffer Console support
[*] Bootup logo --->
编译,重新下载启动,可以正常看到linux的企鹅logo
最后我们把LCD显示屏作为一个控制台的输出设备。
因为上面我们已经配置了Consol display 驱动支持,所以只需要在启动文件inittab中添加,支持tty就可以
最终启动后可以看到下面的打印信息。
最后我们再次通过控制台安装我们在输入子系统章节写的驱动,来把这个显示屏作为真正的一个控制台输出。
最终可以看到通过输入子系统章节的输驱动的几个按键,可以作为一个输入,并显示在lcd的屏幕上。
达到了和我们的串口作为标准输入和输出相同的效果。