drm/modes: parse_cmdline: Allow specifying stand-alone options

Some options which can be specified on the commandline, such as
margin_right=..., margin_left=..., etc. are applied not only to the
specified mode, but to all modes. As such it would be nice if the user
can simply say e.g.
video=HDMI-1:margin_right=14,margin_left=24,margin_bottom=36,margin_top=42

This commit refactors drm_mode_parse_command_line_for_connector() to
add support for this, and as a nice side effect also cleans up the
function a bit.

Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-8-hdegoede@redhat.com
This commit is contained in:
Hans de Goede 2019-11-18 16:51:29 +01:00
parent 6a2d163756
commit 7b1cce760a
3 changed files with 86 additions and 58 deletions

View File

@ -1677,17 +1677,6 @@ static const char * const drm_named_modes_whitelist[] = {
"PAL",
};
static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size)
{
int i;
for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++)
if (!strncmp(mode, drm_named_modes_whitelist[i], size))
return true;
return false;
}
/**
* drm_mode_parse_command_line_for_connector - parse command line modeline for connector
* @mode_option: optional per connector mode option
@ -1718,7 +1707,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
struct drm_cmdline_mode *mode)
{
const char *name;
bool named_mode = false, parse_extras = false;
bool freestanding = false, parse_extras = false;
unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
unsigned int mode_end = 0;
const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
@ -1738,49 +1727,14 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
name = mode_option;
/*
* This is a bit convoluted. To differentiate between the
* named modes and poorly formatted resolutions, we need a
* bunch of things:
* - We need to make sure that the first character (which
* would be our resolution in X) is a digit.
* - If not, then it's either a named mode or a force on/off.
* To distinguish between the two, we need to run the
* extra parsing function, and if not, then we consider it
* a named mode.
*
* If this isn't enough, we should add more heuristics here,
* and matching unit-tests.
*/
if (!isdigit(name[0]) && name[0] != 'x') {
unsigned int namelen = strlen(name);
/*
* Only the force on/off options can be in that case,
* and they all take a single character.
*/
if (namelen == 1) {
ret = drm_mode_parse_cmdline_extra(name, namelen, true,
connector, mode);
if (!ret)
return true;
}
named_mode = true;
}
/* Try to locate the bpp and refresh specifiers, if any */
bpp_ptr = strchr(name, '-');
if (bpp_ptr)
bpp_off = bpp_ptr - name;
refresh_ptr = strchr(name, '@');
if (refresh_ptr) {
if (named_mode)
return false;
if (refresh_ptr)
refresh_off = refresh_ptr - name;
}
/* Locate the start of named options */
options_ptr = strchr(name, ',');
@ -1800,23 +1754,45 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
parse_extras = true;
}
if (named_mode) {
if (mode_end + 1 > DRM_DISPLAY_MODE_LEN)
return false;
/* First check for a named mode */
for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
if (ret == mode_end) {
if (refresh_ptr)
return false; /* named + refresh is invalid */
if (!drm_named_mode_is_in_whitelist(name, mode_end))
return false;
strcpy(mode->name, drm_named_modes_whitelist[i]);
mode->specified = true;
break;
}
}
strscpy(mode->name, name, mode_end + 1);
} else {
/* No named mode? Check for a normal mode argument, e.g. 1024x768 */
if (!mode->specified && isdigit(name[0])) {
ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
parse_extras,
connector,
mode);
if (ret)
return false;
mode->specified = true;
}
/* No mode? Check for freestanding extras and/or options */
if (!mode->specified) {
unsigned int len = strlen(mode_option);
if (bpp_ptr || refresh_ptr)
return false; /* syntax error */
if (len == 1 || (len >= 2 && mode_option[1] == ','))
extra_ptr = mode_option;
else
options_ptr = mode_option - 1;
freestanding = true;
}
mode->specified = true;
if (bpp_ptr) {
ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
@ -1852,7 +1828,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
else
len = strlen(extra_ptr);
ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding,
connector, mode);
if (ret)
return false;
@ -1860,7 +1836,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
if (options_ptr) {
ret = drm_mode_parse_cmdline_options(options_ptr + 1,
false,
freestanding,
connector, mode);
if (ret)
return false;

View File

@ -62,3 +62,5 @@ cmdline_test(drm_cmdline_test_multiple_options)
cmdline_test(drm_cmdline_test_invalid_option)
cmdline_test(drm_cmdline_test_bpp_extra_and_option)
cmdline_test(drm_cmdline_test_extra_and_option)
cmdline_test(drm_cmdline_test_freestanding_options)
cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)

View File

@ -1042,6 +1042,56 @@ static int drm_cmdline_test_extra_and_option(void *ignored)
return 0;
}
static int drm_cmdline_test_freestanding_options(void *ignored)
{
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
&no_connector,
&mode));
FAIL_ON(mode.specified);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.tv_margins.right != 14);
FAIL_ON(mode.tv_margins.left != 24);
FAIL_ON(mode.tv_margins.bottom != 36);
FAIL_ON(mode.tv_margins.top != 42);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
return 0;
}
static int drm_cmdline_test_freestanding_force_e_and_options(void *ignored)
{
struct drm_cmdline_mode mode = { };
FAIL_ON(!drm_mode_parse_command_line_for_connector("e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
&no_connector,
&mode));
FAIL_ON(mode.specified);
FAIL_ON(mode.refresh_specified);
FAIL_ON(mode.bpp_specified);
FAIL_ON(mode.tv_margins.right != 14);
FAIL_ON(mode.tv_margins.left != 24);
FAIL_ON(mode.tv_margins.bottom != 36);
FAIL_ON(mode.tv_margins.top != 42);
FAIL_ON(mode.rb);
FAIL_ON(mode.cvt);
FAIL_ON(mode.interlace);
FAIL_ON(mode.margins);
FAIL_ON(mode.force != DRM_FORCE_ON);
return 0;
}
#include "drm_selftest.c"
static int __init test_drm_cmdline_init(void)