该接口时序协议和MPU接口保持一致,内部采用VSYNC 信号实现frame同步显示,比MPU多了一个VSYNC同步信号
在DVI模式下只用到了数据线LCD_DATA00~LCD_DATA07 和一个时钟线
1、framebuffer帧缓冲
帧缓冲(framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都是由帧缓冲设备驱动本身来完成。
framebuffer机制模仿显卡的功能,将显卡硬件结构抽象为一系列的数据结构,可以通过对framebuffer的读写直接对显存进行操作。用户可以将framebuffer看成是显卡的一个映像,将其映射到进程空间后,就可以直接读写操作,写操作会直接反映在屏幕上。
framebuffer是一个字符设备,主设备号是29,对应于/dev/fb%d设备文件。通常,使用如下方式(数字代表次设备号)
0 = /dev/fb0 第一个fb设备
1 = /dev/fb1 第二个fb设备
fb也是一种普通的内存设备,可以读写其内容。例如,屏幕抓屏:cp /dev/fb0 myfilefb 虽然可以向内存设备(/dev/mem)一样,对其read、write、seek以及mmap。但区别在于fb使用的不是整个内存区。而是显存部分。
2、fb与应用程序的互交
对于应用程序而言,它和其它的设备并没有什么区别,用户可以把fb看成是一块内存,既可以向内存中写数据,也可以读数据。fb的显示缓冲区位于内核空间。应用程序可以把此空间映射到自己的用户空间,再进行操作。
在应用程序中,操作/dev/fbn的一般步骤如下:
(1)打开/dev/fbn设备文件。
(2)用ioctl()操作取得当前显示屏幕的参数,如屏幕的分辨率、每个像素点的比特数。根据屏幕的参数可计算屏幕缓冲区的大小。
(3)用mmap()函数,将屏幕缓冲区映射到用户空间。
(4)映射后就可以直接读/写屏幕缓冲区,进行绘图和图片显示。
framebuffer涉及的数据结构如下:
(1)struct fb_info
一个帧缓冲区对应一个struct fb_info结构,它包括了帧缓冲设备的属性和操作的完整集合,每个帧设备都有一个fb_info结构体。
(2)struct fb_ops
结构体用来实现对帧缓冲设备的操作,这些函数需要驱动开发人员编写,
(3)struct fb_fix_screeninfo
该结构体记录了用户不能修改的固定显示控制器参数。这些固定的参数如缓冲区的物理地址、缓冲区的长度等等。
(4)struct fb_var_screeninfo
结构体中存储了用户可以修改的显示器控制参数,例如屏幕分辨率、透明度等等。
(5)struct fb_cmap
结构体中记录了颜色板信息,即调色板信息。,用户空间可以通过ioctl()的FBIOGETCMAP和 FBIOPUTCMAP命令读取或设定颜色表
(6)struct fb_bitfield
结构体描述每一像素显示缓冲区的组织方式,包含位域偏移、位域长度和MSB 指示。
framebuffer设备在Linux中是以平台设备形式存在的,fb设备驱动核心层为上层提供了系统调用,为底层驱动提供了接口,核心层的主文件及其功能如下。
(1)drivers/video/fbmem.c。主要任务:1、创建graphics类、注册FB的字符设备驱动、提供register_framebuffer接口给具体framebuffer驱动编写着来注册fb设备的。本文件相对于fb来说,地位和作用和misc.c文件相对于杂散类设备来说一样的,结构和分析方法也是类似的。
(2)drivers/video/fbsys.c。这个文件是处理fb在/sys目录下的一些属性文件的。
(3)drivers/video/modedb.c。这个文件是管理显示模式(譬如VGA、720P等就是显示模式)的
(4)drivers/video/fb_notify.c
fbmem_init函数
(1)#ifdef MODULE
(2)fb_proc_fops和fb在proc文件系统中的表现
(3)register_chrdev注册fb设备
(4)class_create创建graphics类
(5)fbmem_exit的对应
fb_fops
(1)为应用层实现read/write/mmap/ioctl在驱动层的接口
(2)registered_fb和num_registered_fb
(3)struct fb_info register_framebuffer函数
(1)fb驱动框架开放给驱动编写着的注册接口
(2)fb_check_foreignness
(3)remove_conflicting_framebuffers
(4)device_create
(5)fb_init_device
fb在sysfs中的接口
(1)device_attrs
(2)dev_set_drvdata和dev_get_drvdata
注册登记该fb设备
(1)registered_fb[i] = fb_info;
(2)结合fb_read等函数中对fb_info的使用
(3)关键点:数据如何封装、数据由谁准备由谁消费、数据如何传递
static int imxfb_probe(struct platform_device *pdev)
{
struct imxfb_info *fbi;
struct lcd_device *lcd;
struct fb_info *info;
struct imx_fb_platform_data *pdata;
struct resource *res;
struct imx_fb_videomode *m;
const struct of_device_id *of_id;
int ret, i;
int bytes_per_pixel;
dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
:::::::://一些资源初始化
//申请framebuff空间
info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
if (!info)
return -ENOMEM;
fbi = info->par;
platform_set_drvdata(pdev, info);
ret = imxfb_init_fbinfo(pdev);//主要填充部分屏参数变量 同时设置可操作函数
if (ret < 0)
goto failed_init;
::::::::::::::::::::::::
/* Calculate maximum bytes used per pixel. In most cases this should
* be the same as m->bpp/8 */
*
//计算smem_len 的长度
m = &fbi->mode[0];
bytes_per_pixel = (m->bpp + 7) / 8;//像素所占字节
for (i = 0; i < fbi->num_modes; i++, m++)
info->fix.smem_len = max_t(size_t, info->fix.smem_len,
m->mode.xres * m->mode.yres * bytes_per_pixel);//一帧总共有多少个字节
res = request_mem_region(res->start, resource_size(res),//申请内存
DRIVER_NAME);
if (!res) {
ret = -EBUSY;
goto failed_req;
}
fbi->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(fbi->clk_ipg)) {
ret = PTR_ERR(fbi->clk_ipg);
goto failed_getclock;
}
fbi->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(fbi->clk_ahb)) {
ret = PTR_ERR(fbi->clk_ahb);
goto failed_getclock;
}
fbi->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(fbi->clk_per)) {
ret = PTR_ERR(fbi->clk_per);
goto failed_getclock;
}
fbi->regs = ioremap(res->start, resource_size(res));//内存映射
if (fbi->regs == NULL) {
dev_err(&pdev->dev, "Cannot map frame buffer registers\n");
ret = -ENOMEM;
goto failed_ioremap;
}
fbi->map_size = PAGE_ALIGN(info->fix.smem_len);//申请DMA的空间
info->screen_base = dma_alloc_writecombine(&pdev->dev, fbi->map_size,
&fbi->map_dma, GFP_KERNEL);
if (!info->screen_base) {
dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto failed_map;
}
info->fix.smem_start = fbi->map_dma;
if (pdata && pdata->init) {
ret = pdata->init(fbi->pdev);
if (ret)
goto failed_platform_init;
}
INIT_LIST_HEAD(&info->modelist);
for (i = 0; i < fbi->num_modes; i++)//设置video的模式
fb_add_videomode(&fbi->mode[i].mode, &info->modelist);
/*
* This makes sure that our colour bitfield
* descriptors are correctly initialised.
*/
imxfb_check_var(&info->var, info);
ret = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0);
if (ret < 0)
goto failed_cmap;
imxfb_set_par(info);//设置LCD的具体参数
ret = register_framebuffer(info);//注册frame buff
if (ret < 0) {
dev_err(&pdev->dev, "failed to register framebuffer\n");
goto failed_register;
}
fbi->lcd_pwr = devm_regulator_get(&pdev->dev, "lcd");
if (IS_ERR(fbi->lcd_pwr) && (PTR_ERR(fbi->lcd_pwr) == -EPROBE_DEFER)) {
ret = -EPROBE_DEFER;
goto failed_lcd;
}
lcd = devm_lcd_device_register(&pdev->dev, "imxfb-lcd", &pdev->dev, fbi,
&imxfb_lcd_ops);//lcd设备注册
if (IS_ERR(lcd)) {
ret = PTR_ERR(lcd);
goto failed_lcd;
}
lcd->props.max_contrast = 0xff;
imxfb_enable_controller(fbi);//使能控制器
fbi->pdev = pdev;
return 0;
failed_lcd:
unregister_framebuffer(info);
failed_register:
fb_dealloc_cmap(&info->cmap);
failed_cmap:
if (pdata && pdata->exit)
pdata->exit(fbi->pdev);
failed_platform_init:
dma_free_writecombine(&pdev->dev, fbi->map_size, info->screen_base,
fbi->map_dma);
failed_map:
iounmap(fbi->regs);
failed_ioremap:
failed_getclock:
release_mem_region(res->start, resource_size(res));
failed_req:
failed_of_parse:
kfree(info->pseudo_palette);
failed_init:
framebuffer_release(info);
return ret;
}
LCD设备管理功能
static struct lcd_ops imxfb_lcd_ops = {
.check_fb = imxfb_lcd_check_fb,//frame buff状态检测
.get_contrast = imxfb_lcd_get_contrast,//获取颜色偏差
.set_contrast = imxfb_lcd_set_contrast,//设置颜色偏差
.get_power = imxfb_lcd_get_power,//获取电源状态
.set_power = imxfb_lcd_set_power,//设置电源状态
};
具体画图功能实现函数集
static struct fb_ops imxfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = imxfb_check_var,//检查变量设置是否越界
.fb_set_par = imxfb_set_par,//设置LCD参数变量
.fb_setcolreg = imxfb_setcolreg,//调试功能
.fb_fillrect = cfb_fillrect,//矩形填充
.fb_copyarea = cfb_copyarea,//颜色区域拷贝
.fb_imageblit = cfb_imageblit,//图像填充
.fb_blank = imxfb_blank,//LCD控制器使能和关闭
};