framebuffer之s3cfb_probe分析

来源:互联网 发布:python黑客教程 编辑:程序博客网 时间:2024/06/05 11:08

本文主要讲解lcd驱动部分的内容(关于framebuffer的驱动框架分析见 Linux Framebuffer驱动框架、接口实现和使用 ),涉及到以下文件:
(1)drivers/video/samsung/s3cfb.c,驱动主体
(2)drivers/video/samsung/s3cfb_fimd6x.c,里面有很多LCD硬件操作的函数
(2)arch/arm/mach-s5pv210/mach-x210.c,负责提供platform_device的
(3)arch/arm/plat-s5p/devs.c,为platform_device提供一些硬件描述信息的

s3cfb.c中的s3cfb_probe设备探测,是驱动注册的主要函数
这里写图片描述

    /*定义一个结构体用来维护驱动程序中各函数中用到的变量      先别看结构体要定义这些成员,到各函数使用的地方就明白了*/    static int __devinit s3cfb_probe(struct platform_device *pdev)    {        struct s3c_platform_fb *pdata;/*LCD屏配置信息结构体*/        struct s3cfb_global *fbdev;/*驱动程序全局变量结构体*/        struct resource *res; /*用来保存从LCD平台设备中获取的LCD资源*/        int i, j, ret = 0;        printk("%s\n",__func__);        fbdev = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);        if (!fbdev) {            dev_err(&pdev->dev, "failed to allocate for "                "global fb structure\n");            ret = -ENOMEM;            goto err_global;        }        fbdev->dev = &pdev->dev;        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;        }        /*获取LCD参数信息*/        pdata = to_fb_plat(&pdev->dev);        if (!pdata) {            dev_err(fbdev->dev, "failed to get platform data\n");            ret = -EINVAL;            goto err_pdata;        }        fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd;        /*配置GPIO端口*/        if (pdata->cfg_gpio)            pdata->cfg_gpio(pdev);        /*设置时钟参数*/        if (pdata->clk_on)            pdata->clk_on(pdev, &fbdev->clock);        /*获取LCD平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和LCD平台设备定义中的一致*/        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);        if (!res) {            dev_err(fbdev->dev, "failed to get io memory region\n");            ret = -EINVAL;            goto err_io;        }        /*申请LCD IO端口所占用的IO空间(注意理解IO空间和内存空间的区别),request_mem_region定义在ioport.h中*/        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;        }        /*将LCD的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中         * 注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作*/        fbdev->regs = ioremap(res->start, res->end - res->start + 1);        if (!fbdev->regs) {            dev_err(fbdev->dev, "failed to remap io region\n");            ret = -EINVAL;            goto err_mem;        }    #ifdef CONFIG_FB_S3C_LTE480WV        /*设置寄存器初始状态*/        s3cfb_pre_init_para(fbdev);    #endif        /*设置gamma 值*/        s3cfb_set_gamma(fbdev);        /*设置VSYNC中断*/        s3cfb_set_vsync_interrupt(fbdev, 1);        /*设置全局中断*/        s3cfb_set_global_interrupt(fbdev, 1);        /*fb设备参数信息初始化*/        s3cfb_init_global(fbdev);        /*为framebuffer分配空间,进行内存映射,填充fb_info*/        if (s3cfb_alloc_framebuffer(fbdev)) {            ret = -ENOMEM;            goto err_alloc;        }        /*注册fb设备到系统中*/        if (s3cfb_register_framebuffer(fbdev)) {            ret = -EINVAL;            goto err_register;        }        s3cfb_set_clock(fbdev);        s3cfb_set_window(fbdev, pdata->default_win, 1);        s3cfb_display_on(fbdev);        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);        if (pdata->lcd_on)            pdata->lcd_on(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        /*对设备文件系统的支持,创建fb设备文件*/        ret = device_create_file(&(pdev->dev), &dev_attr_win_power);        if (ret < 0)            dev_err(fbdev->dev, "failed to add sysfs entries\n");        dev_info(fbdev->dev, "registered successfully\n");        /*显示开机logo*/    #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_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        return 0;    }

1、struct s3c_platform_fb
这个结构体是fb的platform_data结构体,这个结构体变量就是platform设备的私有数据,这个数据在platform_device.device.platform_data中存储。
在mach-x210.c文件中:

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,    .cfg_gpio   = ek070tn93_cfg_gpio,    .backlight_on   = ek070tn93_backlight_on,    .backlight_onoff    = ek070tn93_backlight_off,    .reset_lcd  = ek070tn93_reset_lcd,};

