ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

十一、三星平台framebuffer驱动

2019-06-02 13:45:14  阅读:287  来源: 互联网

标签:十一 struct 三星 lcd var fb s3cfb fbdev framebuffer


 

和总线设备驱动模型类似,framebuffer分为核心层、驱动层和设备层。

核心层:就是上一章分析的fbmem.c文件

驱动层(控制器层):一般由芯片原厂提供,实现了LCD控制器通用的操作接口和配置接口,本章用到的是三星提供的s3cfb_main.c和s3vfb_ops.c

设备层:一般由单板厂商提供,本章用到的是arch/arm/plat-s5p/dev-fimd-s5p.c文件,后续会分析为什么是它。

考虑到我是用的并不是之前的TINY4412,在此给出上述分析的文件:

https://pan.baidu.com/s/1rjM7_v2DZDgRNUErGxZrQQ

提取码为:v6ed

 

注意:有的开发板的驱动层可能是s3c-fb.c,这要取决于:

1. drivers/video/Makefile中的约束条件,比如我的Makefile是obj-$(CONFIG_FB_S3C) += s3c-fb.o

2. 内核根目录下的.config是否配置了CONFIG_FB_S3C这个宏,比如我的.config是# CONFIG_FB_S3C is not set,未设置

进一步分析.config,我发现了CONFIG_FB_S5P=y和CONFIG_FB_S5P_WA101S=y,在drivers/video/Makefile中有obj-$(CONFIG_FB_S5P) += samsung/,故需要查看drivers/video/samsung/Makefile。

经过.config排除drivers/video/samsung/Makefile的宏定义后,Makefile文件最终如下:

ifeq ($(CONFIG_FB_S5P),y)

obj-y               += s3cfb.o
s3cfb-y             := s3cfb_main.o s3cfb_ops.o
obj-$(CONFIG_ARCH_EXYNOS4)  += s3cfb_fimd6x.o

obj-$(CONFIG_FB_S5P_WA101S) += s3cfb_wa101s.o

endif

 

除此之外,我们需要完成的是设备层下的参数层,参数层存储LCD相关的一些时序,如分辨率、BPP等参数。单板厂商提供的是drivers/video/samsung/s3cfb_wa101s.c文件。

把控制器层和设备层分开,是因为芯片的LCD控制器只有一个,而开发板配套LCD选择较多。LCD控制器确定了LCD的操作方式,因此可以被普适化。

对于LCD,我们可以选择4.3寸、7寸等,不同LCD会有一定的差异,此时就可以采取数据总线的形式。我们根据LCD各自的物理特性,把相关参数添加到构建好的平台总线的设备层即可。

 

本章仿照总线设备驱动模型进行分析,首先介绍需要使用的结构体,之后分析platform_driver和platform_device,最后分析参数层。

 

 

一、平台驱动使用的结构体 

1. s3c_platform_fb:总线对应的fb结构体,定义有LCD操作函数

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;  /* 显存的物理起始地址 */
    size_t        pmem_size;    /* 显存的字节大小 */
    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时钟 */
};

 

2. s3cfb_fimd_desc:单板对应的fb结构体

struct s3cfb_fimd_desc {
    int            state;
    int            dual;
    struct s3cfb_global    *fbdev[FIMD_MAX];    /* 通用的平台fb结构体 */
};

 

3. s3cfb_global:平台通用的fb结构体,定义有LCD参数

struct s3cfb_global {
    void __iomem        *regs;   /* LCD对应的寄存器 */
    struct mutex        lock;    /* 互斥量 */
    struct device        *dev;   /* LCD设备 */
    struct clk        *clock;    /* LCD时钟 */
    int            irq;
    wait_queue_head_t    wq;
    unsigned int        wq_count;
    struct fb_info        **fb;  /* 通用的fb属性,指针数组 */

    atomic_t        enabled_win;
    enum s3cfb_output_t    output;
    enum s3cfb_rgb_mode_t    rgb_mode;
    struct s3cfb_lcd    *lcd;    /* 用于描述LCD物理参数 */
    int             system_state;
#ifdef CONFIG_HAS_WAKELOCK
    struct early_suspend    early_suspend;
    struct wake_lock    idle_lock;
#endif
};

 

