LCD驱动笔记
在linux当中,把整个LCD驱动分为两层:LCD帧缓冲区层和LCD硬件驱动层。LCD帧缓冲区层其实就是将内核中的一部分空间当作一个字符型设备,通过操作字符型设备的接口函数就可以操作这段帧缓冲区。而LCD硬件驱动层是对LCD硬件的初始化,LCD控制器在硬件驱动层被看作一个平台设备
LCD帧缓冲区层对应的文件是fbmem.c ,LCD硬件驱动层对应的文件是S32440fb.c
驱动的入口函数
1:向内核申请一段大小为sizeof(struct fb_info) + size的空间,并分配一个fb_info
s3c_lcd =framebuffer_alloc(0, NULL); //
2:设置
/*2.1设置固定参数*/
strcpy(s3c_lcd->fix.id,"mylcd"); // 名字
s3c_lcd->fix.smem_len= 240*320*16/8; //整个屏幕占用空间的大小
s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS; //充满整个空间的像素
s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR; /* TFT */ // 真彩色
s3c_lcd->fix.line_length= 240*2; //行长度
/*2.2 设置可变的参数 */
s3c_lcd->var.xres = 240; //真实的分辨率
s3c_lcd->var.yres = 320;
s3c_lcd->var.xres_virtual = 240; //虚拟分辨率
s3c_lcd->var.yres_virtual = 320;
s3c_lcd->var.bits_per_pixel= 16; //数据位多少位
/*RGB:565 */ 16位三原色
s3c_lcd->var.red.offset = 11; //11-15是表示红色
s3c_lcd->var.red.length = 5; //红色五位
s3c_lcd->var.green.offset = 5; //5-10表示绿色
s3c_lcd->var.green.length = 6; //绿色六位
s3c_lcd->var.blue.offset = 0; //0-4表示蓝色
s3c_lcd->var.blue.length = 5; //
s3c_lcd->var.activate = FB_ACTIVATE_NOW; //立即设置值
/* 2.4 其他的设置*/
s3c_lcd->pseudo_palette =pseudo_palette;
//s3c_lcd->screen_base = ; /*显存的虚拟地址 */
s3c_lcd->screen_size = 240*324*16/8;
/* 3. 硬件相关的操作*/
/* 3.1 配置GPIO用于LCD */
gpbcon = ioremap(0x56000010, 8); //GPB控制寄存机虚拟地址映射
gpbdat = gpbcon+1; //GPB数据寄存机虚拟地址映射
gpccon = ioremap(0x56000020, 4); GBC控制寄存机虚拟地址映射
gpdcon = ioremap(0x56000030, 4); //GPD控制寄存机虚拟地址映射
gpgcon = ioremap(0x56000060, 4); //GPG控制寄存机虚拟地址映射
*gpccon = 0xaaaaaaaa; /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND*/
*gpdcon = 0xaaaaaaaa; /* GPIO管脚用于VD[23:8] */
*gpbcon &= ~(3); /* GPB0设置为输出引脚*/
*gpbcon |= 1;
*gpbdat &= ~1; /* 输出低电平*/
*gpgcon |= (3<<8); /* GPG4用作LCD_PWREN */
/*3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */
lcd_regs= ioremap(0x4D000000, sizeof(struct lcd_regs));
/*bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2], LCD手册P14
* 10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2]
* CLKVAL = 4
* bit[6:5]: 0b11, TFT LCD
* bit[4:1]: 0b1100, 16 bpp for TFT
* bit[0] : 0 = Disable the video output and the LCD control signal.
*/
lcd_regs->lcdcon1 = (4<<8) | (3<<5) |(0x0c<<1);
#if 1
/*垂直方向的时间参数
* bit[31:24]: VBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册T0-T2-T1=4
* VBPD=3
* bit[23:14]: 多少行,320, 所以LINEVAL=320-1=319
* bit[13:6] : VFPD, 发出最后一行数据之后,再过多长时间才发出VSYNC
* LCD手册T2-T5=322-320=2,所以VFPD=2-1=1
* bit[5:0] : VSPW, VSYNC信号的脉冲宽度,LCD手册T1=1, 所以VSPW=1-1=0
*/
lcd_regs->lcdcon2 = (3<<24) | (319<<14) |(1<<6) | (0<<0);
/*水平方向的时间参数
* bit[25:19]: HBPD, VSYNC之后再过多长时间才能发出第1行数据
* LCD手册T6-T7-T8=17
* HBPD=16
* bit[18:8]: 多少列,240, 所以HOZVAL=240-1=239
* bit[7:0] : HFPD, 发出最后一行里最后一个象素数据之后,再过多长时间才发出HSYNC
* LCD手册T8-T11=251-240=11,所以HFPD=11-1=10
*/
lcd_regs->lcdcon3= (16<<19) | (239<<8) | (10<<0);
/*水平方向的同步信号
* bit[7:0] :HSPW, HSYNC信号的脉冲宽度,LCD手册T7=5, 所以HSPW=5-1=4
*/
lcd_regs->lcdcon4= 4;
#else
lcd_regs->lcdcon2 = S3C2410_LCDCON2_VBPD(5) | \
S3C2410_LCDCON2_LINEVAL(319)| \
S3C2410_LCDCON2_VFPD(3)| \
S3C2410_LCDCON2_VSPW(1);
lcd_regs->lcdcon3 = S3C2410_LCDCON3_HBPD(10) | \
S3C2410_LCDCON3_HOZVAL(239)| \
S3C2410_LCDCON3_HFPD(1);
lcd_regs->lcdcon4 = S3C2410_LCDCON4_MVAL(13) | \
S3C2410_LCDCON4_HSPW(0);
#endif
/*信号的极性
* bit[11]: 1=565 format
* bit[10]: 0 = The video data is fetched atVCLK falling edge
* bit[9] : 1 = HSYNC信号要反转,即低电平有效
* bit[8] : 1 = VSYNC信号要反转,即低电平有效
* bit[6] : 0 = VDEN不用反转
* bit[3] : 0 = PWREN输出0
* bit[1] : 0 = BSWP
* bit[0] : 1 = HWSWP 2440手册P413
*/
lcd_regs->lcdcon5= (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);
/*3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 */
s3c_lcd->screen_base= dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len,&s3c_lcd->fix.smem_start, GFP_KERNEL); //分配一个320*240*16/8的显存
lcd_regs->lcdsaddr1 = (s3c_lcd->fix.smem_start >> 1)& ~(3<<30); //告诉LCD控制器起始地址
lcd_regs->lcdsaddr2 = ((s3c_lcd->fix.smem_start +s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;
//告诉LCD控制器结束地址
lcd_regs->lcdsaddr3 = (240*16/16); /* 一行的长度(单位: 2字节)*/
//s3c_lcd->fix.smem_start= xxx; /* 显存的物理地址*/
/*启动LCD */
lcd_regs->lcdcon1|= (1<<0); /* 使能LCD控制器 */
lcd_regs->lcdcon5|= (1<<3); /* 使能LCD本身 */
*gpbdat|= 1; /* 输出高电平, 使能背光 */
/*4. 注册 */
register_framebuffer(s3c_lcd);
出口函数
staticvoid lcd_exit(void)
{
unregister_framebuffer(s3c_lcd);
lcd_regs->lcdcon1&= ~(1<<0); /* 关闭LCD本身 */
*gpbdat&= ~1; /* 关闭背光 */
dma_free_writecombine(NULL,s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start); //释放显存
iounmap(lcd_regs);
iounmap(gpbcon);
iounmap(gpccon);
iounmap(gpdcon);
iounmap(gpgcon);
framebuffer_release(s3c_lcd); //释放fb_info结构体
}
调色板
/* from pxafb.c */
static inline unsigned intchan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan&= 0xffff;
chan>>= 16 - bf->length;
returnchan << bf->offset;
}
static int s3c_lcdfb_setcolreg(unsigned intregno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
unsignedint val;
if(regno > 16)
return1;
/*用red,green,blue三原色构造出val */
val = chan_to_field(red, &info->var.red);
val|= chan_to_field(green, &info->var.green);
val|= chan_to_field(blue, &info->var.blue);
//((u32*)(info->pseudo_palette))[regno] = val;
pseudo_palette[regno]= val;
return0;
}