首页 > 学院 > 开发设计 > 正文

[IMX6Q]LCD参数匹配过程分析

2019-11-09 16:51:13
字体:
来源:转载
供稿:网友

Platform: IMX6Q

OS: Android 4.4

本文只讨论lvds接口的是lcd参数匹配的过程,mipi dsi以及其他接口部分会有一点差异。

核心函数fb_find_mode(),在分析之前先了解下几个参数。

重要参数说明: 

1. ldb.c中的 ldb_modedb

[cpp] view plain copystatic struct fb_videomode ldb_modedb[] = {      {       "LDB-WXGA", 60, 1280, 800, 14065,       40, 40,       10, 3,       80, 10,       0,       FB_VMODE_NONINTERLACED,       FB_MODE_IS_DETAILED,},      {       "LDB-XGA", 60, 1024, 768, 15385,       220, 40,       21, 7,       60, 10,       0,       FB_VMODE_NONINTERLACED,       FB_MODE_IS_DETAILED,},      {       "LDB-1080P60", 60, 1920, 1080, 7692,       100, 40,       30, 3,       10, 2,       0,       FB_VMODE_NONINTERLACED,       FB_MODE_IS_DETAILED,},       {       "LDB-WSVGA", //Name       60, //refresh       1024, //xres       600, //yres       19531,//pixclock       220, 40,//left/right margin       21, 7,//upper/lower margin       60, 7,//hsync_len,vsync_len       0,//sync       FB_VMODE_NONINTERLACED,//vmode       FB_MODE_IS_DETAILED,},  };  static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb);  系统lvds接口支持的lcd时序参数都在此了。

2. board-mx6-xxx.c中的ipuv3_fb_platform_data结构

本例是:

[cpp] view plain copystatic struct ipuv3_fb_platform_data tek_fb_data[] = {      { /*fb0*/      .disp_dev = "ldb",      .interface_pix_fmt = IPU_PIX_FMT_RGB24,      .mode_str = "LDB-WSVGA",      .default_bpp = 32,      .int_clk = false,      .late_init = false,      }, {      .disp_dev = "hdmi",      .interface_pix_fmt = IPU_PIX_FMT_RGB24,      .mode_str = "1920x1080M@60",      .default_bpp = 32,      .int_clk = false,      .late_init = false,      },      {      .disp_dev = "lcd",      .interface_pix_fmt = IPU_PIX_FMT_RGB565,      .mode_str = "CLAA-WVGA",      .default_bpp = 16,      .int_clk = false,      .late_init = false,      }, {      .disp_dev = "ldb",      .interface_pix_fmt = IPU_PIX_FMT_RGB666,      .mode_str = "LDB-VGA",      .default_bpp = 16,      .int_clk = false,      .late_init = false,      },  };  fb_find_mode()用到其mode_str里的值去ldb_modedb查找有没有对应lcd时序参数。

此mode_str其实就是后面会提到的mode_options, 格式如下:

  <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or    <name>[-<bpp>][@<refresh>]

所以有两种类型:

1. 字符规则形, 如 “LDB-WXVGA”

2. 数字规则形,如"1920*1080"

具体各个参数意义可参照fb_find_mode()函数注释。

3.  kernel cmdline里设置的

本例是:

video=mxcfb0:dev=ldb,LDB-WSVGA,if=RGB24,bpp=32

它会覆盖 tek_fb_data中的值,覆盖规则根据mxcfb后面的值。

比如mxcfb0覆盖tek_fb_data[0], 以此类推。

了解了参数意义之后下面就方便理解了.

系统有如下调用:

[cpp] view plain copystatic int ldb_disp_init(struct mxc_dispdrv_handle *disp,      struct mxc_dispdrv_setting *setting) {  ......      ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,                  ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp);  ......  }  本例:

setting->dft_mode_str为: "LDB-WSVGA"

setting->default_bpp为: 32

下面看代码执行流程:

[cpp] view plain copy/**  *    fb_find_mode - finds a valid video mode  *    @var: frame buffer user defined part of display  *    @info: frame buffer info structure  *    @mode_option: string video mode to find  *    @db: video mode database  *    @dbsize: size of @db  *    @default_mode: default video mode to fall back to  *    @default_bpp: default color depth in bits per pixel  *  *    Finds a suitable video mode, starting with the specified mode  *    in @mode_option with fallback to @default_mode.  If  *    @default_mode fails, all modes in the video mode database will  *    be tried.  *  *    Valid mode specifiers for @mode_option:  *  *    <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or  *    <name>[-<bpp>][@<refresh>]  *  *    with <xres>, <yres>, <bpp> and <refresh> decimal numbers and  *    <name> a string.  *  *      If 'M' is PResent after yres (and before refresh/bpp if present),  *      the function will compute the timings using VESA(tm) Coordinated  *      Video Timings (CVT).  If 'R' is present after 'M', will compute with  *      reduced blanking (for flatpanels).  If 'i' is present, compute  *      interlaced mode.  If 'm' is present, add margins equal to 1.8%  *      of xres rounded down to 8 pixels, and 1.8% of yres. The char  *      'i' and 'm' must be after 'M' and 'R'. Example:  *  *      1024x768MR-8@60m - Reduced blank with margins at 60Hz.  *  *    NOTE: The passed struct @var is _not_ cleared!  This allows you  *    to supply values for e.g. the grayscale and accel_flags fields.  *  *    Returns zero for failure, 1 if using specified @mode_option,  *    2 if using specified @mode_option with an ignored refresh rate,  *    3 if default mode is used, 4 if fall back to any valid mode.  *  */  int fb_find_mode(struct fb_var_screeninfo *var,           struct fb_info *info, const char *mode_option,           const struct fb_videomode *db, unsigned int dbsize,           const struct fb_videomode *default_mode,           unsigned int default_bpp)  {      int i;        /* Set up defaults */      /*参数未给则使用modedb。*/      if (!db) {      db = modedb;      dbsize = ARRAY_SIZE(modedb);      }        /*如果没设置则使用db[0]的值,此例传进来的本身就是db[0]*/      if (!default_mode)      default_mode = &db[0];      /*没有设置bpp则默认使用8bpp,本例是32*/      if (!default_bpp)      default_bpp = 8;        /* Did the user specify a video mode? */      if (!mode_option)      mode_option = fb_mode_option;      /*本例是“LDB-WSVGA”*/      if (mode_option) {      const char *name = mode_option;      unsigned int namelen = strlen(name);      int res_specified = 0, bpp_specified = 0, refresh_specified = 0;      unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;      int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;      u32 best, diff, tdiff;         /*数字格式规则形才会跑下面的循环*/      for (i = namelen-1; i >= 0; i--) {          switch (name[i]) {                  /*@后面的是刷新频率*/                  case '@':              namelen = i;              if (!refresh_specified && !bpp_specified &&              !yres_specified) {              refresh = simple_strtol(&name[i+1], NULL, 10);              refresh_specified = 1;              if (cvt || rb)                  cvt = 0;              } else              goto done;              break;                  /*后面是bpp*/                  case '-':              namelen = i;              printk("line:%d/n", __LINE__);              if (!bpp_specified && !yres_specified) {              bpp = simple_strtol(&name[i+1], NULL, 10);              bpp_specified = 1;              if (cvt || rb)                  cvt = 0;              } else              goto done;              break;                  /*获取yres*/          case 'x':              if (!yres_specified) {              yres = simple_strtol(&name[i+1], NULL, 10);              yres_specified = 1;              } else              goto done;              break;          case '0' ... '9':              break;          case 'M':              if (!yres_specified)              cvt = 1;              break;          case 'R':              if (!cvt)              rb = 1;              break;          case 'm':              if (!cvt)              margins = 1;              break;          case 'i':              if (!cvt)              interlace = 1;              break;          default:              goto done;          }      }          /*如果yres有值,那么也获取xres.*/          if (i < 0 && yres_specified) {          xres = simple_strtol(name, NULL, 10);          res_specified = 1;      }  done:          /*不会跑这里*/          if (cvt) {          struct fb_videomode cvt_mode;          int ret;            DPRINTK("CVT mode %dx%d@%dHz%s%s%s/n", xres, yres,              (refresh) ? refresh : 60, (rb) ? " reduced blanking" :              "", (margins) ? " with margins" : "", (interlace) ?              " interlaced" : "");            memset(&cvt_mode, 0, sizeof(cvt_mode));          cvt_mode.xres = xres;          cvt_mode.yres = yres;          cvt_mode.refresh = (refresh) ? refresh : 60;            if (interlace)          cvt_mode.vmode |= FB_VMODE_INTERLACED;          else          cvt_mode.vmode &= ~FB_VMODE_INTERLACED;            ret = fb_find_mode_cvt(&cvt_mode, margins, rb);            if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {          DPRINTK("modedb CVT: CVT mode ok/n");          return 1;          }            DPRINTK("CVT mode invalid, getting mode from database/n");      }        DPRINTK("Trying specified video mode%s %ix%i/n",          refresh_specified ? "" : " (ignoring refresh rate)", xres, yres);           /*刷新率没指定*/         if (!refresh_specified) {          /*          * If the caller has provided a custom mode database and a          * valid monspecs structure, we look for the mode with the          * highest refresh rate.  Otherwise we play it safe it and          * try to find a mode with a refresh rate closest to the          * standard 60 Hz.          */          if (db != modedb &&              info->monspecs.vfmin && info->monspecs.vfmax &&              info->monspecs.hfmin && info->monspecs.hfmax &&              info->monspecs.dclkmax) {                refresh = 1000;          } else {                      /*默认使用60HZ*/                      refresh = 60;          }      }        diff = -1;      best = -1;          /*根据名字以及或者分辨率来匹配。*/          for (i = 0; i < dbsize; i++) {          if ((name_matches(db[i], name, namelen) ||              (res_specified && res_matches(db[i], xres, yres))) &&              !fb_try_mode(var, info, &db[i], bpp)) {                         /*刷新率也匹配的时候就认准你了!*/                         if (refresh_specified && db[i].refresh == refresh) {                  return 1;              } else {                                /*刷新率不一样就找差得最少的*/                                  if (abs(db[i].refresh - refresh) < diff) {                      diff = abs(db[i].refresh - refresh);                      best = i;                  }              }          }      }      /*得到刷新率差得最少的db,然后返回*/         if (best != -1) {          fb_try_mode(var, info, &db[best], bpp);          return (refresh_specified) ? 2 : 1;      }        /*跑到这里说明名字和分辨率都不匹配。*/      diff = 2 * (xres + yres);      best = -1;      DPRINTK("Trying best-fit modes/n");          /*找到分辨率最小的那组数据。*/          for (i = 0; i < dbsize; i++) {                  DPRINTK("Trying %ix%i/n", db[i].xres, db[i].yres);          if (!fb_try_mode(var, info, &db[i], bpp)) {               tdiff = abs(db[i].xres - xres) +                  abs(db[i].yres - yres);                 /*              * Penalize modes with resolutions smaller              * than requested.              */              if (xres > db[i].xres || yres > db[i].yres)                  tdiff += xres + yres;                         /*差值大的会被保留,说白了,最终就是找到分辨率最小的那组参数。*/              if (diff > tdiff) {                  diff = tdiff;                  best = i;              }          }      }         /*获取best对应的var参数。*/         if (best != -1) {          fb_try_mode(var, info, &db[best], bpp);          return 5;      }      }        /*运行到这里有两种情况,      1. 字母规则型(如LDB-WXVGA),那就是名字不匹配,并且参数检查失败,。      2. 数字规则性(如1920x1080), 那就是名字不匹配 && 分辨率比ldb_modedb中的小上两倍以上(比如1920x1080 和 320x240)。 */      DPRINTK("Trying default video mode/n");      if (!fb_try_mode(var, info, default_mode, default_bpp))      return 3;        DPRINTK("Trying all modes/n");      /*默认的还失败那我只能随便找一个了。*/      for (i = 0; i < dbsize; i++)      if (!fb_try_mode(var, info, &db[i], default_bpp))          return 4;        /*随便都不行,那我只能失败了...*/      DPRINTK("No valid mode found/n");      return 0;  }   

  其实本例中fb_try_mode返回的都是0,看代码,这里的作用基本上看成是得到当前对应的db值然后放再var中

供后面的framebuffer driver使用。

kernel_imx/drivers/video/modedb.c

[cpp] view plain copystatic int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,                 const struct fb_videomode *mode, unsigned int bpp)  {      int err = 0;        DPRINTK("Trying mode %s %dx%d-%d@%d/n", mode->name ? mode->name : "noname",          mode->xres, mode->yres, bpp, mode->refresh);      var->xres = mode->xres;      var->yres = mode->yres;      var->xres_virtual = mode->xres;      var->yres_virtual = mode->yres;      var->xoffset = 0;      var->yoffset = 0;      var->bits_per_pixel = bpp;      var->activate |= FB_ACTIVATE_TEST;      var->pixclock = mode->pixclock;      var->left_margin = mode->left_margin;      var->right_margin = mode->right_margin;      var->upper_margin = mode->upper_margin;      var->lower_margin = mode->lower_margin;      var->hsync_len = mode->hsync_len;      var->vsync_len = mode->vsync_len;      var->sync = mode->sync;      var->vmode = mode->vmode;      if (info->fbops->fb_check_var)          err = info->fbops->fb_check_var(var, info);      var->activate &= ~FB_ACTIVATE_TEST;      return err;  }  kernel_imx/drivers/video/mxc/mxc_ipuv3_fb.c

