基于imx FrameBuffer 分析

来源:互联网 发布:国家药监局数据库 编辑:程序博客网 时间:2024/04/29 12:48
FrameBuffer代码分析 +--------+ | app | +--------+ | vfs | +--------+ | fbmem | +--------+ | driver | +--------+先看drivers/video/fbmem.c一般作为builtin编入内核,看下模块入口//----...
FrameBuffer代码分析

   +--------+
   |   app  |
   +--------+
   |   vfs  |
   +--------+
   |  fbmem |
   +--------+
   | driver |
   +--------+

先看drivers/video/fbmem.c
一般作为builtin编入内核,看下模块入口
//-------------------------------------start
#ifdef MODULE
module_init(fbmem_init);
...
#else
subsys_initcall(fbmem_init);
#endif
//--------------------------------------end
从而可知入口是fbmem_init函数
//--------------------------------------start
static int __init
fbmem_init(void)
{
    //创建/proc/fb文件,提供给用户查询驱动部分信息的接口
    proc_create("fb", 0, NULL, &fb_proc_fops);

    //注册一个字符设备驱动,占用主设备号 FB_MAJOR,应用程序对所有fb驱动的操作就在这里的fb_fops
    if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
        printk("unable to get major %d for fb devs\n", FB_MAJOR);

    //创建一个设备类目录/sys/class/graphics
    fb_class = class_create(THIS_MODULE, "graphics");
    ...   
    return 0;
}
//--------------------------------------end

我们来看下fb_fops的定义
//--------------------------------------start
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
};
//--------------------------------------end
针对我们应用经常进行的操作,主要的重点是open/close/read/write/mmap/ioctl,首先我们看open操作
//--------------------------------------start
static int                    
fb_open(struct inode *inode, struct file *file)
__acquires(&info->lock)       
__releases(&info->lock)       
{
    //获取次设备号,作为全局数组registered_fb的下表
    int fbidx = iminor(inode);
    struct fb_info *info;     
    int res = 0;              

    if (fbidx >= FB_MAX)      
        return -ENODEV;
    info = registered_fb[fbidx]; 
    //如果没有加载驱动,请求加载驱动  
    if (!info)
        request_module("fb%d", fbidx);
    info = registered_fb[fbidx];   
    ...    
    mutex_lock(&info->lock);  
    ...
    file->private_data = info;
    if (info->fbops->fb_open) {        
        res = info->fbops->fb_open(info,1);
        ...
    }  
...
}
//--------------------------------------end
从代码看出,真实的操作是推迟到具体的设备驱动来实现,而找到正确的设备驱动是通过次设备号作为下标在registered_fb的全局数组中找到跟具体设备驱动相关的数据(sturct fb_info,这个在后面描述),然后调用对应的fb_open函数来进行打开操作

可以看出来fbmem是将vfs的操作转换城具体驱动的实现,从而达到给上层应用对所有fb的一致操作接口,但又能使用具体不同的设备。这个目的是通过registered_fb数组和fb_ops这2个数据来达到的。
通过其他的几个文件操作实现代码也可以验证这一点。
//--------------------------------------start
fb_mmap(struct file *file, struct vm_area_struct * vma)
__acquires(&info->lock)
__releases(&info->lock)
{
    int fbidx = iminor(file->f_path.dentry->d_inode);
    struct fb_info *info = registered_fb[fbidx];
    struct fb_ops *fb = info->fbops;
    ...
    if (fb->fb_mmap) {
        int res;
        mutex_lock(&info->lock);
        res = fb->fb_mmap(info, vma);
        mutex_unlock(&info->lock);
        return res;
    }
    ...
}
//--------------------------------------end
read操作
//--------------------------------------start
static ssize_t
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    unsigned long p = *ppos;
    struct inode *inode = file->f_path.dentry->d_inode;
    int fbidx = iminor(inode);
    struct fb_info *info = registered_fb[fbidx];
    ...
    if (info->fbops->fb_read) 
        return info->fbops->fb_read(info, buf, count, ppos);
    //如果没有对应的fb_write函数,则通过info中的screen_base来直接进行内存操作
}
//--------------------------------------end

