in fbdev/core/modedb.c [617:839]
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 */
if (!db) {
db = modedb;
dbsize = ARRAY_SIZE(modedb);
}
if (!default_mode)
default_mode = &db[0];
if (!default_bpp)
default_bpp = 8;
/* Did the user specify a video mode? */
if (!mode_option)
mode_option = fb_mode_option;
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;
int interlace_specified = 0, interlace = 0;
int 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;
case '-':
namelen = i;
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;
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 'p':
if (!cvt) {
interlace = 0;
interlace_specified = 1;
}
break;
case 'i':
if (!cvt) {
interlace = 1;
interlace_specified = 1;
}
break;
default:
goto done;
}
}
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 {
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)) {
const int db_interlace = (db[i].vmode &
FB_VMODE_INTERLACED ? 1 : 0);
int score = abs(db[i].refresh - refresh);
if (interlace_specified)
score += abs(db_interlace - interlace);
if (!interlace_specified ||
db_interlace == interlace)
if (refresh_specified &&
db[i].refresh == refresh)
return 1;
if (score < diff) {
diff = score;
best = i;
}
}
}
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;
}
}
}
if (best != -1) {
fb_try_mode(var, info, &db[best], bpp);
return 5;
}
}
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;
}