framebuffer设备驱动分析

一.设备驱动相关文件

    1.1. 驱动框架相关文件

        1.1.1. drivers/video/fbmem.c

            a. 创建graphics类、注册FB的字符设备驱动

fbmem_init(void)
{
    proc_create("fb", 0, NULL, &fb_proc_fops);

    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;
}
View Code

        b. 提供register_framebuffer接口给具体framebuffer驱动编写着来注册fb设备的

/**
 *    register_framebuffer - registers a frame buffer device
 *    @fb_info: frame buffer info structure
 *
 *    Registers a frame buffer device @fb_info.
 *
 *    Returns negative errno on error, or zero for success.
 *
 */

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;

    remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
                     fb_is_primary_device(fb_info));

    num_registered_fb++;
    for (i = 0 ; i < FB_MAX; i++)
        if (!registered_fb[i])
            break;
    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,
                     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);
    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);
    unlock_fb_info(fb_info);
    return 0;
}
View Code

        1.1.2. drivers/video/fbsys.c

            a. 这个文件是处理fb在/sys目录下的一些属性文件的。

        1.1.3. drivers/video/modedb.c

            a. 这个文件是管理显示模式(譬如VGA、720P等就是显示模式)的

    1.2. 设备驱动部分相关文件

        1.2.1. drivers/video/samsung/s3cfb.c

            a. 驱动主体,后续着重讲解

        1.2.2. drivers/video/samsung/s3cfb_fimd6x.c

            a. 很多LCD硬件操作的函数在此文件

        1.2.3. arch/arm/mach-s5pv210/mach-x210.c

            a. 负责提供framebuffer的platform_device数据。

        1.2.4. arch/arm/plat-s5p/devs.c

            a. 为platform_device提供一些硬件描述信息的

二. platform_device平台设备分析

    2.1. fb的驱动是基于platform平台总线的

        2.1.1. 需要有相应platform_device(注册平台设备)和platform_driver(注册平台驱动)。与前面讲的是平台驱动部分相似。

        2.1.2. platform_device相关部分在arch\arm\mach-s5pv210\mach-x210.c

    2.2. platform_device相关代码分析

        2.2.1. s3c_device_fb定义

            a. 从定义的变量中可以看出来,并没有挂接设备的私有数据到s3c_device_fb变量中,因为platform_device结构体中device结构体下的platform_data指针并没有被赋值

            b. platform_data采取在其他函数重新追究platform_data的赋值办法实现的,后面介绍。

            c. 结构体中定义了相关resource,这样把相关硬件添加进dev

struct platform_device s3c_device_fb = {
    .name          = "s3cfb",                                            //  平台设备的名字
    .id          = -1, 
    .num_resources      = ARRAY_SIZE(s3cfb_resource),                  //  平台设备的资源数量
    .resource      = s3cfb_resource,                                        //  平台设备的资源
    .dev          = {
        .dma_mask        = &fb_dma_mask,
        .coherent_dma_mask    = 0xffffffffUL
    }
};

static struct resource s3cfb_resource[] = {
    [0] = {
        .start = S5P_PA_LCD,
        .end   = S5P_PA_LCD + S5P_SZ_LCD - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_LCD1,
        .end   = IRQ_LCD1,
        .flags = IORESOURCE_IRQ,
    },
    [2] = {
        .start = IRQ_LCD0,
        .end   = IRQ_LCD0,
        .flags = IORESOURCE_IRQ,
    },
};
View Code

        2.2.2. s3cfb_set_platdata函数

            2.2.2.1. 这个函数实现了platform_device结构体中device结构体下的platform_data指针的赋值

