我对linux理解之framebuffer

2350阅读 0评论2011-10-22 amingriyue
分类:LINUX

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:amingriyue.blog.chinaunix.net
------------------------------------------

我们看下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操作。


上一篇:我对linux理解之spi
下一篇:我对linux理解之v4l2