一、S3C6410 LCD驱动裸机代码
LCD控制器初始化:
1 unsigned long VideoBuffer[LCD_LOW][LCD_COL] = {0};
2 void lcd_init(void)
3 {
4 /* 1.初始化IO端口为LCD端口 */
5 /* GPIO configure */
6 GPICON = 0xAAAAAAAA;
7 GPJCON = 0x00AAAAAA;
8
9 /* 2.使能LCD时钟 */
10 //HCLK_GATE |= (1 << 3); //默认打开
11
12 MIFPCON &=~(1 << 3);
13
14 /* 3.设置I/F类型 */
15 SPCON &=~(3 << 0);
16 SPCON |= (1 << 0);
17
18 VIDCON0 = (CLK_DIV << 6) | (1 << 4) | (1 << 1) | (1 << 0);
19 VIDCON1 |= (1 << 6) | (1 << 5);
20
21 /* 4.LCD控制器相关寄存器配置 */
22 VIDTCON0 = (VBPD << 16) | (VFPD << 8) | (VSPW << 0);
23 VIDTCON1 = (HBPD << 16) | (HFPD << 8) | (HSPW << 0);
24 VIDTCON2 = (LINEVAL << 11) | (HOZVAL << 0);
25
26 WINCON0 |= (0xB << 2) | (1 << 0);
27 VIDOSD0A = (0 << 11) | (0 << 0);
28 VIDOSD0B = (HOZVAL << 11) | (LINEVAL << 0);
29 VIDOSD0C = LCD_LOW * LCD_COL;
30
31 VIDW00ADD0B0 = ((unsigned long)VideoBuffer);
32 VIDW00ADD1B0 = ((unsigned long)VideoBuffer + LCD_LOW * LCD_COL * 4) & 0xFFFFFF;
33 }
LCD控制器驱动程序有几个重要步骤:
1、初始化IO端口为LCD端口,通过GPICON和GPJCON将相应的为设置成LCD VD[n]模式。
2、使能LCD时钟,HCLK_GATE寄存器默认是打开的,所以不需要设置。
3、设置I/F类型,根据芯片手册上的描述,需要设置SPCON寄存器把I/F模式设置成RGB I/F style。
4、LCD控制器相关寄存器配置,这一步是根据LCD规格书设置时序相关的时间参数,包括水平时间参数和垂直时间参数,下面是我所使用的LCD时间参数:
根据这张图可以确定VSPW的值即为VS pulse width的值,这里取个中间值10。
VFPD的值即为VS Front Porch的值,取典型值22。
VBPD的值没有明确给出,但是通过下图可以知道VBPD = tvb - tvpw,从上图中可以知道tvb就是VS Blanking等于23,tvpw为VSPW = 10,所以可以确定VBPD = 23 - 10。
水平方向这三个方向的分析和垂直方向是一样的,最终得出的数据如下:
1 #define VBPD (23 - 10 - 1) //tvb - tvpw = tvb - VSPW
2 #define VFPD (22 - 1)
3 #define VSPW (10 - 1)
4 #define HBPD (46 - 20 - 1) //thb - thpw = thb - HSPW
5 #define HFPD (210 - 1)
6 #define HSPW (20 - 1)
7 #define LINEVAL (480 - 1)
8 #define HOZVAL (800 - 1)
LINEVAL为屏幕垂直方向的像素,HOZVAL为屏幕水平方向的像素。在芯片手册上可以看到下面的描述,所以上面的参数都要进行减一操作。
最后VIDW00ADD0B0和VIDW00ADD1B0寄存器指定了LCD缓冲区。
二、Linux内核LCD设备驱动
Linux为LCD显示设备提供了一个帧缓冲接口,所谓的帧缓冲其实就是想裸机代码一样在内存中分配一段空间,这段空间就描述了整个屏幕像素点的信息,往缓冲区中与现实点对应的位置写入颜色值,对应的颜色就会在LCD上显示。帧缓冲设备实质上是一个字符设备,主设备号29,对应于/dev/fbn设备文件。
上面简单概括了LCD控制器裸机程序相关寄存器的操作,下面看看Linux内核里面是怎样实现LCD设备驱动的。
Linux内核LCD设备驱动程序在s3cfb.c文件里面,从s3cfb.c里面的module_init()函数入手,找到平台驱动结构体里面的s3cfb_probe()函数,在之前的博客中已经提到,分析平台设备驱动首先要分析的函数就是probe函数。进入s3cfb_probe()函数中,找到了一个s3cfb_init_fbinfo()函数,这个函数又调用了s3cfb_init_hw()函数,在s3cfb_init_hw()函数中调用了s3cfb_set_fimd_info()函数和s3cfb_set_gpio()函数,s3cfb_set_fimd_info()函数中初始化了一个s3cfb_fimd_info_t结构体,里面存储的数据就包括上面裸机程序里面时序相关的时间参数,这个结构体里面包含着所有的LCD控制器相关的参数。s3cfb_set_gpio()函数部分代码如下:
1 int s3cfb_set_gpio(void)
2 {
3 ...
4
5 /* 2.使能LCD时钟 */
6 /* enable clock to LCD */
7 val = readl(S3C_HCLK_GATE);
8 val |= S3C_CLKCON_HCLK_LCD;
9 writel(val, S3C_HCLK_GATE);
10
11 /* 3.设置I/F类型 */
12 /* select TFT LCD type (RGB I/F) */
13 val = readl(S3C64XX_SPCON);
14 val &= ~0x3;
15 val |= (1 << 0);
16 writel(val, S3C64XX_SPCON);
17
18 /* 1.初始化IO端口为LCD端口 */
19 /* VD */
20 for (i = 0; i < 16; i++)
21 s3c_gpio_cfgpin(S3C64XX_GPI(i), S3C_GPIO_SFN(2));
22
23 for (i = 0; i < 12; i++)
24 s3c_gpio_cfgpin(S3C64XX_GPJ(i), S3C_GPIO_SFN(2));
25
26 ...
27
28 return 0;
29 }
在s3cfb_init_registers()函数里面进行了裸机操作的第四步操作,s3cfb_fimd结构体里面的数据就来源于上面所说的s3cfb_set_fimd_info()函数。
最后调用了register_framebuffer()函数注册了一个帧缓冲设备,剩下的事情就交给内核来完成了。