------------------------------------------
本文系本站原创,欢迎转载!
------------------------------------------我们看下imx51的lcd控制器的驱动:
int __init mxcfb_init(void)
{
int ret;
ret = platform_driver_register(&mxcfb_driver);
if (!ret)
register_early_suspend(&fbdrv_earlysuspend);
return ret;
}
mxcfb_driver定义如下:
static struct platform_driver mxcfb_driver = {
.driver = {
.name = MXCFB_NAME,
},
.probe = mxcfb_probe,
.remove = mxcfb_remove,
.suspend = mxcfb_suspend,
.resume = mxcfb_resume,
};
我们知道这里platform匹配后将会执行mxcfb_probe:
static int mxcfb_probe(struct platform_device *pdev)
{
struct fb_info *fbi;
struct mxcfb_info *mxcfbi;
struct mxc_fb_platform_data *plat_data = pdev->dev.platform_data;
struct resource *res;
char *options;
char name[] = "mxcdi0fb";
int ret = 0;
/*
* Initialize FB structures
*/
fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);//这边涉及到ops的赋值,这个很重要,后面framebuffer子系统中好多都用到相应的ops操作
if (!fbi) {
ret = -ENOMEM;
goto err0;
}
mxcfbi = (struct mxcfb_info *)fbi->par;
name[5] += pdev->id;
if (fb_get_options(name, &options))//从启动命令里面得到options
return -ENODEV;
if (options)
mxcfb_option_setup(fbi, options);//根据得到的options设置信息
if (!g_dp_in_use) {
mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
mxcfbi->ipu_ch = MEM_BG_SYNC;
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK;
} else {
mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
mxcfbi->ipu_ch = MEM_DC_SYNC;
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
}
mxcfbi->ipu_di = pdev->id;
if (pdev->id == 0) {//对应fb0
ipu_disp_set_global_alpha(mxcfbi->ipu_ch, true, 0x80);
ipu_disp_set_color_key(mxcfbi->ipu_ch, false, 0);
strcpy(fbi->fix.id, "DISP3 BG");
if (!g_dp_in_use)
if (ipu_request_irq(IPU_IRQ_BG_ALPHA_SYNC_EOF,
mxcfb_alpha_irq_handler, 0,
MXCFB_NAME, fbi) != 0) {
dev_err(&pdev->dev, "Error registering BG "
"alpha irq handler.\n");
ret = -EBUSY;
goto err1;
}
g_dp_in_use = true;
} else if (pdev->id == 1) {//对应fb1
strcpy(fbi->fix.id, "DISP3 BG - DI1");
if (!g_dp_in_use)
if (ipu_request_irq(IPU_IRQ_BG_ALPHA_SYNC_EOF,
mxcfb_alpha_irq_handler, 0,
MXCFB_NAME, fbi) != 0) {
dev_err(&pdev->dev, "Error registering BG "
"alpha irq handler.\n");
ret = -EBUSY;
goto err1;
}
g_dp_in_use = true;
} else if (pdev->id == 2) { /* 对应Overlay */
mxcfbi->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF;
mxcfbi->ipu_ch = MEM_FG_SYNC;
mxcfbi->ipu_di = -1;
mxcfbi->overlay = true;
mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN;
strcpy(fbi->fix.id, "DISP3 FG");
if (ipu_request_irq(IPU_IRQ_FG_ALPHA_SYNC_EOF,
mxcfb_alpha_irq_handler, 0,
MXCFB_NAME, fbi) != 0) {
dev_err(&pdev->dev, "Error registering FG alpha irq "
"handler.\n");
ret = -EBUSY;
goto err1;
}
}
mxcfb_info[pdev->id] = fbi;
if (ipu_request_irq(mxcfbi->ipu_ch_irq, mxcfb_irq_handler, 0,//注册通道中断的处理函数
MXCFB_NAME, fbi) != 0) {
dev_err(&pdev->dev, "Error registering BG irq handler.\n");
ret = -EBUSY;
goto err1;
}
ipu_disable_irq(mxcfbi->ipu_ch_irq);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res && res->end) {//i.mx51 fb device没有resource定义,故不会执行
fbi->fix.smem_len = res->end - res->start + 1;
fbi->fix.smem_start = res->start;
fbi->screen_base =
ioremap(fbi->fix.smem_start, fbi->fix.smem_len);
}
/* Need dummy values until real panel is configured */
fbi->var.xres = 240;
fbi->var.yres = 320;
if (!mxcfbi->default_bpp)
mxcfbi->default_bpp = 16;
if (plat_data && !mxcfbi->ipu_di_pix_fmt)
mxcfbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
if (plat_data && plat_data->mode && plat_data->num_modes)//将plat_data中的videomode都添加到fbi的modelist中
fb_videomode_to_modelist(plat_data->mode, plat_data->num_modes,
&fbi->modelist);
if (!mxcfbi->fb_mode_str && plat_data && plat_data->mode_str)
mxcfbi->fb_mode_str = plat_data->mode_str;//赋值mode_str
if (mxcfbi->fb_mode_str) {
ret =
fb_find_mode(&fbi->var, fbi, mxcfbi->fb_mode_str, NULL, 0,//按照之前赋值的mode_str寻找对应的videomode
NULL, mxcfbi->default_bpp);
if ((!ret || (ret > 2)) && plat_data && plat_data->mode
&& plat_data->num_modes)
fb_find_mode(&fbi->var, fbi, mxcfbi->fb_mode_str,//到platdata里面找对应的videomode,并赋值到fbi->var
plat_data->mode, plat_data->num_modes,
NULL, mxcfbi->default_bpp);
}
mxcfb_check_var(&fbi->var, fbi);
/* Default Y virtual size is 2x panel size */
fbi->var.yres_virtual = ((fbi->var.yres + 127) & ~127) * 2;//y是128对齐
fbi->var.xres_virtual = (fbi->var.xres + 31) & ~31;//x是32对齐
mxcfb_set_fix(fbi);
/* alocate fb first */
if (!res || !res->end)
if (mxcfb_map_video_memory(fbi) < 0)//给framebuffer申请内存
return -ENOMEM;
ret = register_framebuffer(fbi);//注册framebuffer
if (ret < 0)
goto err2;
platform_set_drvdata(pdev, fbi);
ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_property);//添加fsl_disp_property属性文件
if (ret)
dev_err(&pdev->dev, "Error %d on creating file\n", ret);
return 0;
err2:
ipu_free_irq(mxcfbi->ipu_ch_irq, fbi);
err1:
fb_dealloc_cmap(&fbi->cmap);
framebuffer_release(fbi);
err0:
return ret;
}
这里先看下mxcfb_ops:
static struct fb_ops mxcfb_ops = {
.owner = THIS_MODULE,
.fb_set_par = mxcfb_set_par,//设置参数
.fb_check_var = mxcfb_check_var,//检测变量参数
.fb_setcolreg = mxcfb_setcolreg,//设置颜色
.fb_pan_display = mxcfb_pan_display,//显示函数,用于buffer切换显示
.fb_ioctl = mxcfb_ioctl,//设置函数
.fb_mmap = mxcfb_mmap,//将内核空间映射到用户空间
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_blank = mxcfb_blank,//blank
};
我们下面看下register_framebuffer(fbi):
int
register_framebuffer(struct fb_info *fb_info)
{
......
fb_info->node = i;
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock);
fb_info->dev = device_create(fb_class, fb_info->device,//在android中,fb_class的名字是graphics
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
if (IS_ERR(fb_info->dev)) {
/* Not fatal */
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
fb_info->dev = NULL;
} else
fb_init_device(fb_info);
if (fb_info->pixmap.addr == NULL) {
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
if (fb_info->pixmap.addr) {
fb_info->pixmap.size = FBPIXMAPSIZE;
fb_info->pixmap.buf_align = 1;
fb_info->pixmap.scan_align = 1;
fb_info->pixmap.access_align = 32;
fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
}
}
fb_info->pixmap.offset = 0;
if (!fb_info->pixmap.blit_x)
fb_info->pixmap.blit_x = ~(u32)0;
if (!fb_info->pixmap.blit_y)
fb_info->pixmap.blit_y = ~(u32)0;
if (!fb_info->modelist.prev || !fb_info->modelist.next)
INIT_LIST_HEAD(&fb_info->modelist);
fb_var_to_videomode(&mode, &fb_info->var);
fb_add_videomode(&mode, &fb_info->modelist);//添加到modelist中
registered_fb[i] = fb_info;
event.info = fb_info;
if (!lock_fb_info(fb_info))
return -ENODEV;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);//通知fb通知链有fb注册
unlock_fb_info(fb_info);
return 0;
}
我们看到register_framebuffer主要注册了设备节点,添加videomode和发送注册通知等工作。我们知道fb归根结底是一个字符设备,那我们下面看下它的初始化:
static int __init
fbmem_init(void)
{
proc_create("fb", 0, NULL, &fb_proc_fops);//创建proc下的读写节点
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");//创建graphic类
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;
}
注册字符设备时有个fb_fops:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
};
上层打开设备后,就可以利用这里的ops与驱动联系起来工作了,这里ioctl用的最频繁:
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx];
return do_fb_ioctl(info, cmd, arg);
}
转到do_fb_ioctl(info, cmd, arg):
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct fb_ops *fb;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_con2fbmap con2fb;
struct fb_cmap cmap_from;
struct fb_cmap_user cmap;
struct fb_event event;
int status;
void __user *argp = (void __user *)arg;
long ret = 0;
switch (cmd) {
......
case FBIOPAN_DISPLAY://显示命令
if (copy_from_user(&var, argp, sizeof(var)))
return -EFAULT;
if (!lock_fb_info(info))
return -ENODEV;
acquire_console_sem();
ret = fb_pan_display(info, &var);//执行显示函数
release_console_sem();
unlock_fb_info(info);
if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
return -EFAULT;
break;
......
default:
if (!lock_fb_info(info))
return -ENODEV;
fb = info->fbops;
if (fb->fb_ioctl)
ret = fb->fb_ioctl(info, cmd, arg);
else
ret = -ENOTTY;
unlock_fb_info(info);
}
return ret;
}
它会根据cmd的不同执行相应的操作,我们以FBIOPAN_DISPLAY显示命令为例,它会执行fb_pan_display(info, &var):
int
fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
{
struct fb_fix_screeninfo *fix = &info->fix;
unsigned int yres = info->var.yres;
int err = 0;
......
if ((err = info->fbops->fb_pan_display(var, info)))//转到ops的显示函数
return err;
info->var.xoffset = var->xoffset;
info->var.yoffset = var->yoffset;
if (var->vmode & FB_VMODE_YWRAP)//设置vmode标记
info->var.vmode |= FB_VMODE_YWRAP;
else
info->var.vmode &= ~FB_VMODE_YWRAP;
return 0;
}
我们由上面的mxcfb_ops定义知道,fb_pan_display对应于mxcfb_pan_display。这样就对应到具体的驱动函数了。
由上面知道,framebuffer子系统还是比较简单的,framebuffer core是一个字符设备驱动,lcd控制器向其中注册,同时实现framebuffer必须要的ops操作。