kdrive/fbdev: Split framebuffer modesetting into a separate function

Properly get the screen refresh rate from the fb driver.
Allow unknown user-specified modes

Signed-off-by: stefan11111 <stefan11111github@gmail.com>
This commit is contained in:
stefan11111 2026-06-11 19:31:26 +03:00 • committed by Enrico Weigelt
commit d784dfb842

View file

@ -147,6 +147,31 @@ fbdevModeSupported(KdScreenInfo * screen, const KdMonitorTiming * t)
return TRUE;
}
static int
fbdevGetRefreshRate(const struct fb_var_screeninfo *var)
{
#define PICOS2HZ(a) (1.0e12/(a))
long scanline = var->left_margin + var->xres + var->right_margin + var->hsync_len;
long v_total = var->upper_margin + var->yres + var->lower_margin + var->vsync_len;
long vblank = v_total * scanline * var->pixclock;
long rate = vblank ? PICOS2HZ(vblank) : -1;
/* Make sure the rate is reasonable */
if (rate > 0 && rate <= 1000) {
return rate;
}
/**
* We could probe the refresh rate by doing FBIO_WAITFORVSYNC,
* measuring the time between them, and matching that to a list of common rates.
* However, if the rate reported by the driver is wrong, the driver probably doesn't care about
* refresh rates.
*/
return 103;
#undef PICOS2HZ
}
static void
fbdevConvertMonitorTiming(const KdMonitorTiming * t,
struct fb_var_screeninfo *var)
@ -176,15 +201,91 @@ fbdevConvertMonitorTiming(const KdMonitorTiming * t,
var->sync |= FB_SYNC_VERT_HIGH_ACT;
}
static Bool
fbdevSetMode(KdScreenInfo *screen, const KdMonitorTiming *t)
{
FbdevPriv *priv = screen->card->driver;
struct fb_var_screeninfo var = {0};
int depth;
int k;
k = ioctl(priv->fd, FBIOGET_VSCREENINFO, &var);
screen->rate = t->rate;
screen->width = t->horizontal;
screen->height = t->vertical;
if (k < 0 || (t->horizontal != var.xres) || (t->vertical != var.yres)) {
fbdevConvertMonitorTiming(t, &var);
}
var.activate = FB_ACTIVATE_NOW;
var.bits_per_pixel = screen->fb.depth;
var.nonstd = 0;
var.grayscale = 0;
k = ioctl(priv->fd, FBIOPUT_VSCREENINFO, &var);
if (k < 0) {
LogMessage(X_ERROR, "Xfbdev(%d): FBIOPUT_VSCREENINFO: %s\n",
screen->card->mynum, strerror(errno));
}
/* Re-get the "fixed" parameters since they might have changed */
k = ioctl(priv->fd, FBIOGET_FSCREENINFO, &priv->fix);
if (k < 0) {
LogMessage(X_ERROR, "Xfbdev(%d): FBIOGET_FSCREENINFO: %s\n",
screen->card->mynum, strerror(errno));
}
/* Now get the new screeninfo */
k = ioctl(priv->fd, FBIOGET_VSCREENINFO, &priv->var);
if (k >= 0) {
/* Just because the ioctl didn't fail, it doesn't mean we could set the mode */
LogMessage(X_INFO, "Xfbdev(%d): Current screen mode: width = %d, height = %d\n",
screen->card->mynum, priv->var.xres, priv->var.yres);
}
depth = priv->var.bits_per_pixel;
/* Calculate fix.line_length if it's zero */
if (!priv->fix.line_length)
priv->fix.line_length = (priv->var.xres_virtual * depth + 7) / 8;
return (k >= 0) && (t->horizontal == priv->var.xres) && (t->vertical == var.yres);
}
static void
fbdevConvertVarToTiming(const struct fb_var_screeninfo *var,
KdMonitorTiming * t)
{
t->horizontal = var->xres;
t->vertical = var->yres;
t->clock = var->pixclock ? 1000000000 / var->pixclock : 0;
t->hbp = var->left_margin;
t->hfp = var->right_margin;
t->vbp = var->upper_margin;
t->vfp = var->lower_margin;
t->hblank = var->hsync_len + t->hfp + t->hbp;
t->vblank = var->vsync_len + t->vfp + t->vbp;
t->rate = fbdevGetRefreshRate(var);
t->hpol = (var->sync & FB_SYNC_HOR_HIGH_ACT) ? KdSyncPositive : KdSyncNegative;
t->vpol = (var->sync & FB_SYNC_VERT_HIGH_ACT) ? KdSyncPositive : KdSyncNegative;
}
static Bool
fbdevScreenInitialize(KdScreenInfo * screen, FbdevScrPriv * scrpriv)
{
FbdevPriv *priv = screen->card->driver;
Pixel allbits;
int depth;
int rate;
Bool gray;
struct fb_var_screeninfo var;
const KdMonitorTiming *t;
KdMonitorTiming curr_mode = {0};
int k;
k = ioctl(priv->fd, FBIOGET_VSCREENINFO, &var);
@ -193,12 +294,15 @@ fbdevScreenInitialize(KdScreenInfo * screen, FbdevScrPriv * scrpriv)
if (k >= 0) {
screen->width = var.xres;
screen->height = var.yres;
}
else {
screen->rate = fbdevGetRefreshRate(&var);
} else {
screen->width = 1024;
screen->height = 768;
screen->rate = 103;
}
screen->rate = 103; /* FIXME: should get proper value from fb driver */
}
if (!screen->rate) {
screen->rate = (k >= 0) ? fbdevGetRefreshRate(&var) : 103;
}
if (!screen->fb.depth) {
if (k >= 0)
@ -207,52 +311,32 @@ fbdevScreenInitialize(KdScreenInfo * screen, FbdevScrPriv * scrpriv)
screen->fb.depth = 16;
}
if ((screen->width != var.xres) || (screen->height != var.yres)) {
t = KdFindMode(screen, fbdevModeSupported);
screen->rate = t->rate;
screen->width = t->horizontal;
screen->height = t->vertical;
/* Now try setting the mode */
if (k < 0 || (t->horizontal != var.xres || t->vertical != var.yres))
fbdevConvertMonitorTiming(t, &var);
rate = KdFindRate(screen, fbdevModeSupported);
if (k >= 0) {
/* Add the current framebuffer mode */
fbdevConvertVarToTiming(&var, &curr_mode);
KdAddMode(&curr_mode);
if (!rate) {
/* Add the desired framebuffer mode */
curr_mode.horizontal = screen->width;
curr_mode.vertical = screen->height;
KdAddMode(&curr_mode);
}
}
var.activate = FB_ACTIVATE_NOW;
var.bits_per_pixel = screen->fb.depth;
var.nonstd = 0;
var.grayscale = 0;
LogMessage(X_INFO, "Xfbdev(%d): Desired screen mode: width = %d, height = %d\n",
screen->card->mynum, var.xres, var.yres);
k = ioctl(priv->fd, FBIOPUT_VSCREENINFO, &var);
if (k < 0) {
LogMessage(X_ERROR, "Xfbdev(%d): FBIOPUT_VSCREENINFO: %s\n",
screen->card->mynum, strerror(errno));
return FALSE;
/* Fbdev rate isn't reliable, don't forbid modes based on it */
if (screen->rate < rate) {
screen->rate = rate;
}
/* Re-get the "fixed" parameters since they might have changed */
k = ioctl(priv->fd, FBIOGET_FSCREENINFO, &priv->fix);
if (k < 0)
LogMessage(X_ERROR, "Xfbdev(%d): FBIOGET_FSCREENINFO: %s\n",
screen->card->mynum, strerror(errno));
t = KdFindMode(screen, fbdevModeSupported);
/* Sets priv->fix, priv->var */
fbdevSetMode(screen, t);
/* Now get the new screeninfo */
ioctl(priv->fd, FBIOGET_VSCREENINFO, &priv->var);
depth = priv->var.bits_per_pixel;
gray = priv->var.grayscale;
/* Just because the ioctl didn't fail, it doesn't mean we could set the mode */
LogMessage(X_INFO, "Xfbdev(%d): Actual screen mode: width = %d, height = %d\n",
screen->card->mynum, priv->var.xres, priv->var.yres);
/* Calculate fix.line_length if it's zero */
if (!priv->fix.line_length)
priv->fix.line_length = (priv->var.xres_virtual * depth + 7) / 8;
switch (priv->fix.visual) {
case FB_VISUAL_MONO01:
case FB_VISUAL_MONO10: