原文: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. static struct s5p_media_device mini210_media_devs[] = {
  10. ......,
  11. {
  12. .id            = S5P_MDEV_FIMD,
  13. .name        = "fimd",
  14. .bank        = 1,
  15. .memsize    = S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD,
  16. .paddr        = 0,
  17. },
  18. ......,
  19. }
  20. static void __init mini210_map_io(void)
  21. {
  22. ......
  23. frame_size = lcd->width * lcd->height * BYTES_PER_PIXEL;
  24. fimd_size = ALIGN(frame_size, PAGE_SIZE) * NUM_BUFFER;
  25. if (frame_size > 0x200000) {
  26. fimd_size += ALIGN(frame_size, PAGE_SIZE) * 2;            // Not used
  27. }
  28. /* Reserve 0x003f6000 bytes for PVR YUV video, and 1 page */
  29. fimd_size += ALIGN(1280*720, PAGE_SIZE) * 3;
  30. fimd_size += ALIGN(1280*360, PAGE_SIZE) * 3 + PAGE_SIZE;
  31. if (fimd_size != S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD) {
  32. mini210_fixup_bootmem(S5P_MDEV_FIMD, fimd_size);
  33. }
  34. ......
  35. s5p_reserve_bootmem(mini210_media_devs, ARRAY_SIZE(mini210_media_devs), S5P_RANGE_MFC);
  36. ......
  37. }

这些内存是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. .cfg_gpio        = lcd_cfg_gpio,
  8. .backlight_on    = lcd_backlight_on,
  9. .backlight_onoff= lcd_backlight_off,
  10. .reset_lcd        = lcd_reset_lcd,
  11. };
  12. static void __init mini210_machine_init(void)
  13. {
  14. ......
  15. #ifdef CONFIG_FB_S3C_MINI210
  16. {
  17. struct s3cfb_lcd *mlcd = mini210_get_lcd();
  18. if (!(mlcd->args & 0x0f)) {
  19. if (readl(S5PV210_GPF0_BASE + 0x184) & 0x10)
  20. mlcd->args |= (1 << 7);
  21. }
  22. mini210_fb_data.lcd = mlcd;
  23. s3c_fb_set_platdata(&mini210_fb_data);
  24. }
  25. #endif
  26. ......
  27. }
  28. MACHINE_START(MINI210, "MINI210")
  29. /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
  30. .boot_params    = S5P_PA_SDRAM + 0x100,
  31. .fixup            = mini210_fixup,
  32. .init_irq        = s5pv210_init_irq,
  33. .map_io            = mini210_map_io,
  34. .init_machine    = mini210_machine_init,
  35. #ifdef CONFIG_S5P_HIGH_RES_TIMERS
  36. .timer            = &s5p_systimer,
  37. #else
  38. .timer            = &s5p_timer,
  39. #endif
  40. 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. if (!pd)
  9. pd = &default_fb_data;
  10. npd = kmemdup(pd, sizeof(struct s3c_platform_fb), GFP_KERNEL);
  11. if (!npd) {
  12. printk(KERN_ERR "%s: no memory for platform data\n", __func__);
  13. } else {
  14. for (i = 0; i < npd->nr_wins && i < NR_BUFFERS; i++) {
  15. npd->nr_buffers?? = 1;
  16. }
  17. default_win = npd->default_win;
  18. num_overlay_win = CONFIG_FB_S3C_NUM_OVLY_WIN;
  19. if (num_overlay_win >= default_win) {
  20. printk(KERN_WARNING "%s: NUM_OVLY_WIN should be less than default \
  21. window number. set to 0.\n", __func__);
  22. num_overlay_win = 0;
  23. }
  24. ......
  25. /* set starting physical address & size of memory region for
  26. * overlay window and default window */
  27. pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMD, 1);
  28. printk("fimd at 0x%08x\n", pmem_start);
  29. for (i = 0; i < num_overlay_win; i++) {
  30. *(npd->nr_buffers+i) = CONFIG_FB_S3C_NUM_BUF_OVLY_WIN;
  31. *(npd->pmem_start+i) = pmem_start;
  32. *(npd->pmem_size+i) = frame_size *   *(npd->nr_buffers+i);
  33. pmem_start += *(npd->pmem_size+i);
  34. }
  35. npd->nr_buffers[default_win] = CONFIG_FB_S3C_NR_BUFFERS;
  36. npd->pmem_start[default_win] = pmem_start;
  37. npd->pmem_size[default_win] = frame_size * npd->nr_buffers[default_win];
  38. #if defined(CONFIG_MACH_MINI210)
  39. npd->pmem_size[default_win] += ALIGN(1280*720, PAGE_SIZE) * 3;
  40. npd->pmem_size[default_win] += ALIGN(1280*360, PAGE_SIZE) * 3 + PAGE_SIZE;
  41. if (frame_size > 0x200000) {                                        // Not Used : frame_size < 0x200000
  42. pmem_start += npd->pmem_size[default_win];
  43. for (; i < npd->nr_wins; i++) {
  44. if (i != default_win) {
  45. npd->nr_buffers[i] = 2;
  46. npd->pmem_start[i] = pmem_start;
  47. npd->pmem_size[i] = frame_size * npd->nr_buffers[i];
  48. break;
  49. }
  50. }
  51. }
  52. #endif
  53. s3c_device_fb.dev.platform_data = npd;
  54. }
  55. }

我们需要留意一下上面这段代码,    
    因为 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. if (s3cfb_register_framebuffer(fbdev)) {
  9. ret = -EINVAL;
  10. goto err_register;
  11. }
  12. ......
  13. }

