DirectFB学习之修改FrameBuffer驱动支持双缓冲

来源:互联网 发布:java 泛型 多继承 编辑:程序博客网 时间:2024/06/05 05:14

nuc972开发板购买地址,感谢支持

DirectFB学习之修改FrameBuffer驱动支持双缓冲

折腾了一段时间基于nuc972平台的2D硬件加速驱动终于在DirectFB上跑起来了,但是我发现只要我想在独占模式下创建Primary Sufrace并指定为Video memory时会失败,即使我在正常模式下创建成功了,绘图也并不会对Primary Sufrace进行硬件加速,提示失败的原因是Primary Sufrace的内存是来自system memory,创建Primary Sufrace的代码如下:

.............................................................................desc.flags = DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT;desc.caps  = DSCAPS_PRIMARY | DSCAPS_FLIPPING;desc.caps &= ~(DSCAPS_SYSTEMONLY);desc.caps |= DSCAPS_VIDEOONLY;desc.width = 800;desc.height = 480;if (dest_format != DSPF_UNKNOWN) {    desc.flags       |= DSDESC_PIXELFORMAT;    desc.pixelformat  = dest_format;}dfb->SetCooperativeLevel( dfb, DFSCL_FULLSCREEN );/* Create a primary surface. */ret = dfb->CreateSurface( dfb, &desc, &dest );.............................................................................

我有足够的video memory为什么Primary Sufrace的内存还会从system memory来呢,后面实在是没思路了,然后注意到DirectFB启动的时候有几条警告信息,心想先把这几条警告信息解决也许问题就解决了,果不其然,我注意到如下一条警告信息:

.............................................................................(!) DirectFB/DirectFBCreate: Setting desktop buffer mode failed!     -> No virtual resolution support or not enough memory?        Falling back to system back buffer..............................................................................

由上面这条警告信息一路看下去,最后找到是如下检测代码返回失败了。

.............................................................................static DFBResultdfb_fbdev_mode_to_var( const VideoMode           *mode,                       DFBSurfacePixelFormat      pixelformat,                       unsigned int               vxres,                       unsigned int               vyres,                       unsigned int               xoffset,                       unsigned int               yoffset,                       DFBDisplayLayerBufferMode  buffermode,                       struct fb_var_screeninfo  *ret_var ){.............................................................................     /* Set buffer mode */     switch (buffermode) {          case DLBM_TRIPLE:               if (shared->fix.ypanstep == 0 && shared->fix.ywrapstep == 0)                    return DFB_UNSUPPORTED;               var.yres_virtual *= 3;               break;          case DLBM_BACKVIDEO:               if (shared->fix.ypanstep == 0 && shared->fix.ywrapstep == 0)                    return DFB_UNSUPPORTED;               var.yres_virtual *= 2;               break;          case DLBM_BACKSYSTEM:          case DLBM_FRONTONLY:               break;          default:               return DFB_UNSUPPORTED;     }.............................................................................}.............................................................................

当我的buffermode设置为DLBM_BACKVIDEO时结果因为条件if (shared->fix.ypanstep == 0 && shared->fix.ywrapstep == 0)成立了而返回DFB_UNSUPPORTED了。

问题原因终于要浮现出来了,但fix.ypanstepfix.ywrapstep是什么鬼,很显然他们是来自于FrameBuffer驱动返回的值,但我对FrameBuffer驱动了解比较少,一下不知道它们是做什么用的,上网查之,发现这两个变量的设置是和FrameBuffer驱动是否支持双缓冲有关的,它们在内核代码的fb_fix_screeninfo结构体内,如下:

.............................................................................struct fb_fix_screeninfo {    char id[16];            /* identification string eg "TT Builtin" */    unsigned long smem_start;   /* Start of frame buffer mem */                    /* (physical address) */    __u32 smem_len;         /* Length of frame buffer mem */    __u32 type;         /* see FB_TYPE_*        */    __u32 type_aux;         /* Interleave for interleaved Planes */    __u32 visual;           /* see FB_VISUAL_*      */     __u16 xpanstep;         /* zero if no hardware panning  */    __u16 ypanstep;         /* zero if no hardware panning  */    __u16 ywrapstep;        /* zero if no hardware ywrap    */    __u32 line_length;      /* length of a line in bytes    */    unsigned long mmio_start;   /* Start of Memory Mapped I/O   */                    /* (physical address) */    __u32 mmio_len;         /* Length of Memory Mapped I/O  */    __u32 accel;            /* Indicate to driver which */                    /*  specific chip/card we have  */    __u16 capabilities;     /* see FB_CAP_*         */    __u16 reserved[2];      /* Reserved for future compatibility */};.............................................................................

