brcm2708: update to latest patches from RPi foundation
[openwrt/staging/dedeckeh.git] / target / linux / brcm2708 / patches-4.19 / 950-0639-drm-modes-Rewrite-the-command-line-parser.patch
diff --git a/target/linux/brcm2708/patches-4.19/950-0639-drm-modes-Rewrite-the-command-line-parser.patch b/target/linux/brcm2708/patches-4.19/950-0639-drm-modes-Rewrite-the-command-line-parser.patch
new file mode 100644 (file)
index 0000000..24c25d6
--- /dev/null
@@ -0,0 +1,392 @@
+From 3508a8548f13be68b6d098ad99a7bc1fc1810f76 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard@bootlin.com>
+Date: Wed, 19 Jun 2019 12:17:49 +0200
+Subject: [PATCH] drm/modes: Rewrite the command line parser
+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;
+ }