write操作
//--------------------------------------start
static ssize_t
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    unsigned long p = *ppos;
    struct inode *inode = file->f_path.dentry->d_inode;
    int fbidx = iminor(inode);
    struct fb_info *info = registered_fb[fbidx];
    ...
    if (info->fbops->fb_write)
        return info->fbops->fb_write(info, buf, count, ppos);
    //如果没有对应的fb_write函数,则通过info中的screen_base来直接进行内存操作
}
//--------------------------------------end

ioctl操作  fb_ioctl--->do_fb_ioctl
//--------------------------------------start

//--------------------------------------end

至于registered_fb数组中的值从哪来的,就牵扯到下面要说的具体设备驱动,这里以iMx515的framebuffer驱动为例<drivers/video/mxc/mxc_ipuv3_fb.c>
看下驱动模块入口
//--------------------------------------start
module_init(mxcfb_init);

static struct platform_driver mxcfb_driver = {
    .driver = {
           .name = MXCFB_NAME,
           },
    .probe = mxcfb_probe,
    .remove = mxcfb_remove,
    .suspend = mxcfb_suspend,
    .resume = mxcfb_resume,
};

int __init mxcfb_init(void)   
{            
    ...
    ret = platform_driver_register(&mxcfb_driver);
}
//--------------------------------------end
这里注册platform_driver_register函数,如果找到匹配的设备,在iMx515中匹配设备的声明在
<arch/arm/mach-mx51/mx51_3stack.c>
//--------------------------------------start
static struct mxc_fb_platform_data fb_data[] = {
    {    
     .interface_pix_fmt = IPU_PIX_FMT_RGB666,
     },   
    {    
     .interface_pix_fmt = IPU_PIX_FMT_YUV444,
     },   
};

static struct platform_device mxc_fb_device[] = {
    {    
     .name = "mxc_sdc_fb",
     .id = 0, 
     .dev = {
         .release = mxc_nop_release,
         .coherent_dma_mask = 0xFFFFFFFF,
         .platform_data = &fb_data[0],
         },   
     },   
    {    
     .name = "mxc_sdc_fb",
     .id = 1, 
     .dev = {
         .release = mxc_nop_release,
         .coherent_dma_mask = 0xFFFFFFFF,
         .platform_data = &fb_data[1],
         },   
     },   
    {    
     .name = "mxc_sdc_fb",
     .id = 2, 
     .dev = {
         .release = mxc_nop_release,
         .coherent_dma_mask = 0xFFFFFFFF,
         },
     },
};
//--------------------------------------end
则进入probe函数
//--------------------------------------start
//驱动具体实现的文件操作函数
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,
    .fb_ioctl = mxcfb_ioctl,  
    .fb_mmap = mxcfb_mmap,
    .fb_fillrect = cfb_fillrect,   
    .fb_copyarea = cfb_copyarea,   
    .fb_imageblit = cfb_imageblit, 
    .fb_blank = mxcfb_blank,  
};

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;
    int ret = 0;

    /*
     * Initialize FB structures
     */
    //通过framebuffer_alloc来分配空间,给info的fb_ops的参数为mxcfb_ops
    fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
    if (!fbi) {
        ret = -ENOMEM;
        goto err0;
    }
    mxcfbi = (struct mxcfb_info *)fbi->par;

    //第一次进这里
    if (!g_dp_in_use) {
        mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
        mxcfbi->ipu_ch = MEM_BG_SYNC;
        mxcfbi->blank = FB_BLANK_UNBLANK;
    } else {
        mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
        mxcfbi->ipu_ch = MEM_DC_SYNC;
        mxcfbi->blank = FB_BLANK_POWERDOWN;
    }

    //fb的索引号
    mxcfbi->ipu_di = pdev->id;
    
    if (pdev->id == 0) {
        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) {
        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->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;
        }
    }

    //在驱动内部保存fb_info的消息,并对应pdev->id的索引
    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);

    //映射显存空间,但从上面的代码块可以看出没有IORESOURCE_MEM资源,显存地址由后面的代码分配
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res) {
        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;

    //具体的LCD相关参数在下面3个步骤里发现
    //1. 从上面那个代码块的定义可知,plat_data->mode_str为空
    if (!fb_mode && plat_data && plat_data->mode_str)
        fb_find_mode(&fbi->var, fbi, plat_data->mode_str, NULL, 0, NULL,
                 default_bpp);
    //2. fb_mode由内核命令行参数传入video=mxcfb:1024x768M-16@60
    if (fb_mode)
        fb_find_mode(&fbi->var, fbi, fb_mode, NULL, 0, NULL,
                 default_bpp);
    //3. 进入这里
    if (plat_data) {
        mxcfbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
        //但下面的plat_data->mode不满足
        if (!fb_mode && plat_data->mode)
            fb_videomode_to_var(&fbi->var, plat_data->mode);
    }

    /* Default Y virtual size is 2x panel size */
    fbi->var.yres_virtual = fbi->var.yres * 2;

    mxcfb_check_var(&fbi->var, fbi);
    //通过上面的查找和检测过程 fb_info的var部分的值被确定下

    /* Default Y virtual size is 2x panel size */
    fbi->var.yres_virtual = fbi->var.yres * 2;

    mxcfb_set_fix(fbi);

    //这里分配显存空间,见后面的实现代码块
    /* alocate fb first */
    if (!res)
        if (mxcfb_map_video_memory(fbi) < 0)
            return -ENOMEM;
    //执行到这里fb_info的fix部分成员值被确定下来
    
    //这里将具体驱动里的fb_info注册到fb_mem中的registered_fb数组中去
    ret = register_framebuffer(fbi);
    if (ret < 0)
        goto err2;

    platform_set_drvdata(pdev, fbi);

    ret = device_create_file(fbi->dev, &dev_attr_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;
}
//--------------------------------------end

上面的显存物理地址分配由如下函数实现
//--------------------------------------start
static int mxcfb_map_video_memory(struct fb_info *fbi)
{
    if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length)
        fbi->fix.smem_len = fbi->var.yres_virtual *
                    fbi->fix.line_length;          

    fbi->screen_base = dma_alloc_writecombine(fbi->device,
                fbi->fix.smem_len,             
                (dma_addr_t *)&fbi->fix.smem_start,
                GFP_DMA);
    ...
    fbi->screen_size = fbi->fix.smem_len;

    /* Clear the screen */    
    memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);

    return 0;
}
//--------------------------------------end