void __init s3cfb_set_platdata(struct s3c_platform_fb *pd)
{
    struct s3c_platform_fb *npd;  // 定义一个 struct s3c_platform_fb 类型的指针 
    int i;

    if (!pd)                      // 如果没有传入 s3c_platform_fb 结构体变量指针,则使用默认的
        pd = &default_fb_data;

    npd = kmemdup(pd, sizeof(struct s3c_platform_fb), GFP_KERNEL);
    if (!npd)
        printk(KERN_ERR "%s: no memory for platform data\n", __func__);
    else {
        for (i = 0; i < npd->nr_wins; i++)
            npd->nr_buffers[i] = 1;

        npd->nr_buffers[npd->default_win] = CONFIG_FB_S3C_NR_BUFFERS; // 再进一步对数据结构进行填充

        s3cfb_get_clk_name(npd->clk_name);
        npd->clk_on = s3cfb_clk_on;
        npd->clk_off = s3cfb_clk_off;

        /* starting physical address of memory region */
        npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMD, 1);
        /* size of memory region */
        npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMD, 1);

        s3c_device_fb.dev.platform_data = npd;     // 把传进来的 s3c_platform_fb 结构体变量挂载到  s3c_device_fb变量中
    }
}
View Code

        2.2.2.2. 该函数的调用

            a. 程序中通过宏定义可以切换platform_data的赋值,这样比直接初始化结构体时更加灵活

            b. 传入该函数的结构体ek070tn93_fb_data,该结构体与LCD硬件参数相关

#ifdef CONFIG_FB_S3C_LTE480WV
    s3cfb_set_platdata(&lte480wv_fb_data);
#endif  

#ifdef CONFIG_FB_S3C_EK070TN93
    smdkv210_backlight_off();
    s3cfb_set_platdata(&ek070tn93_fb_data);
#endif
View Code

        2.2.3. s3c_platform_fb ek070tn93_fb_data分析

            a. 结构体lcd成员很重要

static struct s3c_platform_fb ek070tn93_fb_data __initdata = {
    .hw_ver    = 0x62,
    .nr_wins = 5,
    .default_win = CONFIG_FB_S3C_DEFAULT_WINDOW,    //  默认开启的虚拟窗口
    .swap = FB_SWAP_WORD | FB_SWAP_HWORD,
 
    .lcd = &ek070tn93,                                              //  描述LCD硬件信息的结构体
    .cfg_gpio    = ek070tn93_cfg_gpio,                         //  配置LCD相关的gpio的方法
    .backlight_on    = ek070tn93_backlight_on,          //   使能LCD背光
    .backlight_onoff    = ek070tn93_backlight_off,   //   关闭LCD背光
    .reset_lcd    = ek070tn93_reset_lcd,                        //   复位LCD
};
View Code

        b. lcd成员分析

            ba. 其实我们移植驱动时一般主要要修改此结构体

static struct s3cfb_lcd ek070tn93 = {
    .width = S5PV210_LCD_WIDTH,
    .height = S5PV210_LCD_HEIGHT,
    .bpp = 32,
    .freq = 60,

    .timing = {
        .h_fp    = 210,
        .h_bp    = 38,
        .h_sw    = 10,
        .v_fp    = 22,
        .v_fpe    = 1,
        .v_bp    = 18,
        .v_bpe    = 1,
        .v_sw    = 7,
    },
    .polarity = {
        .rise_vclk = 0,
        .inv_hsync = 1,
        .inv_vsync = 1,
        .inv_vden = 0,
    },
};
View Code

三. platform_driver平台设备驱动

    3.1. 注册/卸载平台驱动:s3cfb_register/s3cfb_unregister

        a. 这点和之前讲解的LED platform设备驱动相似

static int __init s3cfb_register(void)
{
    platform_driver_register(&s3cfb_driver);

    return 0;
}
static void __exit s3cfb_unregister(void)
{
    platform_driver_unregister(&s3cfb_driver);
}
View Code

        b. s3cfb_driver结构体分析

static struct platform_driver s3cfb_driver = {
    .probe = s3cfb_probe,                             //  平台的probe函数
    .remove = __devexit_p(s3cfb_remove),
    .driver = {
           .name = S3CFB_NAME,                  //  平台设备驱动的名字    s3cfb
           .owner = THIS_MODULE,
    },
};


