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

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 DFBResult
dfb_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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值