[cpp] view plain copystatic int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)  {      u32 vtotal;      u32 htotal;      struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;          if (var->xres == 0 || var->yres == 0) {          return 0;          }      /* fg should not bigger than bg */      if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {          struct fb_info *fbi_tmp;          int bg_xres = 0, bg_yres = 0;          int16_t pos_x, pos_y;            bg_xres = var->xres;          bg_yres = var->yres;            fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);          if (fbi_tmp) {              bg_xres = fbi_tmp->var.xres;              bg_yres = fbi_tmp->var.yres;          }            ipu_disp_get_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, &pos_x, &pos_y);            if ((var->xres + pos_x) > bg_xres)              var->xres = bg_xres - pos_x;          if ((var->yres + pos_y) > bg_yres)              var->yres = bg_yres - pos_y;      }        if (var->rotate > IPU_ROTATE_VERT_FLIP)          var->rotate = IPU_ROTATE_NONE;        if (var->xres_virtual < var->xres)          var->xres_virtual = var->xres;        if (var->yres_virtual < var->yres)          var->yres_virtual = var->yres * 3;        if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&          (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) &&          (var->bits_per_pixel != 8))          var->bits_per_pixel = 16;        if (check_var_pixfmt(var))          /* Fall back to default */          bpp_to_var(var->bits_per_pixel, var);        if (var->pixclock < 1000) {          htotal = var->xres + var->right_margin + var->hsync_len +              var->left_margin;          vtotal = var->yres + var->lower_margin + var->vsync_len +              var->upper_margin;          var->pixclock = (vtotal * htotal * 6UL) / 100UL;          var->pixclock = KHZ2PICOS(var->pixclock);          dev_dbg(info->device,              "pixclock set for 60Hz refresh = %u ps/n",              var->pixclock);      }        if (var->height == 0 && mxc_fbi->panel_height_mm)          var->height = mxc_fbi->panel_height_mm;      else if (var->height == 0)          var->height = -1;        if (var->width == 0 && mxc_fbi->panel_width_mm)          var->width = mxc_fbi->panel_width_mm;      else if (var->width == 0)          var->width = -1;        var->grayscale = 0;        return 0;  }  
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表