通过这个结构体去填充这些数据,在probe函数中通过传参的platform_device指针取出来。

2、struct s3cfb_global
这个结构体主要作用是在驱动部分的2个文件(s3cfb.c和s3cfb_fimd6x.c)的函数中做数据传递用的。

3、struct resource

/* LCD Controller *///LCD控制器的资源信息static struct resource s3cfb_resource [] = {    [0] = {        .start = S5P_PA_LCD, //控制器IO端口开始地址(0xf8000000)        .end = S5P_PA_LCD + S5P_SZ_LCD - 1,//控制器IO端口结束地址(1M)        .flags = IORESOURCE_MEM,//标识为LCD控制器IO端口,在驱动中引用这个就表示引用IO端口    },    [1] = {        .start = IRQ_LCD1,//LCD中断        .end = IRQ_LCD1,        .flags = IORESOURCE_IRQ,//标识为LCD中断    }};

4、regulator:主要是lcd电源功耗管理的

5、to_fb_plat:取出platform_device中放进去的platform_data数据

6、s3cfb_set_platdata
在struct platform_device s3c_device_fb里并没有设置platdata的内容,其实他并不是没有设置platdata,只是通过s3cfb_set_platdata这个函数来单独设置了。为什么要通过这种方式呢?主要是因为这里的platdata比较复杂,所以被拿出来单独去设置。它在启动时通过smdkc110_machine_init函数加载,保证platdata在内核启动时就已经设置好。

7、s3cfb_lcd

struct s3cfb_lcd {    int width;      // horizontal resolution    int height;     // vertical resolution    int p_width;    // width of lcd in mm    int p_height;   // height of lcd in mm    int bpp;        // bits per pixel    int freq;       // vframe frequency    struct  s3cfb_lcd_timing timing;    // timing values    struct  s3cfb_lcd_polarity polarity;// polarity settings    void    (*init_ldi)(void);  // pointer to LDI init function    void    (*deinit_ldi)(void);};

s3cfb_lcd:主要是lcd硬件相关的属性(分辨率、位深度、时序等).
fbdev->lcd = (struct s3cfb_lcd *)pdata->lcd 就是通过函数指针,指向mach-x210.c文件中的platdata数据中的lcd部分(.lcd = &ek070tn93)。

8、pdata->cfg_gpio

static void ek070tn93_cfg_gpio(struct platform_device *pdev){    int i;    for (i = 0; i < 8; i++) {        s3c_gpio_cfgpin(S5PV210_GPF0(i), S3C_GPIO_SFN(2));        s3c_gpio_setpull(S5PV210_GPF0(i), S3C_GPIO_PULL_NONE);    }    for (i = 0; i < 8; i++) {        s3c_gpio_cfgpin(S5PV210_GPF1(i), S3C_GPIO_SFN(2));        s3c_gpio_setpull(S5PV210_GPF1(i), S3C_GPIO_PULL_NONE);    ……}

主要是设置lcd相关的gpio,通过函数指针pdata->cfg_gpio,指向mach-x210.c文件中platdata数据中的gpio相关数据(.cfg_gpio = ek070tn93_cfg_gpio)

9、
platform_get_resource:取出resource
request_mem_region:申请内存
ioremap:动态映射

10、s3cfb_register_framebuffer
在函数里register_framebuffer(ctrl->fb[j])可以注册多个framebuffer,对应/dev/fb0,/dev/fb1等等。主要是设置多个虚拟屏幕。就像电视台的屏幕的左上角是台标,最下面一行可以显示滚动的广告,主画面是人物,他们其实就是对应不同的framebuffer,当他们其中的某一个发生变化时就刷新对应的framebuffer,这样刷新的效率就大大提高了。

11、fb_prepare_logo
linux内核启动时会在lcd左上角显示企鹅。fb_prepare_logo调用fb_find_logo,在fb_find_logo函数中:

#ifdef CONFIG_LOGO_X210_CLUT224logo = &logo_x210_clut224;#endif

logo_x210_clut224就是logo显示数据,它不在源码中,而是在kernel/kernel/drivers/video/logo目录中,里面有很多ppm格式的logo文件,其中就有我们需要的logo_x210_clut224.ppm的文件。

12、fb_show_logo
–>fb_show_logo_line //真正显示logo
–>fb_do_show_logo
–>info->fbops->fb_imageblit //实际操作硬件fb进行显示工作的函数

0 0
原创粉丝点击