struct s3c_platform_fb {
    int        hw_ver;
    char        clk_name[16];
    int        nr_wins;                    //  这个表示虚拟窗口的数量
    int        nr_buffers[5];
    int        default_win;             //  这个表示当前默认的窗口
    int        swap;
    phys_addr_t    pmem_start; /* starting physical address of memory region */  //   显存的物理起始地址
    size_t        pmem_size; /* size of memory region */                                     //  显存的字节大小
    void            *lcd;
    void        (*cfg_gpio)(struct platform_device *dev);                                     //  LCD相关gpio的配置
    int        (*backlight_on)(struct platform_device *dev);                              //  打开LCD的背光
    int        (*backlight_onoff)(struct platform_device *dev, int onoff);          //  关闭LCD的背光
    int        (*reset_lcd)(struct platform_device *dev);                                     //  复位LCD
    int        (*clk_on)(struct platform_device *pdev, struct clk **s3cfb_clk);    //  LCD相关的时钟打开
    int        (*clk_off)(struct platform_device *pdev, struct clk **clk);              //  LCD相关的时钟关闭
};
View Code

    3.2. s3cfb_probe函数分析

        3.2.1. 当devices与driver匹配成功后就会调用此函数

        3.2.2. s3cfb_global结构体定义

            a. 设备驱动部分封装的一个全局的结构体,这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的

struct s3cfb_global {
    /* general */
    void __iomem        *regs;                    //  SoC中LCD控制器部分相关的寄存器地址的基地址(虚拟地址)   Display Controller (FIMD)模块
    struct mutex        lock;                     //   互斥锁
    struct device        *dev;                     //  表示本fb设备的device指针
    struct clk        *clock;
    struct regulator    *regulator;
    int            irq;                               //  本LCD使用到的中断号
    struct fb_info        **fb;                     //   fb_info 的二重指针   用来指向一个 fb_info 指针数组  
    struct completion    fb_complete;

    /* fimd */
    int            enabled;
    int            dsi;
    int            interlace;
    enum s3cfb_output_t    output;           //  LCD的输出模式
    enum s3cfb_rgb_mode_t    rgb_mode;     //  RGB色彩模式 
    struct s3cfb_lcd    *lcd;                       //  用来描述一个LCD的硬件信息

#ifdef CONFIG_HAS_WAKELOCK
    struct early_suspend    early_suspend;
    struct wake_lock    idle_lock;
#endif

#ifdef CONFIG_CPU_FREQ
    struct notifier_block    freq_transition;
    struct notifier_block    freq_policy;
#endif

};
View Code

        3.2.3. s3cfb_probe函数分析

