本文章已收錄於:
分類:
版權聲明:本文爲博主原創文章,未經博主允許不得轉載。
Platform: IMX6Q
OS: Android 4.4
本文只討論lvds接口的是lcd參數匹配的過程,mipi dsi以及其他接口部分會有一點差異。
核心函數fb_find_mode(),在分析之前先了解下幾個參數。
重要參數說明:
1. ldb.c中的 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);
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結構
本例是:
- 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,
- },
- };
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], 以此類推。
瞭解了參數意義之後下面就方便理解了.系統有如下調用:
- 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);
- ......
- }
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
下面看代碼執行流程:
- /**
- * 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_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
- 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;
- }
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
- 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;
- }
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
我的同類文章
http://blog.csdn.net
- •[IMX6DL]dd命令刷u-boot.bin到sd卡失敗解決方法2016-07-21
- •Linux死鎖調試之hardlockup2016-05-05
- •[IMX6DL]Linux dmaengine 使用方法2016-05-03
- •[IMX6DL]超聲波模塊HC-SR04 Linux驅動源代碼2016-04-15
- •[IMX6DL]Linux內核 --- 中斷流程小結2016-04-01
- •[IMX6Q][Android5.1]移植筆記 --- kernel移植2016-03-14
- •[IMX6DL]CPU頻率調節模式以及降頻方法2016-07-04
- •Linux死鎖調試之softlockup2016-05-04
- •[IMX6DL]超聲波模塊KS103 Linux驅動源代碼2016-04-25
- •[IMX6DL]do_gettimeofday()的精度分析2016-04-14
- •如何將dtb反編譯成dts2016-03-14