在fb video mode的查找中,对模式的指定字符串格式如下:
<xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or <name>[-<bpp>][@<refresh>]
例子:1024x768MR-8@60m - Reduced blank with margins at 60Hz

在上面函数的最后部分的register_framebuffer函数即将具体驱动里面的fb_info注册到fb_mem框架中去,这样对具体硬件的操作就有了者落点
//---------------------------------------start
int
register_framebuffer(struct fb_info *fb_info)
{
    int i;
    struct fb_event event;
    struct fb_videomode mode;

    //判断注册数组是否已满
    if (num_registered_fb == FB_MAX)
        return -ENXIO;

    if (fb_check_foreignness(fb_info))
        return -ENOSYS;
    //增加已注册计数
    num_registered_fb++;
    //从注册数组中寻找一个空格
    for (i = 0 ; i < FB_MAX; i++)  
        if (!registered_fb[i])
            break;
    //将空格索引保存在fb_info中,在下面作为设备驱动的次设备号
    fb_info->node = i;        
    mutex_init(&fb_info->lock);    
    //创建设备节点/dev/fbn  n即为索引值
    fb_info->dev = device_create(fb_class, fb_info->device,
                     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);  //创建设备文件属性文件,使得用户从sys文件系统能查询或控制对应的驱动      

    //下面应该是硬件部分,还不清楚,有待再看下????
    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);
    //将fb_info放在前面寻找到的数组空格位置上,到这里,fbmem框架就能根据操作的
    //设备得到次设备号从而在这个数组里得到具体驱动的相应操作实现

    registered_fb[i] = fb_info;

    //下面这2句是给关注framebuffer驱动事件的部分发送通知消息
    event.info = fb_info;
    fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    return 0;
}
//---------------------------------------end

到这里,设备驱动的注册过程就完成了,fbmem框架能够实现具体硬件相关的操作了
接写来描述具体驱动中,应用程序常见的ioctl操作在底层是怎么实现的。
0 0
原创粉丝点击