static int __devinit s3cfb_probe(struct platform_device *pdev)
{
    struct s3c_platform_fb *pdata;              //  这个是三星封装的一个用来表示平台设备层的私有数据的结构体,在x210中定义了该结构体实体
    struct s3cfb_global *fbdev;                   //  设备驱动部分封装的一个全局的结构体,这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的
    struct resource *res;                              //  定义一个资源结构体指针
    int i, j, ret = 0;

    fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);    //  给 fpdev 申请分配内存
    if (!fbdev) {
        dev_err(&pdev->dev, "failed to allocate for "
            "global fb structure\n");
        ret = -ENOMEM;
        goto err_global;
    }
    fbdev->dev = &pdev->dev;              //  通过 fbdev->dev  指向 pdev->dev       /sys/devices/platform/s3cfb/   这个目录作为fb设备的父设备目录

    fbdev->regulator = regulator_get(&pdev->dev, "pd");  //  调整器 : 动态电流和电压控制,具体的我也不清楚
    if (!fbdev->regulator) {
        dev_err(fbdev->dev, "failed to get regulator\n");
        ret = -EINVAL;
        goto err_regulator;
    }
    ret = regulator_enable(fbdev->regulator);
    if (ret < 0) {
        dev_err(fbdev->dev, "failed to enable regulator\n");
        ret = -EINVAL;
        goto err_regulator;
    }
    pdata = to_fb_plat(&pdev->dev);   //  获取平台设备层的私有数据   pdev->dev-> platform_data  存放在 pdata中         
    if (!pdata) {
        dev_err(fbdev->dev, "failed to get platform data\n");
        ret = -EINVAL;
        goto err_pdata;
    }

    fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;     //  通过fbdev->lcd 指向 pdata->lcd    
   
    if (pdata->cfg_gpio)   //  如果平台设备的私有数据中的cfg_gpio指向了一个有效的配置LCD相关的gpio的方法
        pdata->cfg_gpio(pdev);   //  则调用这个函数

    if (pdata->clk_on)     //   打开LCD相关的时钟设置
        pdata->clk_on(pdev, &fbdev->clock);

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   //  获取平台设备的IO资源
    if (!res) {
        dev_err(fbdev->dev, "failed to get io memory region\n");
        ret = -EINVAL;
        goto err_io;
    }

    res = request_mem_region(res->start,      //      请求进行物理地址到虚拟地址的映射
                 res->end - res->start + 1, pdev->name);
    if (!res) {
        dev_err(fbdev->dev, "failed to request io memory region\n");
        ret = -EINVAL;
        goto err_io;
    }

    fbdev->regs = ioremap(res->start, res->end - res->start + 1);   //   申请物理地址到虚拟地址的映射,将映射得到的虚拟地址存放在 fbdev->regs
    if (!fbdev->regs) {
        dev_err(fbdev->dev, "failed to remap io region\n");
        ret = -EINVAL;
        goto err_mem;
    }

    s3cfb_set_vsync_interrupt(fbdev, 1);            //   使能vsync中断(场同步信号中断)
    s3cfb_set_global_interrupt(fbdev, 1);          //    全局中断使能: 使能视频帧中断 和 使能视频中断
    s3cfb_init_global(fbdev);                             //   全局初始化

    if (s3cfb_alloc_framebuffer(fbdev)) {    //   给fb_info 申请分配内存  并构建fb_info结构体
        ret = -ENOMEM;
        goto err_alloc;
    }

    if (s3cfb_register_framebuffer(fbdev)) {    //  注册fb设备    内部其实就是调用了FB驱动框架层中 register_framebuffer 函数进行注册
        ret = -EINVAL;
        goto err_register;
    }

    s3cfb_set_clock(fbdev);                        //   时钟设置
    s3cfb_set_window(fbdev, pdata->default_win, 1);        //   虚拟窗口相关的设置

    s3cfb_display_on(fbdev);                    //  打开LCD显示

    fbdev->irq = platform_get_irq(pdev, 0);            //   获取平台设备私有数据中的 中断号资源
    if (request_irq(fbdev->irq, s3cfb_irq_frame, IRQF_SHARED,      //   申请中断
            pdev->name, fbdev)) {
        dev_err(fbdev->dev, "request_irq failed\n");
        ret = -EINVAL;
        goto err_irq;
    }

#ifdef CONFIG_FB_S3C_LCD_INIT
    if (pdata->backlight_on)
        pdata->backlight_on(pdev);      

    if (!bootloaderfb && pdata->reset_lcd)  
        pdata->reset_lcd(pdev);
#endif

#ifdef CONFIG_HAS_EARLYSUSPEND
    fbdev->early_suspend.suspend = s3cfb_early_suspend;
    fbdev->early_suspend.resume = s3cfb_late_resume;
    fbdev->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
    register_early_suspend(&fbdev->early_suspend);
#endif

    ret = device_create_file(&(pdev->dev), &dev_attr_win_power);     //    在平台设备下   /sys/devices/platform/pdev_dev/dev_attr_win_power   属性文件
    if (ret < 0)                                                                                          //    pdev_dev表示的就是我们的平台设备的名字
        dev_err(fbdev->dev, "failed to add sysfs entries\n");

    dev_info(fbdev->dev, "registered successfully\n");

#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)        //   下面这个是处理Linux启动logo 相关的代码
    if (fb_prepare_logo( fbdev->fb[pdata->default_win], FB_ROTATE_UR)) {
        printk("Start display and show logo\n");
        /* Start display and show logo on boot */
        fb_set_cmap(&fbdev->fb[pdata->default_win]->cmap, fbdev->fb[pdata->default_win]);
        fb_show_logo(fbdev->fb[pdata->default_win], FB_ROTATE_UR);
    }
#endif
    mdelay(100);
    if (pdata->backlight_on)                        //  打开背光
        pdata->backlight_on(pdev);

    return 0;

err_irq:
    s3cfb_display_off(fbdev);
    s3cfb_set_window(fbdev, pdata->default_win, 0);
    for (i = pdata->default_win;
            i < pdata->nr_wins + pdata->default_win; i++) {
        j = i % pdata->nr_wins;
        unregister_framebuffer(fbdev->fb[j]);
    }
err_register:
    for (i = 0; i < pdata->nr_wins; i++) {
        if (i == pdata->default_win)
            s3cfb_unmap_default_video_memory(fbdev->fb[i]);
        framebuffer_release(fbdev->fb[i]);
    }
    kfree(fbdev->fb);

err_alloc:
    iounmap(fbdev->regs);

err_mem:
    release_mem_region(res->start,
                 res->end - res->start + 1);

err_io:
    pdata->clk_off(pdev, &fbdev->clock);

err_pdata:
    regulator_disable(fbdev->regulator);

err_regulator:
    kfree(fbdev);

err_global:
    return ret;
}
View Code

        3.2.4. s3cfb_init_global函数

static int s3cfb_init_global(struct s3cfb_global *ctrl)
{
    ctrl->output = OUTPUT_RGB;                     //  设置初始模式
    ctrl->rgb_mode = MODE_RGB_P;               //   设置RGB色彩模式

    init_completion(&ctrl->fb_complete);       //  初始化完成量(注: 完成量也是一种内核提供的同步机制)
    mutex_init(&ctrl->lock);

    s3cfb_set_output(ctrl);              //  寄存器配置LCD的输出模式
    s3cfb_set_display_mode(ctrl);  //   寄存器配置LCD的显示模式
    s3cfb_set_polarity(ctrl);            //   寄存器配置信号电平翻转
    s3cfb_set_timing(ctrl);              //   寄存器配置LCD时序参数
    s3cfb_set_lcd_size(ctrl);            //   寄存器配置LCD的水平、垂直像素大小

    return 0;
}
View Code

        3.2.5. s3cfb_alloc_framebuffer函数

static int s3cfb_alloc_framebuffer(struct s3cfb_global *ctrl)
{
    struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev);     //  通过 ctrl->dev 去获取平台设备的私有数据
    int ret, i;

    ctrl->fb = kmalloc(pdata->nr_wins *             //   给ctrl->fb  的这个fb_info指针数组分配内存
            sizeof(*(ctrl->fb)), GFP_KERNEL);    //   数量  nr_wins
    if (!ctrl->fb) {
        dev_err(ctrl->dev, "not enough memory\n");
        ret = -ENOMEM;
        goto err_alloc;
    }

    for (i = 0; i < pdata->nr_wins; i++) {                //  给fb_info 指针数组中的每一个指针申请分配内存
        ctrl->fb[i] = framebuffer_alloc(sizeof(*ctrl->fb),
                         ctrl->dev);
        if (!ctrl->fb[i]) {
            dev_err(ctrl->dev, "not enough memory\n");
            ret = -ENOMEM;
            goto err_alloc_fb;
        }

        s3cfb_init_fbinfo(ctrl, i);    //  初始化fb_info  这个结构体   就是去构建fb_info

        if (i == pdata->default_win) {
            if (s3cfb_map_video_memory(ctrl->fb[i])) {        //  给FB显存确定内存地址和分配空间(注意只是对默认的fb设备分配了,一个虚拟的显示窗口其实就是抽象为一个fb设备,多个窗口其实是会进行叠加的)
                dev_err(ctrl->dev,
                    "failed to map video memory "
                    "for default window (%d)\n", i);
                ret = -ENOMEM;
                goto err_map_video_mem;
            }
        }
    }

    return 0;