4. s3cfb_lcd:描述LCD物理参数,这个结构体是需要我们修改的

struct s3cfb_lcd {
    int    width;        /* 水平像素个数 */
    int    height;       /* 垂直像素个数 */
    int    bpp;          /* 每个像素位数 */
    int    freq;         /* LCD刷新率 */
    struct    s3cfb_lcd_timing timing;      /* LCD时序相关参数 */
    struct    s3cfb_lcd_polarity polarity;  /* 电平是否反转 */
    void    (*init_ldi)(void);
    void    (*deinit_ldi)(void);
};

 

 

二、platform_driver

platform_driver定义在s3cfb_main.c中,其定义如下:

static struct platform_driver s3cfb_driver = {
    .probe        = s3cfb_probe,
    .remove        = s3cfb_remove,
...
    .driver        = {
        .name    = S3CFB_NAME,    /* #define S3CFB_NAME "s3cfb" */
...
    },
};

 

我们首先查看s3cfb_probe()函数:

 1 static int s3cfb_probe(struct platform_device *pdev)
 2 {
 3     struct s3c_platform_fb *pdata = NULL;
 4     struct resource *res = NULL;
 5     struct s3cfb_global *fbdev[2];
 6     int ret = 0;
 7     int i = 0;
 8 ...
 9     /* 分配包含平台设备s3cfb_global指针的结构体 */
10     fbfimd = kzalloc(sizeof(struct s3cfb_fimd_desc), GFP_KERNEL);
11 ...
12     for (i = 0; i < FIMD_MAX; i++) {
13         /* 分配平台设备s3cfb_global,里面包含LCD各种属性 */
14         fbfimd->fbdev[i] = kzalloc(sizeof(struct s3cfb_global), GFP_KERNEL);
15         fbdev[i] = fbfimd->fbdev[i];
16 ...
17         fbdev[i]->dev = &pdev->dev;
18         s3cfb_set_lcd_info(fbdev[i]);    /* 设置s3cfb_global的lcd成员为&wa101:struct s3cfb_global *ctrl; ctrl->lcd = &wa101;  */
19 
20         /* 配置platform_device的引脚和时钟参数 */
21         pdata = to_fb_plat(&pdev->dev);
22         if (pdata->cfg_gpio)
23             pdata->cfg_gpio(pdev);
24 
25         if (pdata->clk_on)
26             pdata->clk_on(pdev, &fbdev[i]->clock);
27 
28         /* 内存映射 */
29         res = platform_get_resource(pdev, IORESOURCE_MEM, i);
30 ...
31         res = request_mem_region(res->start,
32                     res->end - res->start + 1, pdev->name);
33 ...
34         fbdev[i]->regs = ioremap(res->start, res->end - res->start + 1);
35 ...
36         /* irq */
37         fbdev[i]->irq = platform_get_irq(pdev, 0);
38 ...
39         /* fb寄存器设置 */
40         s3cfb_init_global(fbdev[i]);
41 
42         fbdev[i]->system_state = POWER_ON;
43 
44         /* 分配 fb_info */
45         if (s3cfb_alloc_framebuffer(fbdev[i], i)) {
46 ...
47         }
48 
49         /* 注册 fb_info */
50         if (s3cfb_register_framebuffer(fbdev[i])) {
51 ...
52         }
53 
54         /* 使能显示 */
55         s3cfb_set_clock(fbdev[i]);
56         s3cfb_enable_window(fbdev[0], pdata->default_win);
57 ...
58         s3cfb_update_power_state(fbdev[i], pdata->default_win, FB_BLANK_UNBLANK);
59         s3cfb_display_on(fbdev[i]);
60 ...
61     }
62     /* 使能背光 */
63 #ifdef CONFIG_FB_S5P_LCD_INIT
64     /* panel control */
65     if (pdata->backlight_on)
66         pdata->backlight_on(pdev);
67 
68     if (pdata->lcd_on)
69         pdata->lcd_on(pdev);
70 #endif
71 
72     ret = device_create_file(&(pdev->dev), &dev_attr_win_power);
73 ...
74     dev_info(fbdev[0]->dev, "registered successfully\n");
75 ...
76     return 0;
77 }

 