但是我们发现,只有当 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\n");
  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)\n", 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\n");
  12. ret = -EINVAL;
  13. goto err_register_fb;
  14. }
  15. pdata->fb_file_minor[ j ] = k;
  16. k++;
  17. #ifndef CONFIG_FRAMEBUFFER_CONSOLE
  18. if (j == pdata->default_win) {
  19. s3cfb_check_var(&ctrl->fb[j]->var, ctrl->fb[j]);
  20. s3cfb_set_par(ctrl->fb[j]);
  21. s3cfb_draw_logo(ctrl->fb[j]);
  22. }
  23. #endif
  24. }
  25. ......
  26. }

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. if (fb->screen_base)
  10. return 0;
  11. if (pdata && pdata->pmem_start[win->id] && (pdata->pmem_size[win->id] >= fix->smem_len)) {
  12. fix->smem_start = pdata->pmem_start[win->id];
  13. fix->smem_len = pdata->pmem_size[win->id];
  14. fb->screen_base = ioremap_wc(fix->smem_start, pdata->pmem_size[win->id]);
  15. dev_err(fbdev->dev, "[fb%d][win%d]: pmem_start=0x%x\n",
  16. pdata->fb_file_minor[win->id],win->id, pdata->pmem_start[win->id]);
  17. } else {
  18. fb->screen_base = dma_alloc_writecombine(fbdev->dev,
  19. PAGE_ALIGN(fix->smem_len),
  20. (unsigned int *)&fix->smem_start, GFP_KERNEL);
  21. }
  22. ......
  23. }

除了在初始化的时候会映射以外,
    还可一通过 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. s3cfb_map_video_memory(fb);
  9. }
  10. ......
  11. }
  12. struct fb_ops s3cfb_ops = {
  13. .fb_set_par = s3cfb_set_par,
  14. };
  15. drivers/video/fbmem.c
  16. --------------------------------------------------
  17. int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
  18. {
  19. ......
  20. if (info->fbops->fb_set_par) {
  21. ret = info->fbops->fb_set_par(info);
  22. ......
  23. }
  24. ......
  25. }
  26. static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
  27. {
  28. case FBIOPUT_VSCREENINFO:
  29. ......
  30. ret = fb_set_var(info, &var);
  31. .....
  32. break;
  33. }
  34. static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  35. {
  36. ......
  37. return do_fb_ioctl(info, cmd, arg);
  38. ......
  39. }
  40. static const struct file_operations fb_fops = {
  41. ......
  42. .unlocked_ioctl = fb_ioctl,
  43. ......
  44. };

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. static u64 fb_dma_mask = 0xffffffffUL;
  19. struct platform_device s3c_device_fb = {
  20. .name            = "s3cfb",
  21. .id                = -1,
  22. .num_resources    = ARRAY_SIZE(s3cfb_resource),
  23. .resource        = s3cfb_resource,
  24. .dev            = {
  25. .dma_mask            = &fb_dma_mask,
  26. .coherent_dma_mask    = 0xffffffffUL
  27. }
  28. };
  29. drivers/video/samsung/s3cfb.c
  30. --------------------------------------------------
  31. static int __devinit s3cfb_probe(struct platform_device *pdev)
  32. {
  33. ......
  34. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  35. if (!res) {
  36. dev_err(fbdev->dev, "failed to get io memory region\n");
  37. ret = -EINVAL;
  38. goto err_io;
  39. }
  40. res = request_mem_region(res->start, res->end - res->start + 1, pdev->name);
  41. if (!res) {
  42. dev_err(fbdev->dev, "failed to request io memory region\n");
  43. ret = -EINVAL;
  44. goto err_io;
  45. }
  46. fbdev->regs = ioremap(res->start, res->end - res->start + 1);
  47. if (!fbdev->regs) {
  48. dev_err(fbdev->dev, "failed to remap io region\n");
  49. ret = -EINVAL;
  50. goto err_mem;
  51. }
  52. ......
  53. }

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. cfg = readl(ctrl->regs + S3C_VIDCON0);
  5. cfg |= (S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE);
  6. writel(cfg, ctrl->regs + S3C_VIDCON0);
  7. dev_dbg(ctrl->dev, "global display is on\n");
  8. return 0;
  9. }

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

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

END

最新文章

  1. MongoDB学习笔记~为IMongoRepository接口添加了排序和表达式树,针对官方驱动
  2. Redis 基本操作
  3. linux的一些常用命令
  4. top命令下的各种指标意义
  5. iOS UITableView UIScrollView 的支持触摸事件
  6. cadence 16.6 Pspice 仿真步骤
  7. background不能填充margin的问题
  8. OI路上-NOIP100天冲刺计划
  9. useradd、passwd、userdel
  10. 一种解决的方法:CGContextSaveGState: invalid context 0x0
  11. 深入理解JAVA的多态性[转]
  12. Keepalived+Nginx提供前端负载均衡+主从双机热备+自动切换
  13. python实战===输入密码以******的形式在cmd中展示
  14. MySQL MERGE存储引擎
  15. ionic app开发遇到的问题
  16. Python中的iteritems()和items()
  17. ARM JTAG 调试原理
  18. Windows下MFC程序利用LockCop解决死锁
  19. (原)关于sdl在部分机器上做视频显示,改变显示窗口大小会崩溃
  20. Golang 知识图谱

热门文章

  1. django 在建模时的一个手贱
  2. Spring中三种配置Bean的方式
  3. AngularJS 使用 UI Router 实现表单向导
  4. 重温java中的String,StringBuffer,StringBuilder类
  5. C语言:冒泡排序法:将若干字符串按字母顺序(由小到大)排序输出
  6. 用rfkill命令管理蓝牙和wifi
  7. vim:去掉响铃
  8. WebService之CXF注解之四(測试类)
  9. TreeView 高速单击时不运行AfterCheck时间
  10. SourceInsight-显示文件完整路径