IMX6 LCD 參數匹配過程分析

[IMX6Q]LCD參數匹配過程分析

2072人閱讀 評論(0) 收藏 舉報
本文章已收錄於:
分類:

Platform: IMX6Q

OS: Android 4.4


本文只討論lvds接口的是lcd參數匹配的過程,mipi dsi以及其他接口部分會有一點差異。


核心函數fb_find_mode(),在分析之前先了解下幾個參數。


重要參數說明:


1. ldb.c中的 ldb_modedb

  1. static struct fb_videomode ldb_modedb[] = {  
  2.     {  
  3.      "LDB-WXGA", 60, 1280, 800, 14065,  
  4.      40, 40,  
  5.      10, 3,  
  6.      80, 10,  
  7.      0,  
  8.      FB_VMODE_NONINTERLACED,  
  9.      FB_MODE_IS_DETAILED,},  
  10.     {  
  11.      "LDB-XGA", 60, 1024, 768, 15385,  
  12.      220, 40,  
  13.      21, 7,  
  14.      60, 10,  
  15.      0,  
  16.      FB_VMODE_NONINTERLACED,  
  17.      FB_MODE_IS_DETAILED,},  
  18.     {  
  19.      "LDB-1080P60", 60, 1920, 1080, 7692,  
  20.      100, 40,  
  21.      30, 3,  
  22.      10, 2,  
  23.      0,  
  24.      FB_VMODE_NONINTERLACED,  
  25.      FB_MODE_IS_DETAILED,},  
  26.      {  
  27.      "LDB-WSVGA"//Name  
  28.      60, //refresh  
  29.      1024, //xres  
  30.      600, //yres  
  31.      19531,//pixclock  
  32.      220, 40,//left/right margin  
  33.      21, 7,//upper/lower margin  
  34.      60, 7,//hsync_len,vsync_len  
  35.      0,//sync  
  36.      FB_VMODE_NONINTERLACED,//vmode  
  37.      FB_MODE_IS_DETAILED,},  
  38. };  
  39. static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb);  
static 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結構

本例是:

  1. static struct ipuv3_fb_platform_data tek_fb_data[] = {  
  2.     { /*fb0*/  
  3.     .disp_dev = "ldb",  
  4.     .interface_pix_fmt = IPU_PIX_FMT_RGB24,  
  5.     .mode_str = "LDB-WSVGA",  
  6.     .default_bpp = 32,  
  7.     .int_clk = false,  
  8.     .late_init = false,  
  9.     }, {  
  10.     .disp_dev = "hdmi",  
  11.     .interface_pix_fmt = IPU_PIX_FMT_RGB24,  
  12.     .mode_str = "1920x1080M@60",  
  13.     .default_bpp = 32,  
  14.     .int_clk = false,  
  15.     .late_init = false,  
  16.     },  
  17.     {  
  18.     .disp_dev = "lcd",  
  19.     .interface_pix_fmt = IPU_PIX_FMT_RGB565,  
  20.     .mode_str = "CLAA-WVGA",  
  21.     .default_bpp = 16,  
  22.     .int_clk = false,  
  23.     .late_init = false,  
  24.     }, {  
  25.     .disp_dev = "ldb",  
  26.     .interface_pix_fmt = IPU_PIX_FMT_RGB666,  
  27.     .mode_str = "LDB-VGA",  
  28.     .default_bpp = 16,  
  29.     .int_clk = false,  
  30.     .late_init = false,  
  31.     },  
  32. };  
static 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], 以此類推。

瞭解了參數意義之後下面就方便理解了.


系統有如下調用:

  1. static int ldb_disp_init(struct mxc_dispdrv_handle *disp,  
  2.     struct mxc_dispdrv_setting *setting) {  
  3. ......  
  4.     ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str,  
  5.                 ldb_modedb, ldb_modedb_sz, NULL, setting->default_bpp);  
  6. ......  
  7. }  
static 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