probe()函数所做的事情有:

1. 分配单板对应的s3cfb_fimd_desc

2. 分配平台对应的s3cfb_global

3. 调用s3cfb_set_lcd_info()设置s3cfb_global的lcd成员

 1 void s3cfb_setup_lcd()
 2 {
 3     int type = get_lcd_type();    /* 获取LCD类型 */
 4 ...
 5     if(0x1 == type)        //7.0
 6     {
 7         wa101.width  = 800;
 8         wa101.height = 1280;
 9         wa101.bpp     = 24;
10         wa101.freq   = 50;    //70;
11     }
12 ...
13 }
14 
15 void s3cfb_set_lcd_info(struct s3cfb_global *ctrl)
16 {
17     s3cfb_setup_lcd();
18 
19     wa101.init_ldi = NULL;
20     ctrl->lcd = &wa101;
21 }
View Code

4. 配置引脚,打开LCD时钟,设置中断

5. 映射寄存器,调用s3cfb_init_global()设置寄存器,有些寄存器的值是通过s3cfb_lcd传入的

 1 int s3cfb_init_global(struct s3cfb_global *fbdev)
 2 {
 3     fbdev->output = OUTPUT_RGB;
 4     fbdev->rgb_mode = MODE_RGB_P;
 5 
 6     fbdev->wq_count = 0;
 7     init_waitqueue_head(&fbdev->wq);
 8     mutex_init(&fbdev->lock);
 9 
10     /* 写寄存器操作 */
11     s3cfb_set_output(fbdev);        /* 设置输出格式为RGB */
12     s3cfb_set_display_mode(fbdev);    /* 设置RGB数据格式 */
13     s3cfb_set_polarity(fbdev);        /* 设置极性是否反转 */
14     s3cfb_set_timing(fbdev);        /* 设置时序 */
15     s3cfb_set_lcd_size(fbdev);        /* 设置LCD分辨率 */
16 
17     return 0;
18 }
View Code

6. 分配、注册s3cfb_global的fb[i]成员

7. 使能背光和显示

 

综合上一章,我们可以知道probe()函数分配和注册了fb_info,但两函数之间并没有设置fb_info,因此我们需要进一步分析。

分配函数如下:

 1 int s3cfb_alloc_framebuffer(struct s3cfb_global *fbdev, int fimd_id)
 2 {
 3     struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
 4     int ret = 0;
 5     int i;
 6 
 7     fbdev->fb = kmalloc(pdata->nr_wins * sizeof(struct fb_info *), GFP_KERNEL);
 8 ...
 9     for (i = 0; i < pdata->nr_wins; i++) {
10         fbdev->fb[i] = framebuffer_alloc(sizeof(struct s3cfb_window), fbdev->dev);
11 ...
12         ret = s3cfb_init_fbinfo(fbdev, i);
13 ...
14         if (i == pdata->default_win)
15             if (s3cfb_map_default_video_memory(fbdev, fbdev->fb[i], fimd_id)) {
16                 ret = -ENOMEM;
17 ...
18             }
19         }
20     }
21 ...
22     return ret;
23 }

 