虽然给了注释但我看完还是一脸茫然的,看了几篇帖子后才明白它们是用来配置显卡是否支持平移显示,即切换显示缓冲区,我们知道我们在LCD上看的内容多是对应的一块连续的内存的,硬件自动的根据行场时钟把这块内存的东西扫描到LCD上显示出来,所以假如我们改变扫描开始的内存地址是不是可以控制LCD上显示的内存区域,这样慢慢增加扫描起始地址,显示内容就好像是在平移了。是不是很酷,这好像是实现了动画的效果了,那xpanstepypanstep指定的就是可以支持水平的平移还是垂直的平移了,这貌似会跟LCD控制器的扫描方向有关,即先扫行还是先扫列这样,而至于ywrapstep我还没搞明白,还希望大牛能指点下。

既然明白了其中的道理,那要怎么来实现它们呢,下面这篇文章给了我很大的帮助,感谢原作者。

Android图形系统的分析与移植–七、双缓冲framebuffer的实现

从中我们大概知道我要做如下动作:

  1. 设置硬件支持的显示平移的方式
  2. 分配更多的显存以支持显示平移,或者说用于双缓冲
  3. 实现显示平移函数fb_pan_display

如下我是针对nuc972平台的实现如下我们设置平移方式为支持垂直平移,在FrameBuffer驱动的probe函数中设置。

.............................................................................fbinfo->fix.xpanstep        = 0;fbinfo->fix.ypanstep        = 1; fbinfo->fix.ywrapstep       = 0;.............................................................................

这么一来我们的显示的区域应该相应的加大,这些信息在fb_var_screeninfo结构体中设置,如上我们加大的y方向,所以我们要设置其中的yres_virtual的值,这里我们加大一倍,即双缓冲其实是两块内存的切换。如下:

............................................................................./* it should be the same size as the display */var->xres_virtual   = display->xres;var->yres_virtual   = display->yres * 2;var->height     = display->height;var->width      = display->width;.............................................................................

当然还有显存的加大,即DirectFB中的video memory,这里就不描述了,下面看下怎么实现平移函数fb_pan_display,如下是它在nuc972平台上的实现:

............................................................................./* pan display */static int nuc970fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){    struct nuc970fb_info *fbi = info->par;    void __iomem *regs = fbi->io;    u32 lcd_va_fbctrl = 0x00;    unsigned long smem_start = (info->fix.smem_start + var->yoffset * info->fix.line_length            + var->xoffset * info->var.bits_per_pixel / 8);    lcd_va_fbctrl = readl(regs + REG_LCM_VA_FBCTRL);    if(lcd_va_fbctrl & 0x40000000)    {        writel(smem_start, regs + REG_LCM_VA_BADDR1);        lcd_va_fbctrl |= 0x40000000;    }    else    {        writel(smem_start, regs + REG_LCM_VA_BADDR0);        lcd_va_fbctrl &= (~0x40000000);    }    writel(lcd_va_fbctrl, regs + REG_LCM_VA_FBCTRL);    return 0x00;}.............................................................................

要搞明白它,我们主要明白数据手册中关于LCD控制器中关于VA_FBCTRL寄存器的描述,关于它的描述如下:

这里写图片描述

如上描述我们只要初始化时使能双缓冲切换功能,即置位DB_EN,然后操作位START_BUF即可实现LCD缓冲区的起始地址的切换了,它们分别存放在VA_BADDR0和VA_BADDR1中,转换为程序实现即是nuc970fb_pan_display函数的实现。

即使硬件上没有提供DB_EN、VA_BADDR0和VA_BADDR1这样的寄存器我们仍然是可以通过改变LCD控制器的framebuffer起始地址来实现这一点。

到这里双缓冲驱动终于欢快的跑起来了。

小结

从这里我们也发现DirectFB为每个创建的Sufrace多提供了双缓冲的支持,所以它在创建Primary Sufrace时会要求我们的FrameBuffer驱动支持双缓冲驱动,当它发现我们的FrameBuffer驱动不支持双缓冲驱动的时候,它就会自己从system memory里面分配出一块内存来实现双缓冲,而从system memory分配的内存是不支持2D硬件加速的,所以这也是为什么我之前对Primary Sufrace一直不能使用硬件加速的原因了。

0 0