下面看代碼執行流程:


  1. /** 
  2.  *    fb_find_mode - finds a valid video mode 
  3.  *    @var: frame buffer user defined part of display 
  4.  *    @info: frame buffer info structure 
  5.  *    @mode_option: string video mode to find 
  6.  *    @db: video mode database 
  7.  *    @dbsize: size of @db 
  8.  *    @default_mode: default video mode to fall back to 
  9.  *    @default_bpp: default color depth in bits per pixel 
  10.  * 
  11.  *    Finds a suitable video mode, starting with the specified mode 
  12.  *    in @mode_option with fallback to @default_mode.  If 
  13.  *    @default_mode fails, all modes in the video mode database will 
  14.  *    be tried. 
  15.  * 
  16.  *    Valid mode specifiers for @mode_option: 
  17.  * 
  18.  *    <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or 
  19.  *    <name>[-<bpp>][@<refresh>] 
  20.  * 
  21.  *    with <xres>, <yres>, <bpp> and <refresh> decimal numbers and 
  22.  *    <name> a string. 
  23.  * 
  24.  *      If 'M' is present after yres (and before refresh/bpp if present), 
  25.  *      the function will compute the timings using VESA(tm) Coordinated 
  26.  *      Video Timings (CVT).  If 'R' is present after 'M', will compute with 
  27.  *      reduced blanking (for flatpanels).  If 'i' is present, compute 
  28.  *      interlaced mode.  If 'm' is present, add margins equal to 1.8% 
  29.  *      of xres rounded down to 8 pixels, and 1.8% of yres. The char 
  30.  *      'i' and 'm' must be after 'M' and 'R'. Example: 
  31.  * 
  32.  *      1024x768MR-8@60m - Reduced blank with margins at 60Hz. 
  33.  * 
  34.  *    NOTE: The passed struct @var is _not_ cleared!  This allows you 
  35.  *    to supply values for e.g. the grayscale and accel_flags fields. 
  36.  * 
  37.  *    Returns zero for failure, 1 if using specified @mode_option, 
  38.  *    2 if using specified @mode_option with an ignored refresh rate, 
  39.  *    3 if default mode is used, 4 if fall back to any valid mode. 
  40.  * 
  41.  */  
  42. int fb_find_mode(struct fb_var_screeninfo *var,  
  43.          struct fb_info *info, const char *mode_option,  
  44.          const struct fb_videomode *db, unsigned int dbsize,  
  45.          const struct fb_videomode *default_mode,  
  46.          unsigned int default_bpp)  
  47. {  
  48.     int i;  
  49.   
  50.     /* Set up defaults */  
  51.     /*參數未給則使用modedb。*/  
  52.     if (!db) {  
  53.     db = modedb;  
  54.     dbsize = ARRAY_SIZE(modedb);  
  55.     }  
  56.   
  57.     /*如果沒設置則使用db[0]的值,此例傳進來的本身就是db[0]*/  
  58.     if (!default_mode)  
  59.     default_mode = &db[0];  
  60.     /*沒有設置bpp則默認使用8bpp,本例是32*/  
  61.     if (!default_bpp)  
  62.     default_bpp = 8;  
  63.   
  64.     /* Did the user specify a video mode? */  
  65.     if (!mode_option)  
  66.     mode_option = fb_mode_option;  
  67.     /*本例是“LDB-WSVGA”*/  
  68.     if (mode_option) {  
  69.     const char *name = mode_option;  
  70.     unsigned int namelen = strlen(name);  
  71.     int res_specified = 0, bpp_specified = 0, refresh_specified = 0;  
  72.     unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0;  
  73.     int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;  
  74.     u32 best, diff, tdiff;  
  75.        /*數字格式規則形纔會跑下面的循環*/  
  76.     for (i = namelen-1; i >= 0; i--) {  
  77.         switch (name[i]) {  
  78.                 /*@後面的是刷新頻率*/  
  79.                 case '@':  
  80.             namelen = i;  
  81.             if (!refresh_specified && !bpp_specified &&  
  82.             !yres_specified) {  
  83.             refresh = simple_strtol(&name[i+1], NULL, 10);  
  84.             refresh_specified = 1;  
  85.             if (cvt || rb)  
  86.                 cvt = 0;  
  87.             } else  
  88.             goto done;  
  89.             break;  
  90.                 /*後面是bpp*/  
  91.                 case '-':  
  92.             namelen = i;  
  93.             printk("line:%d\n", __LINE__);  
  94.             if (!bpp_specified && !yres_specified) {  
  95.             bpp = simple_strtol(&name[i+1], NULL, 10);  
  96.             bpp_specified = 1;  
  97.             if (cvt || rb)  
  98.                 cvt = 0;  
  99.             } else  
  100.             goto done;  
  101.             break;  
  102.                 /*獲取yres*/  
  103.         case 'x':  
  104.             if (!yres_specified) {  
  105.             yres = simple_strtol(&name[i+1], NULL, 10);  
  106.             yres_specified = 1;  
  107.             } else  
  108.             goto done;  
  109.             break;  
  110.         case '0' ... '9':  
  111.             break;  
  112.         case 'M':  
  113.             if (!yres_specified)  
  114.             cvt = 1;  
  115.             break;  
  116.         case 'R':  
  117.             if (!cvt)  
  118.             rb = 1;  
  119.             break;  
  120.         case 'm':  
  121.             if (!cvt)  
  122.             margins = 1;  
  123.             break;  
  124.         case 'i':  
  125.             if (!cvt)  
  126.             interlace = 1;  
  127.             break;  
  128.         default:  
  129.             goto done;  
  130.         }  
  131.     }  
  132.         /*如果yres有值,那麼也獲取xres.*/  
  133.         if (i < 0 && yres_specified) {  
  134.         xres = simple_strtol(name, NULL, 10);  
  135.         res_specified = 1;  
  136.     }  
  137. done:  
  138.         /*不會跑這裏*/  
  139.         if (cvt) {  
  140.         struct fb_videomode cvt_mode;  
  141.         int ret;  
  142.   
  143.         DPRINTK("CVT mode %dx%d@%dHz%s%s%s\n", xres, yres,  
  144.             (refresh) ? refresh : 60, (rb) ? " reduced blanking" :  
  145.             "", (margins) ? " with margins" : "", (interlace) ?  
  146.             " interlaced" : "");  
  147.   
  148.         memset(&cvt_mode, 0, sizeof(cvt_mode));  
  149.         cvt_mode.xres = xres;  
  150.         cvt_mode.yres = yres;  
  151.         cvt_mode.refresh = (refresh) ? refresh : 60;  
  152.   
  153.         if (interlace)  
  154.         cvt_mode.vmode |= FB_VMODE_INTERLACED;  
  155.         else  
  156.         cvt_mode.vmode &= ~FB_VMODE_INTERLACED;  
  157.   
  158.         ret = fb_find_mode_cvt(&cvt_mode, margins, rb);  
  159.   
  160.         if (!ret && !fb_try_mode(var, info, &cvt_mode, bpp)) {  
  161.         DPRINTK("modedb CVT: CVT mode ok\n");  
  162.         return 1;  
  163.         }  
  164.   
  165.         DPRINTK("CVT mode invalid, getting mode from database\n");  
  166.     }  
  167.   
  168.     DPRINTK("Trying specified video mode%s %ix%i\n",  
  169.         refresh_specified ? "" : " (ignoring refresh rate)", xres, yres);  
  170.   
  171.        /*刷新率沒指定*/  
  172.        if (!refresh_specified) {  
  173.         /* 
  174.          * If the caller has provided a custom mode database and a 
  175.          * valid monspecs structure, we look for the mode with the 
  176.          * highest refresh rate.  Otherwise we play it safe it and 
  177.          * try to find a mode with a refresh rate closest to the 
  178.          * standard 60 Hz. 
  179.          */  
  180.         if (db != modedb &&  
  181.             info->monspecs.vfmin && info->monspecs.vfmax &&  
  182.             info->monspecs.hfmin && info->monspecs.hfmax &&  
  183.             info->monspecs.dclkmax) {  
  184.               refresh = 1000;  
  185.         } else {  
  186.                     /*默認使用60HZ*/  
  187.                     refresh = 60;  
  188.         }  
  189.     }  
  190.   
  191.     diff = -1;  
  192.     best = -1;  
  193.         /*根據名字以及或者分辨率來匹配。*/  
  194.         for (i = 0; i < dbsize; i++) {  
  195.         if ((name_matches(db[i], name, namelen) ||  
  196.             (res_specified && res_matches(db[i], xres, yres))) &&  
  197.             !fb_try_mode(var, info, &db[i], bpp)) {  
  198.                        /*刷新率也匹配的時候就認準你了!*/  
  199.                        if (refresh_specified && db[i].refresh == refresh) {  
  200.                 return 1;  
  201.             } else {  
  202.                               /*刷新率不一樣就找差得最少的*/    
  203.                               if (abs(db[i].refresh - refresh) < diff) {  
  204.                     diff = abs(db[i].refresh - refresh);  
  205.                     best = i;  
  206.                 }  
  207.             }  
  208.         }  
  209.     }  
  210.     /*得到刷新率差得最少的db,然後返回*/  
  211.        if (best != -1) {  
  212.         fb_try_mode(var, info, &db[best], bpp);  
  213.         return (refresh_specified) ? 2 : 1;  
  214.     }  
  215.   
  216.     /*跑到這裏說明名字和分辨率都不匹配。*/  
  217.     diff = 2 * (xres + yres);  
  218.     best = -1;  
  219.     DPRINTK("Trying best-fit modes\n");  
  220.         /*找到分辨率最小的那組數據。*/  
  221.         for (i = 0; i < dbsize; i++) {  
  222.                 DPRINTK("Trying %ix%i\n", db[i].xres, db[i].yres);  
  223.         if (!fb_try_mode(var, info, &db[i], bpp)) {  
  224.              tdiff = abs(db[i].xres - xres) +  
  225.                 abs(db[i].yres - yres);  
  226.   
  227.              /* 
  228.              * Penalize modes with resolutions smaller 
  229.              * than requested. 
  230.              */  
  231.             if (xres > db[i].xres || yres > db[i].yres)  
  232.                 tdiff += xres + yres;  
  233.                        /*差值大的會被保留,說白了,最終就是找到分辨率最小的那組參數。*/  
  234.             if (diff > tdiff) {  
  235.                 diff = tdiff;  
  236.                 best = i;  
  237.             }  
  238.         }  
  239.     }  
  240.        /*獲取best對應的var參數。*/  
  241.        if (best != -1) {  
  242.         fb_try_mode(var, info, &db[best], bpp);  
  243.         return 5;  
  244.     }  
  245.     }  
  246.   
  247.     /*運行到這裏有兩種情況, 
  248.      1. 字母規則型(如LDB-WXVGA),那就是名字不匹配,並且參數檢查失敗,。 
  249.      2. 數字規則性(如1920x1080), 那就是名字不匹配 && 分辨率比ldb_modedb中的小上兩倍以上(比如1920x1080 和 320x240)。 */  
  250.     DPRINTK("Trying default video mode\n");  
  251.     if (!fb_try_mode(var, info, default_mode, default_bpp))  
  252.     return 3;  
  253.   
  254.     DPRINTK("Trying all modes\n");  
  255.     /*默認的還失敗那我只能隨便找一個了。*/  
  256.     for (i = 0; i < dbsize; i++)  
  257.     if (!fb_try_mode(var, info, &db[i], default_bpp))  
  258.         return 4;  
  259.   
  260.     /*隨便都不行,那我只能失敗了...*/  
  261.     DPRINTK("No valid mode found\n");  
  262.     return 0;  
  263. }   
