从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

时间:2024-04-06 09:02:10

修改三星平台的fb支持我们的单板上的fb,只需要很少的修改。

 

1.6个时序参数修改

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

 

下面仔细说一下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的手册

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的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情况下的时钟极性

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

VDEN在高电平传输数据

HSYNC在第电平传输一行H数据

VSYNC在低电平传输以屏V数据

VCLK对比我的话的线,可以看到,数据传输是在时钟的下降沿传输(上升沿改变)

 

接下来看一下LCD的芯片手册上的时序

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

DE信号是在高电平期间传输数据(对应SOC的VDEN)

HSYNC信号在高电平期间传输一行数据

VSYNC信号在高电平期间传输一屏数据

DCLK信号在上升沿传输数据(对应SOC的VCLK)见下图

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

对比发现,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,
};

 

下面看一下我们的原理图,所用的背光引脚

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

我们是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
    }
}

 

下载后,背光引脚拉低了,但屏幕上无任何反应(背光都没亮起来)

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

 

因为背光和时序之类无关,所以肯定是硬件那里没设置对。

再次检查原理图,发现还有一个DISP显示相关的引脚没初始化。

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

查看数据手册,可以看到SYS_OE是输出使能。查lcd手册,确定到底是高电平,还是低电平使能显示。

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

 

查看LCD的原理图

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

SYS_OE接到来LCD的L_DISP接口。

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

最终可以看法,它最终是作为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本身。

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

 

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

发现是有花屏显示,说明我们的lcd至少驱动成功了。

 

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的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

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

 

最后我们把LCD显示屏作为一个控制台的输出设备。

因为上面我们已经配置了Consol display 驱动支持,所以只需要在启动文件inittab中添加,支持tty就可以

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

最终启动后可以看到下面的打印信息。

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

 

 

最后我们再次通过控制台安装我们在输入子系统章节写的驱动,来把这个显示屏作为真正的一个控制台输出。

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

从零开始之驱动发开、linux驱动(二十七、修改三星平台framebuffer参数支持我们的LCD)

最终可以看到通过输入子系统章节的输驱动的几个按键,可以作为一个输入,并显示在lcd的屏幕上。

 

达到了和我们的串口作为标准输入和输出相同的效果。