• (转)Tiny210v2( S5PV210 ) 平台下 FIMD 对应 的 framebuffer 驱动中,关于 video buffer 的理解


    原文:http://www.arm9home.net/read.php?tid-25938.html

    管理提醒: 本帖被 xoom 执行加亮操作(2012-12-13)

    如之前所说,一直想知道显示数据都在哪个地方,通常的数据,比如 framebuffer 中的显示数据,和OpenGL 处理的数据有啥关系。
    目前为止我还没有弄明白 OpenGL 这块,但是 framebuffer 这部分差不多了。这篇文章记录了 framebuffer 的显示数据相关内容。


    1. 关于FIMD

        Tiny210v2 开发板属于 s5pv210 的一种,在这块开发板上,显示部分又被叫做 FIMD,我不知道FIMD是什么的缩写,但D应该和Display Controller有关系吧。
        FIMD 的主要功能就是获取显示数据,并将数据输出到显示屏。当然期间会对显示数据进行处理:


             
     

        FIMD一共支持5个layer,在SoC用户手册中,将layer成为window,源代码中也叫做window。

             
     

        FIMD可以通过AXI总线从内存或者Camera哪里获取到显示数据,并进行合成。

             
     
        
    2. 内核配置 framebuffer

        通过 make menuconfig 可以配置 framebuffer 相关的内容。
        
             
     

        保存以后,在 .config 文件中可以找到相关配置内容。
        
            .config
            --------------------------------------------------
     
    复制代码
    1.         CONFIG_FB_S3C_DEFAULT_WINDOW=2
    2.         CONFIG_FB_S3C_NR_BUFFERS=3
    3.         CONFIG_FB_S3C_NUM_OVLY_WIN=1
    4.         CONFIG_FB_S3C_NUM_BUF_OVLY_WIN=3


        同样也会在头文件中生成宏定义:

            include/generated/autoconf.h
            --------------------------------------------------
     
    复制代码
    1.         #define CONFIG_FB_S3C_DEFAULT_WINDOW 2
    2.         #define CONFIG_FB_S3C_NR_BUFFERS 3
    3.         #define CONFIG_FB_S3C_NUM_OVLY_WIN 1
    4.         #define CONFIG_FB_S3C_NUM_BUF_OVLY_WIN 3

        
        CONFIG_FB_S3C_DEFAULT_WINDOW 是指 默认的 window, 0-4。
        CONFIG_FB_S3C_NR_BUFFERS 是指 window 的buffer数,3个就是 trebble-buffer,2个就是double-buffer。
        其中第一个是正在显示的数据,又叫onscreen,其他几个是后台描画的数据,又叫offscreen,通过 flip 操作可以将 onscreen 数据和offscreen 数据交换。
        CONFIG_FB_S3C_NUM_OVLY_WIN 是 OVERLAY window, 0-4。
        CONFIG_FB_S3C_NUM_BUF_OVLY_WIN 是指 OVERLAY window 的buffer数,和 CONFIG_FB_S3C_NR_BUFFERS 一个意思。

            
    3. 显示数据buffer

        内核初始话过程中,为这些window 的 buffer 预留了一部分内存。
        具体看下面的代码:

        初始化函数会首先映射内存空间:

            
     
    复制代码
    1.         MACHINE_START(MINI210, "MINI210")
    2.             /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
    3.             .boot_params    = S5P_PA_SDRAM + 0x100,
    4.             .fixup            = mini210_fixup,
    5.             .init_irq        = s5pv210_init_irq,
    6.             .map_io            = mini210_map_io,
    7.             .init_machine    = mini210_machine_init,
    8.         #ifdef CONFIG_S5P_HIGH_RES_TIMERS
    9.             .timer            = &s5p_systimer,
    10.         #else
    11.             .timer            = &s5p_timer,
    12.         #endif
    13.         MACHINE_END

        
        在映射内存空间中,保留了一部分内存,保留的大小就和window 以及 buffer数有关系:
        大小是 800 x 480 x 2个window x 3个buffer x RGBA的4字节,这是给通常显示数据的,
        除此之外,还预留了 YUV 的数据区域,1280 x 720 x 3个buffer x Y的一个字节数据 + 1280 x 720 x 3个buffer x UV的一个字节数据。
        另外还有一个 4096 字节大小的 数据。

        这些数据我只理解了 RGBA 数据, YUV 的数据不知道是干啥的? 难道是给 HDMI 输出用的?

            
            arch/arm/mach-sp5v210/Mach-mini210.c
            --------------------------------------------------
     
    复制代码
    1.         /* Multimedia support */
    2.         #define LCD_WIDTH                800
    3.         #define LCD_HEIGHT                480
    4.         #define BYTES_PER_PIXEL            4
    5.         #define NUM_BUFFER_OVLY            (CONFIG_FB_S3C_NUM_OVLY_WIN * CONFIG_FB_S3C_NUM_BUF_OVLY_WIN)
    6.         #define NUM_BUFFER                (CONFIG_FB_S3C_NR_BUFFERS + NUM_BUFFER_OVLY)
    7.         #define PXL2FIMD(pixels)        ((pixels) * BYTES_PER_PIXEL * NUM_BUFFER)
    8.         #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD        PXL2FIMD(LCD_WIDTH * LCD_HEIGHT)
    9.         
    10.         static struct s5p_media_device mini210_media_devs[] = {
    11.             ......,
    12.             {
    13.                 .id            = S5P_MDEV_FIMD,
    14.                 .name        = "fimd",
    15.                 .bank        = 1,
    16.                 .memsize    = S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD,
    17.                 .paddr        = 0,
    18.             },
    19.             ......,
    20.         }
    21.         static void __init mini210_map_io(void)
    22.         {
    23.             ......
    24.             frame_size = lcd->width * lcd->height * BYTES_PER_PIXEL;
    25.             fimd_size = ALIGN(frame_size, PAGE_SIZE) * NUM_BUFFER;
    26.             if (frame_size > 0x200000) {
    27.                 fimd_size += ALIGN(frame_size, PAGE_SIZE) * 2;            // Not used
    28.             }
    29.             /* Reserve 0x003f6000 bytes for PVR YUV video, and 1 page */
    30.             fimd_size += ALIGN(1280*720, PAGE_SIZE) * 3;
    31.             fimd_size += ALIGN(1280*360, PAGE_SIZE) * 3 + PAGE_SIZE;
    32.             if (fimd_size != S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD) {
    33.                 mini210_fixup_bootmem(S5P_MDEV_FIMD, fimd_size);
    34.             }
    35.             ......
    36.             s5p_reserve_bootmem(mini210_media_devs, ARRAY_SIZE(mini210_media_devs), S5P_RANGE_MFC);
    37.             ......
    38.         }


        这些内存是PAGE对齐的,对齐的部分参考下面的算法。
        PAGE_SIZE 大小是 4096。


            include/linux/Const.h
            --------------------------------------------------
     
    复制代码
    1.         #ifdef __ASSEMBLY__
    2.         #define _AC(X,Y)    X
    3.         #define _AT(T,X)    X
    4.         #else
    5.         #define __AC(X,Y)    (X##Y)
    6.         #define _AC(X,Y)    __AC(X,Y)
    7.         #define _AT(T,X)    ((T)(X))
    8.         #endif

            
            arch/arm/include/asm/Page.h
            --------------------------------------------------
     
    复制代码
    1.         #define PAGE_SHIFT        12
    2.         #define PAGE_SIZE        (_AC(1,UL) << PAGE_SHIFT)

            
            include/linux/Kernel.h
            --------------------------------------------------
     
    复制代码
    1.         #define __ALIGN_KERNEL_MASK(x, mask)    (((x) + (mask)) & ~(mask))
    2.         #define __ALIGN_KERNEL(x, a)        __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
    3.         #define ALIGN(x, a)        __ALIGN_KERNEL((x), (a))

            

    4. 内核关于FIMD内存预留的相关debug log

        在内核的 boot log 中,我们可以找到关于内存预留的log:

            dmesg log :
            --------------------------------------------------
            [    0.000000] s5p: 13060 kbytes system memory reserved for fimd at 0x3c330000, 1-bank base(0x3c330000) 
            
            [    0.648804] fimd at 0x3c330000



    5. 物理内存视图

        画了一个简单的物理内存布局图,可以看到 RGBA 部分的数据,其实一共给 2 个 window 预留了。

            |              |
            |--------------|
            |              |0x3CFF0000 -- 0x3CFF0FFF || ????
            |--------------|
            |              |0x3CF7F000 -- 0x3CFEFFFF ||
            |--------------|                         ||
            |              |0x3CF0E000 -- 0x3CF7EFFF || treble-buffer YUV( UV ) framebuffer : 1280 x 360 x 1 byte
            |--------------|                         ||
            |              |0x3CE9D000 -- 0x3CF0DFFF ||
            |--------------|                           
            |              |0x3CDBC000 -- 0x3CE9EFFF ||
            |--------------|                         ||
            |              |0x3CCDB000 -- 0x3CDBBFFF || treble-buffer YUV(  Y ) framebuffer : 1280 x 720 x 1 byte
            |--------------|                         ||
            |              |0x3CBFA000 -- 0x3CCDAFFF ||
            |--------------|                           
            |              |0x3CA83000 -- 0x3CBF9FFF ||
            |--------------|                         ||
            |              |0x3C90C000 -- 0x3CA82FFF || treble-buffer RGBA framebuffer : 800 x 480 x 4 bytes
            |--------------|                         ||
            |              |0x3C795000 -- 0x3C90BFFF ||
            |--------------|                           
            |              |0x3C61E000 -- 0x3C794FFF ||
            |--------------|                         ||
            |              |0x3C4A7000 -- 0x3C61DFFF || treble-buffer RGBA framebuffer : 800 x 480 x 4 byte
            |--------------|                         ||
            |              |0x3C330000 -- 0x3C4A6FFF ||
            |--------------|
            |              |

    6. 将预留的物理内存记录到platform信息中

        这些预留的物理内存,在 platform 初始化过程中,会映射到内存空间。
        在开发板初始化过程中,会进行 machine init, 而 machine init 过程中会调用 s3c_fb_set_platdata 初始化 platform 信息。

            
            arch/arm/mach-sp5v210/Mach-mini210.c
            --------------------------------------------------
     
    复制代码
    1.         static struct s3c_platform_fb mini210_fb_data __initdata = {
    2.             .hw_ver            = 0x62,
    3.             .clk_name       = "sclk_fimd",
    4.             .nr_wins        = 5,
    5.             .default_win    = CONFIG_FB_S3C_DEFAULT_WINDOW,
    6.             .swap            = FB_SWAP_WORD | FB_SWAP_HWORD,
    7.         
    8.             .cfg_gpio        = lcd_cfg_gpio,
    9.             .backlight_on    = lcd_backlight_on,
    10.             .backlight_onoff= lcd_backlight_off,
    11.             .reset_lcd        = lcd_reset_lcd,
    12.         };
    13.         static void __init mini210_machine_init(void)
    14.         {
    15.             ......
    16.         #ifdef CONFIG_FB_S3C_MINI210
    17.             {
    18.                 struct s3cfb_lcd *mlcd = mini210_get_lcd();
    19.                 if (!(mlcd->args & 0x0f)) {
    20.                     if (readl(S5PV210_GPF0_BASE + 0x184) & 0x10)
    21.                         mlcd->args |= (1 << 7);
    22.                 }
    23.                 mini210_fb_data.lcd = mlcd;
    24.                 s3c_fb_set_platdata(&mini210_fb_data);
    25.             }
    26.         #endif
    27.             ......
    28.         }
    29.         
    30.         MACHINE_START(MINI210, "MINI210")
    31.             /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
    32.             .boot_params    = S5P_PA_SDRAM + 0x100,
    33.             .fixup            = mini210_fixup,
    34.             .init_irq        = s5pv210_init_irq,
    35.             .map_io            = mini210_map_io,
    36.             .init_machine    = mini210_machine_init,
    37.         #ifdef CONFIG_S5P_HIGH_RES_TIMERS
    38.             .timer            = &s5p_systimer,
    39.         #else
    40.             .timer            = &s5p_timer,
    41.         #endif
    42.         MACHINE_END

        
        在platform初始化过程中,就会通过 pmem_start 和 pmem_size 分别记录这5个window 的物理内存地址和大小。    
        注意的是 platform 初始化,相当于是记录系统都有哪些资源。而设备驱动初始化过程中,比如s3cfb这个fb驱动,则会来使用这些资源。


            arch/arm/plat-s5p/include/plat/Fb.h
            --------------------------------------------------
     
    复制代码
    1.         struct s3c_platform_fb {
    2.             int            hw_ver;
    3.             char        clk_name[16];
    4.             int            nr_wins;
    5.             int            nr_buffers[5];
    6.             int            default_win;
    7.             int            swap;
    8.             phys_addr_t    pmem_start[5]; /* starting physical address of memory region */
    9.             size_t        pmem_size[5]; /* size of memory region */
    10.             void        *lcd;
    11.             void        (*cfg_gpio)(struct platform_device *dev);
    12.             int            (*backlight_on)(struct platform_device *dev);
    13.             int            (*backlight_onoff)(struct platform_device *dev, int onoff);
    14.             int            (*reset_lcd)(struct platform_device *dev);
    15.             int            (*clk_on)(struct platform_device *pdev, struct clk **s3cfb_clk);
    16.             int            (*clk_off)(struct platform_device *pdev, struct clk **clk);
    17.         };

            
            arch/arm/plat-samsung/Dev-fb.c
            --------------------------------------------------
     
    复制代码
    1.        void __init s3c_fb_set_platdata(struct s3c_platform_fb *pd)
    2.         {
    3.             struct s3c_platform_fb *npd;
    4.             struct s3cfb_lcd *lcd;
    5.             phys_addr_t pmem_start;
    6.             int i, default_win, num_overlay_win;
    7.             int frame_size;
    8.         
    9.             if (!pd)
    10.                 pd = &default_fb_data;
    11.         
    12.             npd = kmemdup(pd, sizeof(struct s3c_platform_fb), GFP_KERNEL);
    13.             if (!npd) {
    14.                 printk(KERN_ERR "%s: no memory for platform data ", __func__);
    15.         
    16.             } else {
    17.                 for (i = 0; i < npd->nr_wins && i < NR_BUFFERS; i++) {
    18.                     npd->nr_buffers?? = 1;
    19.                 }
    20.         
    21.                 default_win = npd->default_win;
    22.                 num_overlay_win = CONFIG_FB_S3C_NUM_OVLY_WIN;
    23.                 if (num_overlay_win >= default_win) {
    24.                     printk(KERN_WARNING "%s: NUM_OVLY_WIN should be less than default
    25.                             window number. set to 0. ", __func__);
    26.                     num_overlay_win = 0;
    27.                 }
    28.                 ......
    29.                 
    30.                 /* set starting physical address & size of memory region for
    31.                  * overlay window and default window */
    32.                 pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMD, 1);
    33.                 printk("fimd at 0x%08x ", pmem_start);
    34.         
    35.                 for (i = 0; i < num_overlay_win; i++) {
    36.                     *(npd->nr_buffers+i) = CONFIG_FB_S3C_NUM_BUF_OVLY_WIN;
    37.                     *(npd->pmem_start+i) = pmem_start;
    38.                     *(npd->pmem_size+i) = frame_size *   *(npd->nr_buffers+i);
    39.                     pmem_start += *(npd->pmem_size+i);
    40.                 }
    41.         
    42.                 npd->nr_buffers[default_win] = CONFIG_FB_S3C_NR_BUFFERS;
    43.                 npd->pmem_start[default_win] = pmem_start;
    44.                 npd->pmem_size[default_win] = frame_size * npd->nr_buffers[default_win];
    45.         
    46.         #if defined(CONFIG_MACH_MINI210)
    47.                 npd->pmem_size[default_win] += ALIGN(1280*720, PAGE_SIZE) * 3;
    48.                 npd->pmem_size[default_win] += ALIGN(1280*360, PAGE_SIZE) * 3 + PAGE_SIZE;
    49.         
    50.                 if (frame_size > 0x200000) {                                        // Not Used : frame_size < 0x200000
    51.                     pmem_start += npd->pmem_size[default_win];
    52.                     for (; i < npd->nr_wins; i++) {
    53.                         if (i != default_win) {
    54.                             npd->nr_buffers[i] = 2;
    55.                             npd->pmem_start[i] = pmem_start;
    56.                             npd->pmem_size[i] = frame_size * npd->nr_buffers[i];
    57.                             break;
    58.                         }
    59.                     }
    60.                 }
    61.         #endif
    62.         
    63.                 s3c_device_fb.dev.platform_data = npd;
    64.             }
    65.         }

        
        我们需要留意一下上面这段代码,    
        因为 num_overlay_win 是 CONFIG_FB_S3C_NUM_OVLY_WIN=1,所以 for 循环只执行了一次,那么 window 0 的物理内存地址初始化了。
        然后又手工为 default_win 也就是 CONFIG_FB_S3C_DEFAULT_WINDOW=2 进行了初始化。
        也就是说: window 0 和 window 2 在platform初始化中记录了数据buffer的物理内存地址。
        虽然是 for 循环做的,我认为这个for循环写得不好。



    7. 在s3cfb初始化过程中,为window 关联这些内存

        在初始化过程中,会分配fb设备相关数据结构,并注册fb设备。

            drivers/video/samsung/s3cfb.c
            --------------------------------------------------
     
    复制代码
    1.         static int __devinit s3cfb_probe(struct platform_device *pdev)
    2.         {
    3.             ......
    4.             if (s3cfb_alloc_framebuffer(fbdev)) {
    5.                 ret = -ENOMEM;
    6.                 goto err_alloc;
    7.             }
    8.         
    9.             if (s3cfb_register_framebuffer(fbdev)) {
    10.                 ret = -EINVAL;
    11.                 goto err_register;
    12.             }
    13.             ......
    14.         }


        但是我们发现,只有当 fb 设备对应的 window 是 default window ,也就是 window 2 的时候,
        才会将 window 2 对应的内存映射到内存空间。
        虽然预留的物理内存是 window 0 和 window 2 的,但这个时候 window 0 的物理内存是没有被映射的。


            drivers/video/samsung/s3cfb.c
            --------------------------------------------------
     
    复制代码
    1.         static int s3cfb_alloc_framebuffer(struct s3cfb_global *ctrl)
    2.         {
    3.             ......
    4.             for (i = 0; i < pdata->nr_wins; i++) {
    5.                 ctrl->fb[i] = framebuffer_alloc(sizeof(*ctrl->fb), ctrl->dev);
    6.                 if (!ctrl->fb[i]) {
    7.                     dev_err(ctrl->dev, "not enough memory ");
    8.                     ret = -ENOMEM;
    9.                     goto err_alloc_fb;
    10.                 }
    11.                 ......
    12.                 if (i == pdata->default_win) {
    13.                     if (s3cfb_map_video_memory(ctrl->fb[i])) {
    14.                         dev_err(ctrl->dev,
    15.                             "failed to map video memory "
    16.                             "for default window (%d) ", i);
    17.                         ret = -ENOMEM;
    18.                         goto err_map_video_mem;
    19.                     }
    20.                 }
    21.             }
    22.             ......
    23.         }

        
        这是注册 fb 设备的地方,只要注册成功,/dev 目录下就会有一个 fb 文件。
        虽然只给 window 2 映射了显示数据的内存,但是 for 循环还是将所有的 window 都注册设备文件了。
        这就是为什么 /dev 目录下有 fb0 - fb4 5个fb设备。其实每一个 fb 设备对应一个 window。
        
        另外需要注意的是,第一个注册的 fb 是 fb0,然后依次 ++。
        下面的代码在注册的时候, 首先注册的是 default window 2,然后是 3, 4, 0, 1。
        也就是说 :
        fb0 -> window 2
        fb1 -> window 3
        fb2 -> window 4
        fb3 -> window 0
        fb4 -> window 1

        另外还需要注意的是,如果你直接去 cat  /dev/fbX ,只有 fb0 是成功的,其他全部失败,
        因为到目前位置,只有 fb0 也就是 default window 2,映射了显示数据的内存。

            
            drivers/video/samsung/s3cfb.c
            --------------------------------------------------
     
    复制代码
    1.         static int s3cfb_register_framebuffer(struct s3cfb_global *ctrl)
    2.         {
    3.             ......
    4.             k = 0;
    5.             for (i = pdata->default_win;
    6.                 i < pdata->nr_wins + pdata->default_win; i++) {
    7.                     j = i % pdata->nr_wins;
    8.                     ret = register_framebuffer(ctrl->fb[j]);
    9.                     if (ret) {
    10.                         dev_err(ctrl->dev, "failed to register "
    11.                                 "framebuffer device ");
    12.                         ret = -EINVAL;
    13.                         goto err_register_fb;
    14.                     }
    15.                     pdata->fb_file_minor[ j ] = k;
    16.                     k++;
    17.                     
    18.                     #ifndef CONFIG_FRAMEBUFFER_CONSOLE
    19.                     if (j == pdata->default_win) {
    20.                         s3cfb_check_var(&ctrl->fb[j]->var, ctrl->fb[j]);
    21.                         s3cfb_set_par(ctrl->fb[j]);
    22.                         s3cfb_draw_logo(ctrl->fb[j]);
    23.                     }
    24.                     #endif
    25.             }
    26.             ......
    27.         }


    8. 内核关于显示内存映射的相关debug log

        需要注意一下,部分log中写的是fb2,但这个log是boot阶段的,真正有效的是fb0,
        代码中log写错了,fb 很多应该是 window。fb0 -> window 2


            dmesg log :
            --------------------------------------------------
            [  507.316250] [s3cfb]win 2: pmem_start=0x3c795000
            [  507.316292] [s3cfb][fb2] dma: 0x3c795000, cpu: 0xe1000000, size: 0x0085c000
            
            [  507.333888] PA FB = 0x3C795000, bits per pixel = 32
            [  507.333933] screen width=800 height=480 va=0xdc795000 pa=0x3c795000
            [  507.333987] xres_virtual = 800, yres_virtual = 1440, xoffset = 0, yoffset = 0
            [  507.336543] fb_size=8765440 
            [  507.339351] Back frameBuffer[0].VAddr=dc90c000 PAddr=3c90c000 size=1536000 
            [  507.346146] Back frameBuffer[1].VAddr=dca83000 PAddr=3ca83000 size=1536000 
            [  507.353014] Video Y Buffer[0].VAddr=dcbfa000 PAddr=3cbfa000 size=921600
            [  507.359576] Video Y Buffer[1].VAddr=dccdb000 PAddr=3ccdb000 size=921600 
            [  507.366161] Video Y Buffer[2].VAddr=dcdbc000 PAddr=3cdbc000 size=921600
            [  507.372750] Video UV Buffer[0].VAddr=dce9d000 PAddr=3ce9d000 size=462848
            [  507.379418] Video UV Buffer[1].VAddr=dcf0e000 PAddr=3cf0e000 size=462848
            [  507.386093] Video UV Buffer[2].VAddr=dcf7f000 PAddr=3cf7f000 size=462848


    9. 显示数据使用内存映射的一些细节

        s3cfb_map_video_memory 这个函数用于映射显示数据的内存地址,
        如果是 platform 初始化过程中预留过物理内存,则会使用这个物理内存。
        否则就会临时申请一块内存。

        也就是说 window 0 和 window 2 会使用预留的物理内存。
        window 1 / window 3 / window 4 会使用新申请的内存。


            drivers/video/samsung/s3cfb.c
            --------------------------------------------------
     
    复制代码
    1.         static int s3cfb_map_video_memory(struct fb_info *fb)
    2.         {
    3.             ......
    4.             if (win->owner == DMA_MEM_OTHER) {
    5.                 fix->smem_start = win->other_mem_addr;
    6.                 fix->smem_len = win->other_mem_size;
    7.                 return 0;
    8.             }
    9.         
    10.             if (fb->screen_base)
    11.                 return 0;
    12.         
    13.             if (pdata && pdata->pmem_start[win->id] && (pdata->pmem_size[win->id] >= fix->smem_len)) {
    14.                 fix->smem_start = pdata->pmem_start[win->id];
    15.                 fix->smem_len = pdata->pmem_size[win->id];
    16.                 fb->screen_base = ioremap_wc(fix->smem_start, pdata->pmem_size[win->id]);
    17.         
    18.                 dev_err(fbdev->dev, "[fb%d][win%d]: pmem_start=0x%x ",
    19.                         pdata->fb_file_minor[win->id],win->id, pdata->pmem_start[win->id]);
    20.             } else {
    21.                 fb->screen_base = dma_alloc_writecombine(fbdev->dev,
    22.                         PAGE_ALIGN(fix->smem_len),
    23.                         (unsigned int *)&fix->smem_start, GFP_KERNEL);
    24.             }
    25.             ......
    26.         }


        除了在初始化的时候会映射以外,
        还可一通过 ioctl 来调用 s3cfb_map_video_memory。
        传递的参数是 FBIOPUT_VSCREENINFO。


            drivers/video/samsung/s3cfb.c
            --------------------------------------------------
     
    复制代码
    1.         static int s3cfb_set_par(struct fb_info *fb)
    2.         {
    3.             ......
    4.             if (win->id != pdata->default_win) {
    5.                 fb->fix.line_length = fb->var.xres_virtual *
    6.                                 fb->var.bits_per_pixel / 8;
    7.                 fb->fix.smem_len = fb->fix.line_length * fb->var.yres_virtual;
    8.         
    9.                 s3cfb_map_video_memory(fb);
    10.             }
    11.             ......
    12.         }
    13.         struct fb_ops s3cfb_ops = {
    14.             .fb_set_par = s3cfb_set_par,
    15.         };
    16.         drivers/video/fbmem.c
    17.         --------------------------------------------------
    18.         int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
    19.         {
    20.             ......
    21.             if (info->fbops->fb_set_par) {
    22.                 ret = info->fbops->fb_set_par(info);
    23.                 ......
    24.             }
    25.             ......
    26.         }
    27.         static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
    28.         {
    29.             case FBIOPUT_VSCREENINFO:
    30.                 ......
    31.                 ret = fb_set_var(info, &var);
    32.                 .....
    33.                 break;
    34.         }
    35.         static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    36.         {
    37.             ......
    38.             return do_fb_ioctl(info, cmd, arg);
    39.             ......
    40.         }
    41.         static const struct file_operations fb_fops = {
    42.             ......
    43.             .unlocked_ioctl = fb_ioctl,
    44.             ......
    45.         };



    10. 在s3cfb初始化过程中映射FIMD的控制寄存器,用于控制硬件

        FIMD的控制寄存器和内存是统一编址的,因此可以像使用内存一样访问他们,但前提是需要将他们映射到内存空间。
        内核代码中的寄存器地址定义都能和 SoC 用户手册对应上,我们看看下面的截图,很一致吧。
        但是在看代码的过程中,我发现代码使用的寄存器的一些信息,Soc 用户手册没有,是不是用户手册比较老了?

        
             
     

             
     

        在 s3cfb 模块初始化的过程中,就会将控制寄存器映射到内存中。

            arch/arm/mach-sp5v210/include/mach/map.h
            --------------------------------------------------
     
    复制代码
    1.         #define S5PV210_PA_LCD          (0xF8000000)
    2.         #define S5P_PA_LCD              S5PV210_PA_LCD
    3.         #define S5PV210_SZ_LCD          SZ_1M
    4.         #define S5P_SZ_LCD              S5PV210_SZ_LCD

            
            
            arch/arm/plat-samsung/dev-fb.c
            --------------------------------------------------
     
    复制代码
    1.         static struct resource s3cfb_resource[] = {
    2.             [0] = {
    3.                 .start        = S5P_PA_LCD,
    4.                 .end        = S5P_PA_LCD + S5P_SZ_LCD - 1,
    5.                 .flags        = IORESOURCE_MEM,
    6.             },
    7.             [1] = {
    8.                 .start        = IRQ_LCD1,
    9.                 .end        = IRQ_LCD1,
    10.                 .flags        = IORESOURCE_IRQ,
    11.             },
    12.             [2] = {
    13.                 .start        = IRQ_LCD0,
    14.                 .end        = IRQ_LCD0,
    15.                 .flags        = IORESOURCE_IRQ,
    16.             },
    17.         };
    18.         
    19.         static u64 fb_dma_mask = 0xffffffffUL;
    20.         
    21.         struct platform_device s3c_device_fb = {
    22.             .name            = "s3cfb",
    23.             .id                = -1,
    24.             .num_resources    = ARRAY_SIZE(s3cfb_resource),
    25.             .resource        = s3cfb_resource,
    26.             .dev            = {
    27.                 .dma_mask            = &fb_dma_mask,
    28.                 .coherent_dma_mask    = 0xffffffffUL
    29.             }
    30.         };
    31.         
    32.         drivers/video/samsung/s3cfb.c
    33.         --------------------------------------------------
    34.         static int __devinit s3cfb_probe(struct platform_device *pdev)
    35.         {
    36.             ......
    37.             res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    38.             if (!res) {
    39.                 dev_err(fbdev->dev, "failed to get io memory region ");
    40.                 ret = -EINVAL;
    41.                 goto err_io;
    42.             }
    43.         
    44.             res = request_mem_region(res->start, res->end - res->start + 1, pdev->name);
    45.             if (!res) {
    46.                 dev_err(fbdev->dev, "failed to request io memory region ");
    47.                 ret = -EINVAL;
    48.                 goto err_io;
    49.             }
    50.         
    51.             fbdev->regs = ioremap(res->start, res->end - res->start + 1);
    52.             if (!fbdev->regs) {
    53.                 dev_err(fbdev->dev, "failed to remap io region ");
    54.                 ret = -EINVAL;
    55.                 goto err_mem;
    56.             }
    57.             ......
    58.         }

            
        s3cfb_fimd6x.c这个文件将读写 FIMD 的控制寄存器相关功能都集中在这里了。
        我理解 6x 可能是对应硬件版本号,因为印象中看到硬件版本号好象是 0x62。

        通过设定这些控制寄存器,就能控制比如 window 是否显示啊,显示数据格式是啥啊,显示数据地址在哪里啊,等等。


            drivers/video/samsung/s3cfb_fimd6x.c
            --------------------------------------------------
     
    复制代码
    1.         int s3cfb_display_on(struct s3cfb_global *ctrl)
    2.         {
    3.             u32 cfg;
    4.         
    5.             cfg = readl(ctrl->regs + S3C_VIDCON0);
    6.             cfg |= (S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE);
    7.             writel(cfg, ctrl->regs + S3C_VIDCON0);
    8.         
    9.             dev_dbg(ctrl->dev, "global display is on ");
    10.         
    11.             return 0;
    12.         }



    11. 用户空间的测试用例

        这个用例其实是用来确认 /dev 目录下的 fb0 - fb4 是否好用的,以及 window 之间的alpha透过是否正确。

        关于 window 之间的层次关系可以参考:

             
     

        下面的代码,大概意思是:
        * 打开 window 0 - window 4 对应的 fb 设备文件。
        * 设置这些 window 都是 800 x 480 x RGBA
        * 设置这些 window 内部的显示数据,是从 window 0 - window 4 开始的,分别画 500x480, 400x480, 300x480, 200x480, 100x480 这么大块数据。
        * 让window 的显示数据显示出来。


            s3cfb_test.c
            --------------------------------------------------
     
    复制代码
    1.         #include <sys/types.h>
    2.         #include <sys/stat.h>
    3.         #include <sys/ioctl.h>
    4.         #include <sys/mman.h>
    5.         #include <fcntl.h>
    6.         #include <stdio.h>
    7.         #include <string.h>
    8.         #include <linux/fb.h>
    9.         
    10.         
    11.         #define    WIN0    0
    12.         #define    WIN1    1
    13.         #define    WIN2    2
    14.         #define    WIN3    3
    15.         #define    WIN4    4
    16.         #define    WIN_MAX    5
    17.         
    18.         const char * fb_file_path[ WIN_MAX ] = {
    19.         /* WIN0 */"/dev/fb3",
    20.         /* WIN1 */"/dev/fb4",
    21.         /* WIN2 */"/dev/fb0",
    22.         /* WIN3 */"/dev/fb1",
    23.         /* WIN4 */"/dev/fb2"
    24.         };
    25.         const int fb_x[ WIN_MAX ] = {
    26.         /* WIN0 */500,
    27.         /* WIN1 */400,
    28.         /* WIN2 */300,
    29.         /* WIN3 */200,
    30.         /* WIN4 */100
    31.         };
    32.         const int fb_color[ WIN_MAX ] = {
    33.         /* WIN0 */0x10FFFFFF,
    34.         /* WIN1 */0x10FF0000,
    35.         /* WIN2 */0x1000FF00,
    36.         /* WIN3 */0x100000FF,
    37.         /* WIN4 */0x10000000,
    38.         };
    39.         
    40.         void draw_framebuffer( int win, int fd, int xres, int yres )
    41.         {
    42.             int i, j;
    43.             int *p;
    44.             int color;
    45.             
    46.             p = mmap( NULL, xres*yres*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
    47.             if( !p ){
    48.                 printf( "mmap failed " );
    49.                 return;
    50.             }
    51.         
    52.             /* draw default color with alpha : all pass through*/
    53.             for( j = 0; j < yres; j++ ){
    54.                 for( i = 0; i < xres ; i++ ){
    55.                     *( p + j * xres + i ) = 0x00FFFFFF;
    56.                 }
    57.             }
    58.         
    59.             /* draw color */
    60.             for( j = 0; j < yres; j++ ){
    61.                 for( i = 0; i < fb_x[win] ; i++ ){
    62.                     *( p + j * xres + i ) = fb_color[win];
    63.                 }
    64.             }
    65.         
    66.             munmap( p, xres*yres*4 );
    67.         }
    68.         
    69.         int main( int argc, char *argv[] )
    70.         {
    71.             int i;
    72.             int ret;
    73.             struct fb_var_screeninfo var;
    74.             int    blank;
    75.             int fd[ WIN_MAX ];
    76.         
    77.             /* initial */
    78.             memset( fd, -1, sizeof( fd ) );
    79.         
    80.             /* open framebuffer */
    81.             for( i=0; i<WIN_MAX; i++ ){
    82.                 fd[i] = open( fb_file_path[i], O_RDWR );
    83.                 if( fd[i] < 0 ){
    84.                     printf( "open %s failed ", fb_file_path[i] );
    85.                     goto end;
    86.                 }
    87.             }
    88.             printf( "open framebuffer ok " );
    89.         
    90.             /* set screen info */
    91.             ret = ioctl( fd[WIN2], FBIOGET_VSCREENINFO, &var );
    92.             if( ret < 0 ){
    93.                 printf( "ioctl %s FBIOGET_VSCREENINFO failed ", fb_file_path[WIN2] );
    94.                 goto end;
    95.             }
    96.         
    97.             var.activate = FB_ACTIVATE_FORCE;
    98.             var.yres_virtual = var.yres;
    99.             
    100.             for( i=0; i<WIN_MAX; i++ ){
    101.                 ret = ioctl( fd[i], FBIOPUT_VSCREENINFO, &var );
    102.                 if( ret < 0 ){
    103.                     printf( "ioctl %s FBIOPUT_VSCREENINFO failed ", fb_file_path[i] );
    104.                     goto end;
    105.                 }
    106.             }
    107.             printf( "set screeninfo ok " );
    108.         
    109.             /* draw some color */
    110.             for( i=0; i<WIN_MAX; i++ ){
    111.                 draw_framebuffer( i, fd[i], var.xres, var.yres );
    112.             }
    113.             printf( "draw color ok " );
    114.         
    115.             /* show window */
    116.             blank = FB_BLANK_UNBLANK;
    117.         
    118.             for( i=0; i<WIN_MAX; i++ ){
    119.                 ret = ioctl( fd[i], FBIOBLANK, blank );
    120.                 if( ret < 0 ){
    121.                     printf( "ioctl %s FBIOBLANK failed ", fb_file_path[i] );
    122.                     goto end;
    123.                 }
    124.             }
    125.             printf( "show window ok " );
    126.         
    127.             /* wait input */    
    128.             getchar();
    129.         end:
    130.             return 0;
    131.         }

            
        下面是屏幕截图,其实我对 RGB 不太了解,不知到什么值应该是什么颜色,但 只描画了红/绿/蓝 三种颜色,而屏幕上不是这三种颜色。
        说明 alpha blending 应该是正常工作了的。


             
     

    END
  • 相关阅读:
    opencv.js小项目demo
    face-api.js 前端人脸识别,人脸检测,登录认证
    note.js 引用jQuery,
    note.js使用express创建项目的步骤以及ip和端口配置
    学习kettle遇到的问题
    Linux 后台执行python或者java代码的命令
    kettle入门大数据管理工具
    java保留2位或n位小数
    快速排序基本思想,递归写法,python和java编写快速排序
    学了一天的golang从入门到放弃
  • 原文地址:https://www.cnblogs.com/lihaiping/p/frambuffer.html
Copyright © 2020-2023  润新知