err_alloc_fb:
    while (--i >= 0) {
        if (i == pdata->default_win)
            s3cfb_unmap_default_video_memory(ctrl->fb[i]);

err_map_video_mem:
        framebuffer_release(ctrl->fb[i]);
    }
    kfree(ctrl->fb);

err_alloc:
    return ret;
}



struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
{
#define BYTES_PER_LONG (BITS_PER_LONG/8)
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
    int fb_info_size = sizeof(struct fb_info);          //  获取fb_info结构体类型的字节大小
    struct fb_info *info;                    
    char *p;

    if (size)
        fb_info_size += PADDING;            

    p = kzalloc(fb_info_size + size, GFP_KERNEL);

    if (!p)
        return NULL;

    info = (struct fb_info *) p;

    if (size)
        info->par = p + fb_info_size;

    info->device = dev;       //   指定我们的 fb 设备的父类设备是平台设备    /sys/devices/platform/plat_xxxdev/  这个目录,也就是我们将来创建的设备就在这个目录下 

#ifdef CONFIG_FB_BACKLIGHT
    mutex_init(&info->bl_curve_mutex);
#endif
 
    return info;
#undef PADDING
#undef BYTES_PER_LONG
}



static int s3cfb_map_video_memory(struct fb_info *fb)
{
    struct fb_fix_screeninfo *fix = &fb->fix;
    struct s3cfb_window *win = fb->par;
    struct s3cfb_global *fbdev =
        platform_get_drvdata(to_platform_device(fb->device));
    struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);

    if (win->owner == DMA_MEM_OTHER) {
        fix->smem_start = win->other_mem_addr;
        fix->smem_len = win->other_mem_size;
        return 0;
    }

    if (fb->screen_base)          //  如果我们之前就已经确定了FB的显存地址的虚拟地址,那么就直接退出,因为这个函数的作用就是给显存确定虚拟内存地址并分配内存空间
        return 0;

    if (pdata && pdata->pmem_start && (pdata->pmem_size >= fix->smem_len)) {  //  如果我们的平台设备中的私有数据中已经确定了显存的物理地址和大小
        fix->smem_start = pdata->pmem_start;                                                     //  那么就使用平台设备私有数据中定义的
        fb->screen_base = ioremap_wc(fix->smem_start, pdata->pmem_size);
    } else
        fb->screen_base = dma_alloc_writecombine(fbdev->dev,       //  否则的话我们就自己申请分配显存空间
                         PAGE_ALIGN(fix->smem_len),
                         (unsigned int *)
                         &fix->smem_start, GFP_KERNEL);

    if (!fb->screen_base)
        return -ENOMEM;

    dev_info(fbdev->dev, "[fb%d] dma: 0x%08x, cpu: 0x%08x, "
             "size: 0x%08x\n", win->id,
             (unsigned int)fix->smem_start,
             (unsigned int)fb->screen_base, fix->smem_len);

    memset(fb->screen_base, 0, fix->smem_len);                 //  将FB显存清零
    win->owner = DMA_MEM_FIMD;

    return 0;
}
View Code

 

 

 

索引文献:https://www.cnblogs.com/deng-tao/p/6078072.html

转载于:https://www.cnblogs.com/linux-37ge/p/10212470.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值