在framebuffer_alloc()之后,我们需要关注第12行代码:ret = s3cfb_init_fbinfo(fbdev, i);,这个函数就是我们要找的设置fb_info函数。

 1 int s3cfb_init_fbinfo(struct s3cfb_global *fbdev, int id)
 2 {
 3     struct fb_info *fb = fbdev->fb[id];
 4     struct fb_fix_screeninfo *fix = &fb->fix;
 5     struct fb_var_screeninfo *var = &fb->var;
 6     struct s3cfb_window *win = fb->par;
 7     struct s3cfb_alpha *alpha = &win->alpha;
 8     struct s3cfb_lcd *lcd = fbdev->lcd;
 9     struct s3cfb_lcd_timing *timing = &lcd->timing;
10 
11     memset(win, 0, sizeof(struct s3cfb_window));
12     platform_set_drvdata(to_platform_device(fbdev->dev), fb);
13     strcpy(fix->id, S3CFB_NAME);
14 
15     /* fimd specific */
16     win->id = id;
17     win->path = DATA_PATH_DMA;
18     win->dma_burst = 16;
19     s3cfb_update_power_state(fbdev, win->id, FB_BLANK_POWERDOWN);
20     alpha->mode = PLANE_BLENDING;
21 
22     /* 设置fbinfo */
23     fb->fbops = &s3cfb_ops;
24     fb->flags = FBINFO_FLAG_DEFAULT;
25     fb->pseudo_palette = &win->pseudo_pal;
26 #if (CONFIG_FB_S5P_NR_BUFFERS != 1)
27     fix->xpanstep = 2;
28     fix->ypanstep = 1;
29 #else
30     fix->xpanstep = 0;
31     fix->ypanstep = 0;
32 #endif
33     fix->type = FB_TYPE_PACKED_PIXELS;
34     fix->accel = FB_ACCEL_NONE;
35     fix->visual = FB_VISUAL_TRUECOLOR;
36     var->xres = lcd->width;
37     var->yres = lcd->height;
38 
39 #if defined(CONFIG_FB_S5P_VIRTUAL)
40     var->xres_virtual = CONFIG_FB_S5P_X_VRES;
41     var->yres_virtual = CONFIG_FB_S5P_Y_VRES * CONFIG_FB_S5P_NR_BUFFERS;
42 #else
43     var->xres_virtual = var->xres;
44     var->yres_virtual = var->yres * CONFIG_FB_S5P_NR_BUFFERS;
45 #endif
46     var->bits_per_pixel = 32;
47     var->xoffset = 0;
48     var->yoffset = 0;
49     var->width = 0;
50     var->height = 0;
51     var->transp.length = 0;
52 
53     fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
54     fix->smem_len = fix->line_length * var->yres_virtual;
55 
56     var->nonstd = 0;
57     var->activate = FB_ACTIVATE_NOW;
58     var->vmode = FB_VMODE_NONINTERLACED;
59     var->hsync_len = timing->h_sw;
60     var->vsync_len = timing->v_sw;
61     var->left_margin = timing->h_bp;
62     var->right_margin = timing->h_fp;
63     var->upper_margin = timing->v_bp;
64     var->lower_margin = timing->v_fp;
65     var->pixclock = (lcd->freq *
66         (var->left_margin + var->right_margin
67         + var->hsync_len + var->xres) *
68         (var->upper_margin + var->lower_margin
69         + var->vsync_len + var->yres));
70     var->pixclock = KHZ2PICOS(var->pixclock/1000);
71 
72     s3cfb_set_bitfield(var);
73     s3cfb_set_alpha_info(var, win);
74 
75     return 0;
76 }

读者如果熟悉LCD裸机操作,想更接近底层修改代码,可以修改此函数,在修改前注意备份。

 

 

三、platform_device

之前platform_driver定义的匹配方式是name匹配,搜索后确定platform_device定义在arch/arm/dev-fimd-s5p.c中

 

platform_device定义如下:

struct platform_device s3c_device_fb = {
    .name        = "s3cfb",
#if defined(CONFIG_ARCH_EXYNOS4)
    .id        = 0,
#else
    .id        = -1,
#endif
    .num_resources    = ARRAY_SIZE(s3cfb_resource),
    .resource    = s3cfb_resource,
    .dev        = {
        .dma_mask        = &fb_dma_mask,
        .coherent_dma_mask    = 0xffffffffUL
    }
};

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

但是前面平台驱动中使用了平台设备的私有数据。我们可以搜索s3c_device_fb.dev.platform_data:

代码如下:

 1 static struct s3c_platform_fb default_fb_data __initdata = {
 2 #if defined(CONFIG_ARCH_EXYNOS4)
 3     .hw_ver    = 0x70,
 4 #else
 5     .hw_ver    = 0x62,
 6 #endif
 7     .nr_wins    = 5,
 8 #if defined(CONFIG_FB_S5P_DEFAULT_WINDOW)
 9     .default_win    = CONFIG_FB_S5P_DEFAULT_WINDOW,
10 #else
11     .default_win    = 0,
12 #endif
13     .swap        = FB_SWAP_WORD | FB_SWAP_HWORD,
14 };
15 
16 void __init s3cfb_set_platdata(struct s3c_platform_fb *pd)
17 {
18     struct s3c_platform_fb *npd;
19     int i;
20 
21     if (!pd)
22         pd = &default_fb_data;
23 
24     npd = kmemdup(pd, sizeof(struct s3c_platform_fb), GFP_KERNEL);
25     if (!npd)
26         printk(KERN_ERR "%s: no memory for platform data\n", __func__);
27     else {
28         for (i = 0; i < npd->nr_wins; i++)
29             npd->nr_buffers[i] = 1;
30 
31 #if defined(CONFIG_FB_S5P_NR_BUFFERS)
32         npd->nr_buffers[npd->default_win] = CONFIG_FB_S5P_NR_BUFFERS;
33 #else
34         npd->nr_buffers[npd->default_win] = 1;
35 #endif
36 
37         s3cfb_get_clk_name(npd->clk_name);
38         npd->cfg_gpio = s3cfb_cfg_gpio;
39         npd->backlight_on = s3cfb_backlight_on;        /* 最终调用的背光打开函数 */
40         npd->backlight_off = s3cfb_backlight_off;    /* 最终调用的背光关闭函数 */
41         npd->lcd_on = s3cfb_lcd_on;        /* 最终调用的LCD打开函数 */
42         npd->lcd_off = s3cfb_lcd_off;    /* 最终调用的LCD关闭函数 */
43         npd->clk_on = s3cfb_clk_on;        /* 最终调用的时钟使能函数 */
44         npd->clk_off = s3cfb_clk_off;
45 
46         s3c_device_fb.dev.platform_data = npd;
47     }
48 }

 

 

四、关系总结

各个结构体关系如下图所示:

 

 

五、s3cfb_lcd

此结构体定义在drivers/video/samsung/s3cfb_wa101s.c文件中,其定义如下:

 1 #include "s3cfb.h"
 2 
 3 static struct s3cfb_lcd wa101 = {
 4 ...
 5 /* CONFIG_TOUCHSCREEN_TSC2007=y */
 6 #ifdef CONFIG_TOUCHSCREEN_TSC2007
 7     .width    = 800,
 8     .height = 480,
 9 #endif
10     .bpp    = 24,
11     .freq    = 70,    // 70,
12 
13     .polarity = {
14         .rise_vclk    = 1,
15         .inv_hsync    = 0,
16         .inv_vsync    = 1,
17         .inv_vden    = 0,
18     },
19 
20 };
21 
22 extern int get_lcd_type();
23 
24 void s3cfb_setup_lcd()
25 {
26     int type = get_lcd_type();
27 ...
28     if(0x1 == type)   //7.0
29     {
30         wa101.width     = 800;
31         wa101.height = 1280;
32         wa101.bpp     = 24;
33         wa101.freq     = 50;    //70;
34     }
35 ...
36 }
37 
38 /* name should be fixed as 's3cfb_set_lcd_info' */
39 void s3cfb_set_lcd_info(struct s3cfb_global *ctrl)
40 {
41     s3cfb_setup_lcd();
42 
43     wa101.init_ldi = NULL;
44     ctrl->lcd = &wa101;
45 }

之前分析过,probe()会调用s3cfb_set_lcd_info()函数。故最终LCD参数为:

 1 static struct s3cfb_lcd wa101 = {
 2     .width     = 800;
 3     .height     = 1280;
 4     .bpp     = 24;
 5     .freq     = 50;
 6 
 7     .polarity = {
 8         .rise_vclk    = 1,
 9         .inv_hsync    = 0,
10         .inv_vsync    = 1,
11         .inv_vden    = 0,
12     },
13 };

 

标签:十一,struct,三星,lcd,var,fb,s3cfb,fbdev,framebuffer
来源: https://www.cnblogs.com/Lioker/p/10933146.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有