Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 02db9ac..8823269 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -30,14 +30,18 @@
* 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>
-#include <drm/drmP.h>
-#include <drm/drm_crtc.h>
+
#include <video/of_videomode.h>
#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
#include <drm/drm_modes.h>
+#include <drm/drm_print.h>
#include "drm_crtc_internal.h"
@@ -71,11 +75,6 @@
if (!nmode)
return NULL;
- if (drm_mode_object_add(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
- kfree(nmode);
- return NULL;
- }
-
return nmode;
}
EXPORT_SYMBOL(drm_mode_create);
@@ -92,8 +91,6 @@
if (!mode)
return;
- drm_mode_object_unregister(dev, &mode->base);
-
kfree(mode);
}
EXPORT_SYMBOL(drm_mode_destroy);
@@ -161,6 +158,9 @@
int interlace;
u64 tmp;
+ if (!hdisplay || !vdisplay)
+ return NULL;
+
/* allocate the drm_display_mode structure. If failure, we will
* return directly
*/
@@ -395,6 +395,9 @@
int hsync, hfront_porch, vodd_front_porch_lines;
unsigned int tmp1, tmp2;
+ if (!hdisplay || !vdisplay)
+ return NULL;
+
drm_mode = drm_mode_create(dev);
if (!drm_mode)
return NULL;
@@ -662,22 +665,22 @@
* @bus_flags: information about pixelclk, sync and DE polarity will be stored
* here
*
- * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE and
- * DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS
+ * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_DRIVE_(POS|NEG)EDGE
+ * and DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS
* found in @vm
*/
void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags)
{
*bus_flags = 0;
if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
- *bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
+ *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
- *bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+ *bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE)
- *bus_flags |= DRM_BUS_FLAG_SYNC_POSEDGE;
+ *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE;
if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE)
- *bus_flags |= DRM_BUS_FLAG_SYNC_NEGEDGE;
+ *bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
if (vm->flags & DISPLAY_FLAGS_DE_LOW)
*bus_flags |= DRM_BUS_FLAG_DE_LOW;
@@ -716,8 +719,8 @@
if (bus_flags)
drm_bus_flags_from_videomode(&vm, bus_flags);
- pr_debug("%pOF: got %dx%d display mode from %s\n",
- np, vm.hactive, vm.vactive, np->name);
+ pr_debug("%pOF: got %dx%d display mode\n",
+ np, vm.hactive, vm.vactive);
drm_mode_debug_printmodeline(dmode);
return 0;
@@ -758,7 +761,7 @@
if (mode->hsync)
return mode->hsync;
- if (mode->htotal < 0)
+ if (mode->htotal <= 0)
return 0;
calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */
@@ -911,11 +914,9 @@
*/
void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src)
{
- int id = dst->base.id;
struct list_head head = dst->head;
*dst = *src;
- dst->base.id = id;
dst->head = head;
}
EXPORT_SYMBOL(drm_mode_copy);
@@ -1281,7 +1282,7 @@
* @verbose: be verbose about it
*
* This helper function can be used to prune a display mode list after
- * validation has been completed. All modes who's status is not MODE_OK will be
+ * validation has been completed. All modes whose status is not MODE_OK will be
* removed from the list, and if @verbose the status code and mode name is also
* printed to dmesg.
*/
@@ -1414,6 +1415,284 @@
}
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,
+ bool freestanding,
+ const struct drm_connector *connector,
+ struct drm_cmdline_mode *mode)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ switch (str[i]) {
+ case 'i':
+ if (freestanding)
+ return -EINVAL;
+
+ mode->interlace = true;
+ break;
+ case 'm':
+ if (freestanding)
+ return -EINVAL;
+
+ 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,
+ const 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,
+ false,
+ connector,
+ mode);
+ if (ret)
+ return ret;
+ } else {
+ return -EINVAL;
+ }
+ }
+ }
+
+ mode->xres = xres;
+ mode->yres = yres;
+ mode->cvt = cvt;
+ mode->rb = rb;
+
+ return 0;
+}
+
+static int drm_mode_parse_cmdline_options(char *str, size_t len,
+ const struct drm_connector *connector,
+ struct drm_cmdline_mode *mode)
+{
+ unsigned int rotation = 0;
+ char *sep = str;
+
+ while ((sep = strchr(sep, ','))) {
+ char *delim, *option;
+
+ option = sep + 1;
+ delim = strchr(option, '=');
+ if (!delim) {
+ delim = strchr(option, ',');
+
+ if (!delim)
+ delim = str + len;
+ }
+
+ if (!strncmp(option, "rotate", delim - option)) {
+ const char *value = delim + 1;
+ unsigned int deg;
+
+ deg = simple_strtol(value, &sep, 10);
+
+ /* Make sure we have parsed something */
+ if (sep == value)
+ return -EINVAL;
+
+ switch (deg) {
+ case 0:
+ rotation |= DRM_MODE_ROTATE_0;
+ break;
+
+ case 90:
+ rotation |= DRM_MODE_ROTATE_90;
+ break;
+
+ case 180:
+ rotation |= DRM_MODE_ROTATE_180;
+ break;
+
+ case 270:
+ rotation |= DRM_MODE_ROTATE_270;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else if (!strncmp(option, "reflect_x", delim - option)) {
+ rotation |= DRM_MODE_REFLECT_X;
+ sep = delim;
+ } else if (!strncmp(option, "reflect_y", delim - option)) {
+ rotation |= DRM_MODE_REFLECT_Y;
+ sep = delim;
+ } else if (!strncmp(option, "margin_right", delim - option)) {
+ const char *value = delim + 1;
+ unsigned int margin;
+
+ margin = simple_strtol(value, &sep, 10);
+
+ /* Make sure we have parsed something */
+ if (sep == value)
+ return -EINVAL;
+
+ mode->tv_margins.right = margin;
+ } else if (!strncmp(option, "margin_left", delim - option)) {
+ const char *value = delim + 1;
+ unsigned int margin;
+
+ margin = simple_strtol(value, &sep, 10);
+
+ /* Make sure we have parsed something */
+ if (sep == value)
+ return -EINVAL;
+
+ mode->tv_margins.left = margin;
+ } else if (!strncmp(option, "margin_top", delim - option)) {
+ const char *value = delim + 1;
+ unsigned int margin;
+
+ margin = simple_strtol(value, &sep, 10);
+
+ /* Make sure we have parsed something */
+ if (sep == value)
+ return -EINVAL;
+
+ mode->tv_margins.top = margin;
+ } else if (!strncmp(option, "margin_bottom", delim - option)) {
+ const char *value = delim + 1;
+ unsigned int margin;
+
+ margin = simple_strtol(value, &sep, 10);
+
+ /* Make sure we have parsed something */
+ if (sep == value)
+ return -EINVAL;
+
+ mode->tv_margins.bottom = margin;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ mode->rotation_reflection = rotation;
+
+ return 0;
+}
+
+static const char * const drm_named_modes_whitelist[] = {
+ "NTSC",
+ "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
@@ -1429,6 +1708,10 @@
*
* <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
*
+ * Additionals options can be provided following the mode, using a comma to
+ * separate each option. Valid options can be found in
+ * Documentation/fb/modedb.rst.
+ *
* The intermediate drm_cmdline_mode structure is required to store additional
* options from the command line modline like the force-enable/disable flag.
*
@@ -1436,17 +1719,17 @@
* True if a valid modeline has been parsed, false otherwise.
*/
bool drm_mode_parse_command_line_for_connector(const char *mode_option,
- struct drm_connector *connector,
+ const struct drm_connector *connector,
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 named_mode = false, parse_extras = false;
+ unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
+ unsigned int mode_end = 0;
+ char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
+ char *options_ptr = NULL;
+ char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+ int ret;
#ifdef CONFIG_FB
if (!mode_option)
@@ -1459,127 +1742,131 @@
}
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;
+ /*
+ * 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);
- 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;
-
- force = DRM_FORCE_OFF;
- break;
- default:
- goto done;
+ /*
+ * 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;
}
- 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;
- }
-
- if (res_specified) {
- mode->specified = true;
- mode->xres = xres;
- mode->yres = yres;
- }
-
- if (refresh_specified) {
- mode->refresh_specified = true;
- mode->refresh = refresh;
- }
-
- if (bpp_specified) {
+ /* 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;
- mode->bpp = bpp;
}
- mode->rb = rb;
- mode->cvt = cvt;
- mode->interlace = interlace;
- mode->margins = margins;
- mode->force = force;
+
+ refresh_ptr = strchr(name, '@');
+ if (refresh_ptr) {
+ if (named_mode)
+ return false;
+
+ refresh_off = refresh_ptr - name;
+ mode->refresh_specified = true;
+ }
+
+ /* Locate the start of named options */
+ options_ptr = strchr(name, ',');
+ if (options_ptr)
+ options_off = options_ptr - name;
+
+ /* Locate the end of the name / resolution, and parse it */
+ if (bpp_ptr) {
+ mode_end = bpp_off;
+ } else if (refresh_ptr) {
+ mode_end = refresh_off;
+ } else if (options_ptr) {
+ mode_end = options_off;
+ } else {
+ mode_end = strlen(name);
+ parse_extras = true;
+ }
+
+ if (named_mode) {
+ if (mode_end + 1 > DRM_DISPLAY_MODE_LEN)
+ return false;
+
+ if (!drm_named_mode_is_in_whitelist(name, mode_end))
+ return false;
+
+ strscpy(mode->name, name, mode_end + 1);
+ } else {
+ 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_ptr) {
+ ret = drm_mode_parse_cmdline_refresh(refresh_ptr,
+ &refresh_end_ptr, mode);
+ if (ret)
+ return false;
+ }
+
+ /*
+ * 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 &&
+ extra_ptr != options_ptr) {
+ int len = strlen(name) - (extra_ptr - name);
+
+ ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
+ connector, mode);
+ if (ret)
+ return false;
+ }
+
+ if (options_ptr) {
+ int len = strlen(name) - (options_ptr - name);
+
+ ret = drm_mode_parse_cmdline_options(options_ptr, len,
+ connector, mode);
+ if (ret)
+ return false;
+ }
return true;
}
@@ -1669,8 +1956,11 @@
case HDMI_PICTURE_ASPECT_256_135:
out->flags |= DRM_MODE_FLAG_PIC_AR_256_135;
break;
- case HDMI_PICTURE_ASPECT_RESERVED:
default:
+ WARN(1, "Invalid aspect ratio (0%x) on mode\n",
+ in->picture_aspect_ratio);
+ /* fall through */
+ case HDMI_PICTURE_ASPECT_NONE:
out->flags |= DRM_MODE_FLAG_PIC_AR_NONE;
break;
}
@@ -1729,20 +2019,22 @@
switch (in->flags & DRM_MODE_FLAG_PIC_AR_MASK) {
case DRM_MODE_FLAG_PIC_AR_4_3:
- out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_4_3;
+ out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
break;
case DRM_MODE_FLAG_PIC_AR_16_9:
- out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_16_9;
+ out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
break;
case DRM_MODE_FLAG_PIC_AR_64_27:
- out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_64_27;
+ out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27;
break;
case DRM_MODE_FLAG_PIC_AR_256_135:
- out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_256_135;
+ out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135;
break;
- default:
+ case DRM_MODE_FLAG_PIC_AR_NONE:
out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
break;
+ default:
+ return -EINVAL;
}
out->status = drm_mode_validate_driver(dev, out);