11、三星平台framebuffer驅動


 

和總線設備驅動模型類似,framebuffer分為核心層、驅動層和設備層。

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

驅動層(控制器層):一般由芯片原廠提供,實現了LCD控制器通用的操作接口和配置接口,本章用到的是三星提供的s3cfb_main.cs3cfb_ops.c

設備層:一般由單板廠商提供,本章用到的是arch/arm/plat-s5p/dev-fimd-s5p.c文件,后續會分析為什么是它。

考慮到我是用的並不是之前的TINY4412,在此給出上述分析的文件:

https://files.cnblogs.com/files/Lioker/11_fb.zip

 

注意:有的開發板的驅動層可能是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=yCONFIG_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 }

 

 

四、關系總結

各個結構體關系如下圖所示:

 

上一章的framebuffer設備驅動分為核心、信息(fb_info)和操作(fb_ops)。

本章的在上一章的基礎上完成了適配平台的工作,在總線下分為信息(s3c_fb_global)和操作(s3c_platform_fb)。由於同一CPU可以適配多個單板,單板和LCD選擇項過多,因此提取單板結構體(s3cfb_fimd_desc)和LCD結構體(s3cfb_lcd)為信息(s3c_fb_global)提供數據。

 

 

五、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 };

 

 

下一章  12、使用PWM調整LCD背光亮度

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM