欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

LCD驱动

程序员文章站 2022-03-05 11:35:05
...

fbmem.c是内核自带的LCD驱动程序,它是抽象出来的,依赖于底层提供的fb_info结构体

fbmem_init(void)
{    
	create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);
	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))		
		printk("unable to get major %d for fb devs\n", FB_MAJOR);
	fb_class = class_create(THIS_MODULE, "graphics");
	if (IS_ERR(fb_class)) {
		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
		fb_class = NULL;
	}
	return 0;
}

因为“fbmem.c”是通用的文件,故并不能直接使用这个 file_operations 结构中的.read 等函数。这里 fbmem.c 没有在设备类下创建设备,只有真正有硬件设备时才有必要在这个类下去创建设备。

app: open("/dev/fb0", …)
info = registered_fb[fbidx]得到这个设备节点的次设备号,从这个数组里面registered_fb[0]得到以次设备号为下标的一项,如果它有open函数就调用它,没有返回。

kernel:
fb_open
	int fbidx = iminor(inode);	//得到次设备号
		struct fb_info *info = = registered_fb[0];    

app: read("/dev/fb0", …)
假设app想读,得到次设备号,以次设备号为下标在数组registered_fb[fbidx]里得到一项,如果这个数组项里提供了fbops,就调用它的读函数。如果没有从screen_base读,src等于显存的基地址加偏移值,从里面读源(显存基地址screen_base)到数据放到目的(buffer)里。

kernel:
fb_read
	int fbidx = iminor(inode);
	struct fb_info *info = registered_fb[fbidx];	//以次设备号为下标从 registered_fd 数组中得一项赋给"info"结构。
		if (info->fbops->fb_read)
			return info->fbops->fb_read(info, buf, count, ppos);
		src = (u32 __iomem *) (info->screen_base + p);				//screen_base 是指显存的基地址。这里是读源 src 等于显存的基地址加上某个偏移值。
		dst = buffer;	
		*dst++ = fb_readl(src++);	//读源(从显存基地址+P偏移)那里读到一个数据放到目标“*dst++”里。dst是buffer,buffer是kmalloc()上面分配的空间。
		copy_to_user(buf, buffer, c)	//把数据拷贝到用户空间    	

open和read都依赖于fb_info结构体(fb是framebuffe帧缓冲区)结构体),这个结构体从registered_fb[fbidx]数组里得到的,以次设备号为下标得到一项。

问1. registered_fb数组在哪里被设置?
答1. register_framebuffer
把fb_info结构体先找到某个空项,找到空项后device_create,在类下面创建设备,之前只创建了类,真正有硬件设备才去创建设备节点。fbmem.c 提供的都是抽象出来的东西。最终都得依赖这个“registered_fb”数组里的“fb_info”结构体。

register_framebuffer(struct fb_info *fb_info)
{	for (i = 0 ; i < FB_MAX; i++)
		if (!registered_fb[i])	//先找出一个空项
			break;
	fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), "fb%d", i);    
}

LCD驱动
怎么写LCD驱动程序?
1 分配fb_info结构体: framebuffer_alloc
2 设置fb_info结构体
3 注册fb_info结构体: register_framebuffer
4 硬件相关的操作

1 分配
framebuffer_alloc(size_t size, struct device *dev)
2 设置

struct fb_info {
	struct fb_var_screeninfo var;	    /* 可变参数 */ 
	struct fb_fix_screeninfo fix;	    /* 固定参数 */  
	struct fb_ops *fbops;	//操作函数
	void *pseudo_palette;	//Fake palette of 16 colors  假调色板};
固定参数
struct fb_fix_screeninfo {
	char id[16];	//名字     
	unsigned long smem_start;	//显存的物理起始地址,分配内存时再设置
	__u32 smem_len;	 //显存长度(看液晶屏)240*320*16 */  
	__u32 type;		// see FB_TYPE_* 宏		默认值	 
	__u32 visual;	// see FB_VISUAL_*  TFT  真彩色
	__u32 line_length;	// 一行的长度240*16/8   
};
可变参数
struct fb_var_screeninfo {
	__u32 xres;		// x方向分辨率240     
	__u32 yres;     //y方向分辨率320
	__u32 xres_virtual;   //虚拟分辨率简单设为和硬件相同  
	__u32 yres_virtual;
	__u32 bits_per_pixel;	//每个像素位数16(2440只支持16浪费2位)
	struct fb_bitfield red;		//红  5
	struct fb_bitfield green;	//绿  6
	struct fb_bitfield blue;	//蓝  5									  
	struct fb_bitfield transp;	///透明度  0};
//RGB 565
struct fb_bitfield {
	__u32 offset;		// 开始位   	
	__u32 length;		// 长度			
	__u32 msb_right;	//最重要位在右边,我们最重要位在左边不设置*/ 
};

显存操作函数
static struct fb_ops s3c_lcdfb_ops = {
	.owner		= THIS_MODULE,
	.fb_setcolreg	= s3c_lcdfb_setcolreg,
	.fb_fillrect	= cfb_fillrect,		//填充一个矩形
	.fb_copyarea	= cfb_copyarea,     //拷贝一个区域
	.fb_imageblit	= cfb_imageblit,
};

其它设置
s3c_lcd->pseudo_palette = pseudo_palette;	//假调色板
s3c_lcd->screen_base  = ;        //显存的虚拟地址
s3c_lcd->screen_size   = 240*324*16/8;    //显存大小

调色板是块内存,像数组一样,里面存放有真正的十六位数据颜色。LCD以内存中8位数据为索引,在调色板中取得16位真正颜色的数据。

3 硬件操作

3.1 配置GPIO用于LCD
3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等
LCDCON1:像素时钟VCLK,LCD类型TFT,BPP 16,使能LCD信号输出
LCDCON2:垂直方向的时间参数和同步信号:VBPD上等待时间,LINEVAL垂直宽度,VFPD下无效时间,VSPW脉冲宽度
LCDCON3:水平方向的时间参数:HBPD左等待时间,HOZVAL垂直宽度,HFPD右无效时间
LCDCON4:水平方向的同步信号:HSPW信号脉冲宽度
LCDCON5:数据格式565,引脚信号的极性,字节交换使能(16bpp:高低字节2143,1234)
帧内存与视口,帧内存很大,真正要显示的区域为视口,它处于帧内存之中,这3个寄存器用于确定帧内存的起始地址,定位视口在帧内存中的位置。
LCDADDR1:bit29:21帧内存起始地址A30:22,4M对齐。bit20:0保存视口所对应内存的起始地址,A21:1这块内存称为帧缓冲区
LCDADDR2:帧缓冲区结束地址A21:1
LCDADDR3:上一行最后和下一行开始地址差值,视口宽度
3.3 分配显存(framebuffer), 并把地址告诉LCD控制器
dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
第一个参数是设备NULL,第二个参数是大小,第三个参数是存放物理地址,第四个参数是标记,返回值是这块内存的虚拟地址s3c_lcd->screen_base。

4 注册
register_framebuffer(s3c_lcd);

相关标签: 驱动