2019-09-19 14:43:19 +00:00
|
|
|
From 3508a8548f13be68b6d098ad99a7bc1fc1810f76 Mon Sep 17 00:00:00 2001
|
2019-08-09 17:50:30 +00:00
|
|
|
From: Maxime Ripard <maxime.ripard@bootlin.com>
|
|
|
|
Date: Wed, 19 Jun 2019 12:17:49 +0200
|
2019-12-23 12:42:55 +00:00
|
|
|
Subject: [PATCH] drm/modes: Rewrite the command line parser
|
2019-08-09 17:50:30 +00:00
|
|
|
MIME-Version: 1.0
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
|
|
|
|
commit e08ab74bd4c7a5fe311bc05f32dbb4f1e7fa3428 upstream.
|
|
|
|
|
|
|
|
Rewrite the command line parser in order to get away from the state machine
|
|
|
|
parsing the video mode lines.
|
|
|
|
|
|
|
|
Hopefully, this will allow to extend it more easily to support named modes
|
|
|
|
and / or properties set directly on the command line.
|
|
|
|
|
|
|
|
Reviewed-by: Noralf Trønnes <noralf@tronnes.org>
|
|
|
|
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
|
|
|
|
Link: https://patchwork.freedesktop.org/patch/msgid/e32cd4009153b184103554009135c7bf7c9975d7.1560783090.git-series.maxime.ripard@bootlin.com
|
|
|
|
---
|
|
|
|
drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------
|
|
|
|
1 file changed, 210 insertions(+), 115 deletions(-)
|
|
|
|
|
|
|
|
--- a/drivers/gpu/drm/drm_modes.c
|
|
|
|
+++ b/drivers/gpu/drm/drm_modes.c
|
|
|
|
@@ -30,6 +30,7 @@
|
|
|
|
* authorization from the copyright holder(s) and author(s).
|
|
|
|
*/
|
|
|
|
|
|
|
|
+#include <linux/ctype.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/list_sort.h>
|
|
|
|
#include <linux/export.h>
|
|
|
|
@@ -1414,6 +1415,151 @@ void drm_connector_list_update(struct dr
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(drm_connector_list_update);
|
|
|
|
|
|
|
|
+static int drm_mode_parse_cmdline_bpp(const char *str, char **end_ptr,
|
|
|
|
+ struct drm_cmdline_mode *mode)
|
|
|
|
+{
|
|
|
|
+ unsigned int bpp;
|
|
|
|
+
|
|
|
|
+ if (str[0] != '-')
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ str++;
|
|
|
|
+ bpp = simple_strtol(str, end_ptr, 10);
|
|
|
|
+ if (*end_ptr == str)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ mode->bpp = bpp;
|
|
|
|
+ mode->bpp_specified = true;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int drm_mode_parse_cmdline_refresh(const char *str, char **end_ptr,
|
|
|
|
+ struct drm_cmdline_mode *mode)
|
|
|
|
+{
|
|
|
|
+ unsigned int refresh;
|
|
|
|
+
|
|
|
|
+ if (str[0] != '@')
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ str++;
|
|
|
|
+ refresh = simple_strtol(str, end_ptr, 10);
|
|
|
|
+ if (*end_ptr == str)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ mode->refresh = refresh;
|
|
|
|
+ mode->refresh_specified = true;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int drm_mode_parse_cmdline_extra(const char *str, int length,
|
|
|
|
+ struct drm_connector *connector,
|
|
|
|
+ struct drm_cmdline_mode *mode)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < length; i++) {
|
|
|
|
+ switch (str[i]) {
|
|
|
|
+ case 'i':
|
|
|
|
+ mode->interlace = true;
|
|
|
|
+ break;
|
|
|
|
+ case 'm':
|
|
|
|
+ mode->margins = true;
|
|
|
|
+ break;
|
|
|
|
+ case 'D':
|
|
|
|
+ if (mode->force != DRM_FORCE_UNSPECIFIED)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
|
|
|
|
+ (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
|
|
|
|
+ mode->force = DRM_FORCE_ON;
|
|
|
|
+ else
|
|
|
|
+ mode->force = DRM_FORCE_ON_DIGITAL;
|
|
|
|
+ break;
|
|
|
|
+ case 'd':
|
|
|
|
+ if (mode->force != DRM_FORCE_UNSPECIFIED)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ mode->force = DRM_FORCE_OFF;
|
|
|
|
+ break;
|
|
|
|
+ case 'e':
|
|
|
|
+ if (mode->force != DRM_FORCE_UNSPECIFIED)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ mode->force = DRM_FORCE_ON;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length,
|
|
|
|
+ bool extras,
|
|
|
|
+ struct drm_connector *connector,
|
|
|
|
+ struct drm_cmdline_mode *mode)
|
|
|
|
+{
|
|
|
|
+ const char *str_start = str;
|
|
|
|
+ bool rb = false, cvt = false;
|
|
|
|
+ int xres = 0, yres = 0;
|
|
|
|
+ int remaining, i;
|
|
|
|
+ char *end_ptr;
|
|
|
|
+
|
|
|
|
+ xres = simple_strtol(str, &end_ptr, 10);
|
|
|
|
+ if (end_ptr == str)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (end_ptr[0] != 'x')
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ end_ptr++;
|
|
|
|
+
|
|
|
|
+ str = end_ptr;
|
|
|
|
+ yres = simple_strtol(str, &end_ptr, 10);
|
|
|
|
+ if (end_ptr == str)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ remaining = length - (end_ptr - str_start);
|
|
|
|
+ if (remaining < 0)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < remaining; i++) {
|
|
|
|
+ switch (end_ptr[i]) {
|
|
|
|
+ case 'M':
|
|
|
|
+ cvt = true;
|
|
|
|
+ break;
|
|
|
|
+ case 'R':
|
|
|
|
+ rb = true;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ /*
|
|
|
|
+ * Try to pass that to our extras parsing
|
|
|
|
+ * function to handle the case where the
|
|
|
|
+ * extras are directly after the resolution
|
|
|
|
+ */
|
|
|
|
+ if (extras) {
|
|
|
|
+ int ret = drm_mode_parse_cmdline_extra(end_ptr + i,
|
|
|
|
+ 1,
|
|
|
|
+ connector,
|
|
|
|
+ mode);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+ } else {
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mode->xres = xres;
|
|
|
|
+ mode->yres = yres;
|
|
|
|
+ mode->cvt = cvt;
|
|
|
|
+ mode->rb = rb;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
/**
|
|
|
|
* drm_mode_parse_command_line_for_connector - parse command line modeline for connector
|
|
|
|
* @mode_option: optional per connector mode option
|
|
|
|
@@ -1440,13 +1586,12 @@ bool drm_mode_parse_command_line_for_con
|
|
|
|
struct drm_cmdline_mode *mode)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
- unsigned int namelen;
|
|
|
|
- bool res_specified = false, bpp_specified = false, refresh_specified = false;
|
|
|
|
- unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
|
|
|
|
- bool yres_specified = false, cvt = false, rb = false;
|
|
|
|
- bool interlace = false, margins = false, was_digit = false;
|
|
|
|
- int i;
|
|
|
|
- enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
|
|
|
|
+ bool parse_extras = false;
|
|
|
|
+ unsigned int bpp_off = 0, refresh_off = 0;
|
|
|
|
+ unsigned int mode_end = 0;
|
|
|
|
+ char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
|
|
|
|
+ char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
|
|
|
|
+ int ret;
|
|
|
|
|
|
|
|
#ifdef CONFIG_FB
|
|
|
|
if (!mode_option)
|
|
|
|
@@ -1459,127 +1604,77 @@ bool drm_mode_parse_command_line_for_con
|
|
|
|
}
|
|
|
|
|
|
|
|
name = mode_option;
|
|
|
|
- namelen = strlen(name);
|
|
|
|
- for (i = namelen-1; i >= 0; i--) {
|
|
|
|
- switch (name[i]) {
|
|
|
|
- case '@':
|
|
|
|
- if (!refresh_specified && !bpp_specified &&
|
|
|
|
- !yres_specified && !cvt && !rb && was_digit) {
|
|
|
|
- refresh = simple_strtol(&name[i+1], NULL, 10);
|
|
|
|
- refresh_specified = true;
|
|
|
|
- was_digit = false;
|
|
|
|
- } else
|
|
|
|
- goto done;
|
|
|
|
- break;
|
|
|
|
- case '-':
|
|
|
|
- if (!bpp_specified && !yres_specified && !cvt &&
|
|
|
|
- !rb && was_digit) {
|
|
|
|
- bpp = simple_strtol(&name[i+1], NULL, 10);
|
|
|
|
- bpp_specified = true;
|
|
|
|
- was_digit = false;
|
|
|
|
- } else
|
|
|
|
- goto done;
|
|
|
|
- break;
|
|
|
|
- case 'x':
|
|
|
|
- if (!yres_specified && was_digit) {
|
|
|
|
- yres = simple_strtol(&name[i+1], NULL, 10);
|
|
|
|
- yres_specified = true;
|
|
|
|
- was_digit = false;
|
|
|
|
- } else
|
|
|
|
- goto done;
|
|
|
|
- break;
|
|
|
|
- case '0' ... '9':
|
|
|
|
- was_digit = true;
|
|
|
|
- break;
|
|
|
|
- case 'M':
|
|
|
|
- if (yres_specified || cvt || was_digit)
|
|
|
|
- goto done;
|
|
|
|
- cvt = true;
|
|
|
|
- break;
|
|
|
|
- case 'R':
|
|
|
|
- if (yres_specified || cvt || rb || was_digit)
|
|
|
|
- goto done;
|
|
|
|
- rb = true;
|
|
|
|
- break;
|
|
|
|
- case 'm':
|
|
|
|
- if (cvt || yres_specified || was_digit)
|
|
|
|
- goto done;
|
|
|
|
- margins = true;
|
|
|
|
- break;
|
|
|
|
- case 'i':
|
|
|
|
- if (cvt || yres_specified || was_digit)
|
|
|
|
- goto done;
|
|
|
|
- interlace = true;
|
|
|
|
- break;
|
|
|
|
- case 'e':
|
|
|
|
- if (yres_specified || bpp_specified || refresh_specified ||
|
|
|
|
- was_digit || (force != DRM_FORCE_UNSPECIFIED))
|
|
|
|
- goto done;
|
|
|
|
|
|
|
|
- force = DRM_FORCE_ON;
|
|
|
|
- break;
|
|
|
|
- case 'D':
|
|
|
|
- if (yres_specified || bpp_specified || refresh_specified ||
|
|
|
|
- was_digit || (force != DRM_FORCE_UNSPECIFIED))
|
|
|
|
- goto done;
|
|
|
|
+ if (!isdigit(name[0]))
|
|
|
|
+ return false;
|
|
|
|
|
|
|
|
- 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':
|
|
|
|
- if (yres_specified || bpp_specified || refresh_specified ||
|
|
|
|
- was_digit || (force != DRM_FORCE_UNSPECIFIED))
|
|
|
|
- goto done;
|
|
|
|
+ /* Try to locate the bpp and refresh specifiers, if any */
|
|
|
|
+ bpp_ptr = strchr(name, '-');
|
|
|
|
+ if (bpp_ptr) {
|
|
|
|
+ bpp_off = bpp_ptr - name;
|
|
|
|
+ mode->bpp_specified = true;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- force = DRM_FORCE_OFF;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- goto done;
|
|
|
|
- }
|
|
|
|
+ refresh_ptr = strchr(name, '@');
|
|
|
|
+ if (refresh_ptr) {
|
|
|
|
+ refresh_off = refresh_ptr - name;
|
|
|
|
+ mode->refresh_specified = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (i < 0 && yres_specified) {
|
|
|
|
- char *ch;
|
|
|
|
- xres = simple_strtol(name, &ch, 10);
|
|
|
|
- if ((ch != NULL) && (*ch == 'x'))
|
|
|
|
- res_specified = true;
|
|
|
|
- else
|
|
|
|
- i = ch - name;
|
|
|
|
- } else if (!yres_specified && was_digit) {
|
|
|
|
- /* catch mode that begins with digits but has no 'x' */
|
|
|
|
- i = 0;
|
|
|
|
- }
|
|
|
|
-done:
|
|
|
|
- if (i >= 0) {
|
|
|
|
- pr_warn("[drm] parse error at position %i in video mode '%s'\n",
|
|
|
|
- i, name);
|
|
|
|
- mode->specified = false;
|
|
|
|
- return false;
|
|
|
|
+ /* Locate the end of the name / resolution, and parse it */
|
|
|
|
+ if (bpp_ptr && refresh_ptr) {
|
|
|
|
+ mode_end = min(bpp_off, refresh_off);
|
|
|
|
+ } else if (bpp_ptr) {
|
|
|
|
+ mode_end = bpp_off;
|
|
|
|
+ } else if (refresh_ptr) {
|
|
|
|
+ mode_end = refresh_off;
|
|
|
|
+ } else {
|
|
|
|
+ mode_end = strlen(name);
|
|
|
|
+ parse_extras = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (res_specified) {
|
|
|
|
- mode->specified = true;
|
|
|
|
- mode->xres = xres;
|
|
|
|
- mode->yres = yres;
|
|
|
|
+ ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
|
|
|
|
+ parse_extras,
|
|
|
|
+ connector,
|
|
|
|
+ mode);
|
|
|
|
+ if (ret)
|
|
|
|
+ return false;
|
|
|
|
+ mode->specified = true;
|
|
|
|
+
|
|
|
|
+ if (bpp_ptr) {
|
|
|
|
+ ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
|
|
|
|
+ if (ret)
|
|
|
|
+ return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (refresh_specified) {
|
|
|
|
- mode->refresh_specified = true;
|
|
|
|
- mode->refresh = refresh;
|
|
|
|
+ if (refresh_ptr) {
|
|
|
|
+ ret = drm_mode_parse_cmdline_refresh(refresh_ptr,
|
|
|
|
+ &refresh_end_ptr, mode);
|
|
|
|
+ if (ret)
|
|
|
|
+ return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (bpp_specified) {
|
|
|
|
- mode->bpp_specified = true;
|
|
|
|
- mode->bpp = bpp;
|
|
|
|
+ /*
|
|
|
|
+ * Locate the end of the bpp / refresh, and parse the extras
|
|
|
|
+ * if relevant
|
|
|
|
+ */
|
|
|
|
+ if (bpp_ptr && refresh_ptr)
|
|
|
|
+ extra_ptr = max(bpp_end_ptr, refresh_end_ptr);
|
|
|
|
+ else if (bpp_ptr)
|
|
|
|
+ extra_ptr = bpp_end_ptr;
|
|
|
|
+ else if (refresh_ptr)
|
|
|
|
+ extra_ptr = refresh_end_ptr;
|
|
|
|
+
|
|
|
|
+ if (extra_ptr) {
|
|
|
|
+ int remaining = strlen(name) - (extra_ptr - name);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We still have characters to process, while
|
|
|
|
+ * we shouldn't have any
|
|
|
|
+ */
|
|
|
|
+ if (remaining > 0)
|
|
|
|
+ return false;
|
|
|
|
}
|
|
|
|
- mode->rb = rb;
|
|
|
|
- mode->cvt = cvt;
|
|
|
|
- mode->interlace = interlace;
|
|
|
|
- mode->margins = margins;
|
|
|
|
- mode->force = force;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|