/**
 *    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

  1. static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,  
  2.                const struct fb_videomode *mode, unsigned int bpp)  
  3. {  
  4.     int err = 0;  
  5.   
  6.     DPRINTK("Trying mode %s %dx%d-%d@%d\n", mode->name ? mode->name : "noname",  
  7.         mode->xres, mode->yres, bpp, mode->refresh);  
  8.     var->xres = mode->xres;  
  9.     var->yres = mode->yres;  
  10.     var->xres_virtual = mode->xres;  
  11.     var->yres_virtual = mode->yres;  
  12.     var->xoffset = 0;  
  13.     var->yoffset = 0;  
  14.     var->bits_per_pixel = bpp;  
  15.     var->activate |= FB_ACTIVATE_TEST;  
  16.     var->pixclock = mode->pixclock;  
  17.     var->left_margin = mode->left_margin;  
  18.     var->right_margin = mode->right_margin;  
  19.     var->upper_margin = mode->upper_margin;  
  20.     var->lower_margin = mode->lower_margin;  
  21.     var->hsync_len = mode->hsync_len;  
  22.     var->vsync_len = mode->vsync_len;  
  23.     var->sync = mode->sync;  
  24.     var->vmode = mode->vmode;  
  25.     if (info->fbops->fb_check_var)  
  26.         err = info->fbops->fb_check_var(var, info);  
  27.     var->activate &= ~FB_ACTIVATE_TEST;  
  28.     return err;  
  29. }  
static 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

  1. static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)  
  2. {  
  3.     u32 vtotal;  
  4.     u32 htotal;  
  5.     struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;  
  6.   
  7.   
  8.     if (var->xres == 0 || var->yres == 0) {  
  9.         return 0;  
  10.         }  
  11.     /* fg should not bigger than bg */  
  12.     if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {  
  13.         struct fb_info *fbi_tmp;  
  14.         int bg_xres = 0, bg_yres = 0;  
  15.         int16_t pos_x, pos_y;  
  16.   
  17.         bg_xres = var->xres;  
  18.         bg_yres = var->yres;  
  19.   
  20.         fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxc_fbi->ipu_id);  
  21.         if (fbi_tmp) {  
  22.             bg_xres = fbi_tmp->var.xres;  
  23.             bg_yres = fbi_tmp->var.yres;  
  24.         }  
  25.   
  26.         ipu_disp_get_window_pos(mxc_fbi->ipu, mxc_fbi->ipu_ch, &pos_x, &pos_y);  
  27.   
  28.         if ((var->xres + pos_x) > bg_xres)  
  29.             var->xres = bg_xres - pos_x;  
  30.         if ((var->yres + pos_y) > bg_yres)  
  31.             var->yres = bg_yres - pos_y;  
  32.     }  
  33.   
  34.     if (var->rotate > IPU_ROTATE_VERT_FLIP)  
  35.         var->rotate = IPU_ROTATE_NONE;  
  36.   
  37.     if (var->xres_virtual < var->xres)  
  38.         var->xres_virtual = var->xres;  
  39.   
  40.     if (var->yres_virtual < var->yres)  
  41.         var->yres_virtual = var->yres * 3;  
  42.   
  43.     if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&  
  44.         (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) &&  
  45.         (var->bits_per_pixel != 8))  
  46.         var->bits_per_pixel = 16;  
  47.   
  48.     if (check_var_pixfmt(var))  
  49.         /* Fall back to default */  
  50.         bpp_to_var(var->bits_per_pixel, var);  
  51.   
  52.     if (var->pixclock < 1000) {  
  53.         htotal = var->xres + var->right_margin + var->hsync_len +  
  54.             var->left_margin;  
  55.         vtotal = var->yres + var->lower_margin + var->vsync_len +  
  56.             var->upper_margin;  
  57.         var->pixclock = (vtotal * htotal * 6UL) / 100UL;  
  58.         var->pixclock = KHZ2PICOS(var->pixclock);  
  59.         dev_dbg(info->device,  
  60.             "pixclock set for 60Hz refresh = %u ps\n",  
  61.             var->pixclock);  
  62.     }  
  63.   
  64.     if (var->height == 0 && mxc_fbi->panel_height_mm)  
  65.         var->height = mxc_fbi->panel_height_mm;  
  66.     else if (var->height == 0)  
  67.         var->height = -1;  
  68.   
  69.     if (var->width == 0 && mxc_fbi->panel_width_mm)  
  70.         var->width = mxc_fbi->panel_width_mm;  
  71.     else if (var->width == 0)  
  72.         var->width = -1;  
  73.   
  74.     var->grayscale = 0;  
  75.   
  76.     return 0;  
  77. }  
static 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;
}



1
0

我的同類文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章