1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-02-05 18:05:16 +00:00

drm: Take vt(4) default mode from loader tunables

By default, vt(4) gets the "preferred mode" from DRM, when using a DRM
video driver as its backend. The preferred mode is usually the native
screen resolution.

Now, if this mode isn't appropriate, a user can use loader tunables to
select a mode. The tunables are read in the following order:
    1. kern.vt.fb.modes.$connector_name
    2. kern.vt.fb.default_mode

For example, to set a 1024x768 mode, no matter the connector:
    kern.vt.fb.default_mode="1024x768"

To set a 800x600 mode only on the laptop builtin screen:
    kern.vt.fb.modes.LVDS-1="800x600"

MFC after:	1 week
This commit is contained in:
Jean-Sébastien Pédron 2014-11-03 10:42:27 +00:00
parent ab57474c83
commit 4a60312011
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=274031
2 changed files with 65 additions and 182 deletions

View File

@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$");
#include <dev/drm2/drm_crtc_helper.h>
#include <sys/kdb.h>
#include <sys/param.h>
#include <sys/systm.h>
struct vt_kms_softc {
struct drm_fb_helper *fb_helper;
@ -96,170 +98,31 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
return 0;
}
const char *fb_mode_option;
/**
* drm_fb_helper_connector_parse_command_line - parse command line for connector
* @connector - connector to parse line for
* @mode_option - per connector mode option
*
* This parses the connector specific then generic command lines for
* modes and options to configure the connector.
*
* This uses the same parameters as the fb modedb.c, except for extra
* <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
*
* enable/enable Digital/disable bit at the end
*/
static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn,
const char *mode_option)
{
const char *name;
unsigned int namelen;
int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
int i;
enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
struct drm_fb_helper_cmdline_mode *cmdline_mode;
struct drm_connector *connector;
if (!fb_helper_conn)
return false;
connector = fb_helper_conn->connector;
cmdline_mode = &fb_helper_conn->cmdline_mode;
if (!mode_option)
mode_option = fb_mode_option;
if (!mode_option) {
cmdline_mode->specified = false;
return false;
}
name = mode_option;
namelen = strlen(name);
for (i = namelen-1; i >= 0; i--) {
switch (name[i]) {
case '@':
namelen = i;
if (!refresh_specified && !bpp_specified &&
!yres_specified) {
refresh = 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 = strtol(&name[i+1], NULL, 10);
bpp_specified = 1;
if (cvt || rb)
cvt = 0;
} else
goto done;
break;
case 'x':
if (!yres_specified) {
yres = strtol(&name[i+1], NULL, 10);
yres_specified = 1;
} else
goto done;
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;
case 'e':
force = DRM_FORCE_ON;
break;
case 'D':
if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
(connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
force = DRM_FORCE_ON;
else
force = DRM_FORCE_ON_DIGITAL;
break;
case 'd':
force = DRM_FORCE_OFF;
break;
default:
goto done;
}
}
if (i < 0 && yres_specified) {
xres = strtol(name, NULL, 10);
res_specified = 1;
}
done:
DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
drm_get_connector_name(connector), xres, yres,
(refresh) ? refresh : 60, (rb) ? " reduced blanking" :
"", (margins) ? " with margins" : "", (interlace) ?
" interlaced" : "");
if (force) {
const char *s;
switch (force) {
case DRM_FORCE_OFF: s = "OFF"; break;
case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
default:
case DRM_FORCE_ON: s = "ON"; break;
}
DRM_INFO("forcing %s connector %s\n",
drm_get_connector_name(connector), s);
connector->force = force;
}
if (res_specified) {
cmdline_mode->specified = true;
cmdline_mode->xres = xres;
cmdline_mode->yres = yres;
}
if (refresh_specified) {
cmdline_mode->refresh_specified = true;
cmdline_mode->refresh = refresh;
}
if (bpp_specified) {
cmdline_mode->bpp_specified = true;
cmdline_mode->bpp = bpp;
}
cmdline_mode->rb = rb ? true : false;
cmdline_mode->cvt = cvt ? true : false;
cmdline_mode->interlace = interlace ? true : false;
return true;
}
static int
fb_get_options(const char *connector_name, char **option)
{
char tunable[64];
/*
* TODO: store mode options pointer in ${option} for connector with
* name ${connector_name}
* A user may use loader tunables to set a specific mode for the
* console. Tunables are read in the following order:
* 1. kern.vt.fb.modes.$connector_name
* 2. kern.vt.fb.default_mode
*
* Example of a mode specific to the LVDS connector:
* kern.vt.fb.modes.LVDS="1024x768"
*
* Example of a mode applied to all connectors not having a
* connector-specific mode:
* kern.vt.fb.default_mode="640x480"
*/
return (1);
snprintf(tunable, sizeof(tunable), "kern.vt.fb.modes.%s",
connector_name);
*option = kern_getenv(tunable);
if (*option == NULL)
*option = kern_getenv("kern.vt.fb.default_mode");
return (*option != NULL ? 0 : 1);
}
static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
@ -268,15 +131,52 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
int i;
for (i = 0; i < fb_helper->connector_count; i++) {
struct drm_cmdline_mode *mode;
struct drm_connector *connector;
char *option = NULL;
fb_helper_conn = fb_helper->connector_info[i];
connector = fb_helper_conn->connector;
mode = &fb_helper_conn->cmdline_mode;
/* do something on return - turn off connector maybe */
if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option))
if (fb_get_options(drm_get_connector_name(connector), &option))
continue;
drm_fb_helper_connector_parse_command_line(fb_helper_conn, option);
if (drm_mode_parse_command_line_for_connector(option,
connector,
mode)) {
if (mode->force) {
const char *s;
switch (mode->force) {
case DRM_FORCE_OFF:
s = "OFF";
break;
case DRM_FORCE_ON_DIGITAL:
s = "ON - dig";
break;
default:
case DRM_FORCE_ON:
s = "ON";
break;
}
DRM_INFO("forcing %s connector %s\n",
drm_get_connector_name(connector), s);
connector->force = mode->force;
}
DRM_INFO("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
drm_get_connector_name(connector),
mode->xres, mode->yres,
mode->refresh_specified ? mode->refresh : 60,
mode->rb ? " reduced blanking" : "",
mode->margins ? " with margins" : "",
mode->interlace ? " interlaced" : "");
}
freeenv(option);
}
return 0;
}
@ -955,7 +855,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
/* first up get a count of crtcs now in use and new min/maxes width/heights */
for (i = 0; i < fb_helper->connector_count; i++) {
struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
struct drm_fb_helper_cmdline_mode *cmdline_mode;
struct drm_cmdline_mode *cmdline_mode;
cmdline_mode = &fb_helper_conn->cmdline_mode;
@ -1180,7 +1080,7 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_conn
static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
{
struct drm_fb_helper_cmdline_mode *cmdline_mode;
struct drm_cmdline_mode *cmdline_mode;
cmdline_mode = &fb_connector->cmdline_mode;
return cmdline_mode->specified;
}
@ -1191,10 +1091,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne
struct drm_cmdline_mode *cmdline_mode;
struct drm_display_mode *mode = NULL;
cmdline_mode = &fb_helper_conn->cmdline_mode1;
if (cmdline_mode->specified == false &&
!drm_fetch_cmdline_mode_from_kenv(fb_helper_conn->connector,
cmdline_mode))
cmdline_mode = &fb_helper_conn->cmdline_mode;
if (cmdline_mode->specified == false)
return (NULL);
/* attempt to find a matching mode in the list of modes

View File

@ -40,20 +40,6 @@ struct drm_fb_helper_crtc {
struct drm_display_mode *desired_mode;
};
/* mode specified on the command line */
struct drm_fb_helper_cmdline_mode {
bool specified;
bool refresh_specified;
bool bpp_specified;
int xres, yres;
int bpp;
int refresh;
bool rb;
bool interlace;
bool cvt;
bool margins;
};
struct drm_fb_helper_surface_size {
u32 fb_width;
u32 fb_height;
@ -74,9 +60,8 @@ struct drm_fb_helper_funcs {
};
struct drm_fb_helper_connector {
struct drm_fb_helper_cmdline_mode cmdline_mode;
struct drm_cmdline_mode cmdline_mode1;
struct drm_connector *connector;
struct drm_cmdline_mode cmdline_mode;
};
struct drm_fb_helper {