Hi,
The proprietary stack for the RaspberryPi allows for a number of video parameters widely used by their users, but yet don't have any equivalents in the mainline kernel.
Those options are detailed here: https://www.raspberrypi.org/documentation/configuration/config-txt/video.md
While not all of them are desirable to have in the mainline kernel, some of them still have value, such as properties to initialise the overscan or rotation parameters.
This series is an attempt to support those, and is based on a rewrite of the command line parser I did a couple of years ago that never reached upstream (due to a lack of time on my side). While this parser was initially made to deal with named modes (in order to support TV modes), it also allowed to extend it more easily, which is why it's resurrected.
It relies on the series "drm/fb-helper: Move modesetting code to drm_client" by Noralf Trønnes found here: https://patchwork.freedesktop.org/series/58598/
Let me know what you think, Maxime
Changes from v4: - Change the name of the variable to make it clear it's about reflection and rotation. - Fix the reflection case in drm_client_rotation - Fix the documentation accordingly - Added missing modedb.rst documentation - Fix a chunk not in the proper commit - Fix a few typos - Rebased on top of current next
Changes from v3: - Add documentation for drm_cmdline_mode and the new variables - Move the TV properties reset to a helper - Fix a missing X resolution or a missing Y resolution - Add more tests - Add the rotation to the orientation - Fix the reflection - Change the name of the drm_client_panel_rotation function - Rebased on top of current next
Changes from v2: - Fixed some sparse warnings - Rebased on top of next and Noralf series - Moved the property initialisation to vc4 reset hook - Added documentation for the new drm_cmdline_mode - Renamed overscan to tv_margins to be consistent with the APIs
Changes from v1: - Dropped the patches to deal with EDID - Added the unit test as selftest - Rebased on next
Maxime Ripard (12): drm/connector: Add documentation for drm_cmdline_mode drm/client: Restrict the plane_state scope drm/client: Restrict the rotation check to the rotation itself drm/client: Change drm_client_panel_rotation name drm/modes: Rewrite the command line parser drm/modes: Support modes names on the command line drm/modes: Allow to specify rotation and reflection on the commandline drm/connector: Introduce a TV margins structure drm/modes: Parse overscan properties drm/atomic: Add a function to reset connector TV properties drm/selftests: Add command line parser selftests drm/vc4: hdmi: Set default state margin at reset
Documentation/fb/modedb.rst | 13 +- drivers/gpu/drm/drm_atomic_state_helper.c | 18 +- drivers/gpu/drm/drm_client_modeset.c | 54 +- drivers/gpu/drm/drm_connector.c | 3 +- drivers/gpu/drm/drm_fb_helper.c | 2 +- drivers/gpu/drm/drm_modes.c | 473 +++++-- drivers/gpu/drm/selftests/Makefile | 2 +- drivers/gpu/drm/selftests/drm_cmdline_selftests.h | 55 +- drivers/gpu/drm/selftests/test-drm_cmdline_parser.c | 918 +++++++++++++- drivers/gpu/drm/vc4/vc4_hdmi.c | 8 +- include/drm/drm_atomic_state_helper.h | 1 +- include/drm/drm_client.h | 2 +- include/drm/drm_connector.h | 149 +- 13 files changed, 1557 insertions(+), 141 deletions(-) create mode 100644 drivers/gpu/drm/selftests/drm_cmdline_selftests.h create mode 100644 drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
base-commit: a125097c841039deef9dd589b86467f7d20f4b3d
The struct drm_cmdline_mode holds the result of the command line parsers. However, it wasn't documented so far, so let's do that.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- include/drm/drm_connector.h | 86 +++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-)
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c6f8486d8b8f..c802780b0bfc 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -923,18 +923,100 @@ struct drm_connector_funcs { const struct drm_connector_state *state); };
-/* mode specified on the command line */ +/** + * struct drm_cmdline_mode - DRM Mode passed through the kernel command-line + * + * Each connector can have an initial mode with additional options + * passed through the kernel command line. This structure allows to + * express those parameters and will be filled by the command-line + * parser. + */ struct drm_cmdline_mode { + /** + * @specified: + * + * Has a mode been read from the command-line? + */ bool specified; + + /** + * @refresh_specified: + * + * Did the mode have a preferred refresh rate? + */ bool refresh_specified; + + /** + * @bpp_specified: + * + * Did the mode have a preferred BPP? + */ bool bpp_specified; - int xres, yres; + + /** + * @xres: + * + * Active resolution on the X axis, in pixels. + */ + int xres; + + /** + * @yres: + * + * Active resolution on the Y axis, in pixels. + */ + int yres; + + /** + * @bpp: + * + * Bits per pixels for the mode. + */ int bpp; + + /** + * @refresh: + * + * Refresh rate, in Hertz. + */ int refresh; + + /** + * @rb: + * + * Do we need to use reduced blanking? + */ bool rb; + + /** + * @interlace: + * + * The mode is interlaced. + */ bool interlace; + + /** + * @cvt: + * + * The timings will be calculated using the VESA Coordinated + * Video Timings instead of looking up the mode from a table. + */ bool cvt; + + /** + * @margins: + * + * Add margins to the mode calculation (1.8% of xres rounded + * down to 8 pixels and 1.8% of yres). + */ bool margins; + + /** + * @force: + * + * Ignore the hotplug state of the connector, and force its + * state to one of the DRM_FORCE_* values. + */ enum drm_connector_force force; };
The drm_client_modeset_commit_atomic function uses two times the plane_state variable in inner blocks of code, but the variable has a scope global to this function.
This will lead to inadvertent devs to reuse the variable in the second block with the value left by the first, without any warning from the compiler since value would have been initialized.
Fix this by moving the variable declaration to the proper scope.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_client_modeset.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 006bf7390e7d..8264c3a732b0 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -861,7 +861,6 @@ EXPORT_SYMBOL(drm_client_panel_rotation); static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active) { struct drm_device *dev = client->dev; - struct drm_plane_state *plane_state; struct drm_plane *plane; struct drm_atomic_state *state; struct drm_modeset_acquire_ctx ctx; @@ -879,6 +878,8 @@ static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool state->acquire_ctx = &ctx; retry: drm_for_each_plane(plane, dev) { + struct drm_plane_state *plane_state; + plane_state = drm_atomic_get_plane_state(state, plane); if (IS_ERR(plane_state)) { ret = PTR_ERR(plane_state); @@ -901,6 +902,8 @@ static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool unsigned int rotation;
if (drm_client_panel_rotation(mode_set, &rotation)) { + struct drm_plane_state *plane_state; + /* Cannot fail as we've already gotten the plane state above */ plane_state = drm_atomic_get_new_plane_state(state, primary); plane_state->rotation = rotation;
The drm_client_rotation has a check on the rotation value, but the reflections are also stored in the same variable, and the check doesn't take this into account.
Therefore, even though we might have a valid rotation, if we're also using a reflection parameter, the test will fail for no particular reason.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_client_modeset.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 8264c3a732b0..b4e5fb0a17cf 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -845,7 +845,8 @@ bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotat * depending on the hardware this may require the framebuffer * to be in a specific tiling format. */ - if (*rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) + if ((*rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_180 || + !plane->rotation_property) return false;
for (i = 0; i < plane->rotation_property->num_values; i++)
The drm_client_panel_rotation function has been used so far to set the default rotation based on the panel orientation.
However, we can have more sources of information to make that decision, starting with the command line that we will introduce later in this series.
Change the name to remove the panel mention.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_client_modeset.c | 12 ++++++------ drivers/gpu/drm/drm_fb_helper.c | 2 +- include/drm/drm_client.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index b4e5fb0a17cf..4869a0170bec 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -804,19 +804,19 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, EXPORT_SYMBOL(drm_client_modeset_probe);
/** - * drm_client_panel_rotation() - Check panel orientation + * drm_client_rotation() - Check the initial rotation value * @modeset: DRM modeset * @rotation: Returned rotation value * - * This function checks if the primary plane in @modeset can hw rotate to match - * the panel orientation on its connector. + * This function checks if the primary plane in @modeset can hw rotate + * to match the rotation needed on its connector. * * Note: Currently only 0 and 180 degrees are supported. * * Return: * True if the plane can do the rotation, false otherwise. */ -bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation) +bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) { struct drm_connector *connector = modeset->connectors[0]; struct drm_plane *plane = modeset->crtc->primary; @@ -857,7 +857,7 @@ bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotat
return true; } -EXPORT_SYMBOL(drm_client_panel_rotation); +EXPORT_SYMBOL(drm_client_rotation);
static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool active) { @@ -902,7 +902,7 @@ static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool struct drm_plane *primary = mode_set->crtc->primary; unsigned int rotation;
- if (drm_client_panel_rotation(mode_set, &rotation)) { + if (drm_client_rotation(mode_set, &rotation)) { struct drm_plane_state *plane_state;
/* Cannot fail as we've already gotten the plane state above */ diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 42852cae749b..1984e5c54d58 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1722,7 +1722,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
modeset->fb = fb_helper->fb;
- if (drm_client_panel_rotation(modeset, &rotation)) + if (drm_client_rotation(modeset, &rotation)) /* Rotating in hardware, fbcon should not rotate */ sw_rotations |= DRM_MODE_ROTATE_0; else diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index f2d5ed745733..72d51d1e9dd9 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -153,7 +153,7 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); int drm_client_modeset_create(struct drm_client_dev *client); void drm_client_modeset_free(struct drm_client_dev *client); int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height); -bool drm_client_panel_rotation(struct drm_mode_set *modeset, unsigned int *rotation); +bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation); int drm_client_modeset_commit_force(struct drm_client_dev *client); int drm_client_modeset_commit(struct drm_client_dev *client); int drm_client_modeset_dpms(struct drm_client_dev *client, int mode);
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com --- drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 115 deletions(-)
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 5a07a28fec6d..6debbd6c1763 100644 --- 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> @@ -1408,6 +1409,151 @@ void drm_connector_list_update(struct drm_connector *connector) } 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 @@ -1434,13 +1580,12 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, 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) @@ -1453,127 +1598,77 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, }
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; + /* 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; } -done: - if (i >= 0) { - pr_warn("[drm] parse error at position %i in video mode '%s'\n", - i, name); - mode->specified = false; + + ret = drm_mode_parse_cmdline_res_mode(name, mode_end, + parse_extras, + connector, + mode); + if (ret) return false; - } + mode->specified = true;
- if (res_specified) { - mode->specified = true; - mode->xres = xres; - mode->yres = yres; + 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; }
17.06.2019 17:51, Maxime Ripard пишет:
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 115 deletions(-)
Hello,
I have a Tegra device that uses a stock android bootloader which passes "video=tegrafb" in the kernels cmdline. That wasn't a problem before this patch, but now Tegra DRM driver fails to probe because the mode is 0x0:0 and hence framebuffer allocation fails. Is it a legit regression that should be fixed in upstream?
Hi,
On Fri, Jul 05, 2019 at 07:54:47PM +0300, Dmitry Osipenko wrote:
17.06.2019 17:51, Maxime Ripard пишет:
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 115 deletions(-)
I have a Tegra device that uses a stock android bootloader which passes "video=tegrafb" in the kernels cmdline. That wasn't a problem before this patch, but now Tegra DRM driver fails to probe because the mode is 0x0:0 and hence framebuffer allocation fails. Is it a legit regression that should be fixed in upstream?
Thierry indeed reported that issue, but the discussion pretty much stalled since then.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
09.07.2019 15:45, Maxime Ripard пишет:
Hi,
On Fri, Jul 05, 2019 at 07:54:47PM +0300, Dmitry Osipenko wrote:
17.06.2019 17:51, Maxime Ripard пишет:
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 115 deletions(-)
I have a Tegra device that uses a stock android bootloader which passes "video=tegrafb" in the kernels cmdline. That wasn't a problem before this patch, but now Tegra DRM driver fails to probe because the mode is 0x0:0 and hence framebuffer allocation fails. Is it a legit regression that should be fixed in upstream?
Thierry indeed reported that issue, but the discussion pretty much stalled since then.
Sorry, this doesn't answer my question. Where it was reported?
If it's a valid regression (my device is broken), then the patch should either be fixed or reverted.
On 09/07/2019 13:52, Dmitry Osipenko wrote:
09.07.2019 15:45, Maxime Ripard пишет:
Hi,
On Fri, Jul 05, 2019 at 07:54:47PM +0300, Dmitry Osipenko wrote:
17.06.2019 17:51, Maxime Ripard пишет:
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 115 deletions(-)
I have a Tegra device that uses a stock android bootloader which passes "video=tegrafb" in the kernels cmdline. That wasn't a problem before this patch, but now Tegra DRM driver fails to probe because the mode is 0x0:0 and hence framebuffer allocation fails. Is it a legit regression that should be fixed in upstream?
Thierry indeed reported that issue, but the discussion pretty much stalled since then.
Yes Thierry is currently out and hence has not responded. I had been planning to look at this this week and responded.
Sorry, this doesn't answer my question. Where it was reported?
Same thread [0]. Dmitry, are you able to respond to Maxime's response to Thierry?
Cheers Jon
[0] https://patchwork.kernel.org/patch/10999393/
On 09/07/2019 14:26, Jon Hunter wrote:
On 09/07/2019 13:52, Dmitry Osipenko wrote:
09.07.2019 15:45, Maxime Ripard пишет:
Hi,
On Fri, Jul 05, 2019 at 07:54:47PM +0300, Dmitry Osipenko wrote:
17.06.2019 17:51, Maxime Ripard пишет:
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 115 deletions(-)
I have a Tegra device that uses a stock android bootloader which passes "video=tegrafb" in the kernels cmdline. That wasn't a problem before this patch, but now Tegra DRM driver fails to probe because the mode is 0x0:0 and hence framebuffer allocation fails. Is it a legit regression that should be fixed in upstream?
Thierry indeed reported that issue, but the discussion pretty much stalled since then.
Yes Thierry is currently out and hence has not responded. I had been planning to look at this this week and responded.
Sorry, this doesn't answer my question. Where it was reported?
Same thread [0].
Correction, it was on patch 6/12 and not this one.
Jon
09.07.2019 16:28, Jon Hunter пишет:
On 09/07/2019 14:26, Jon Hunter wrote:
On 09/07/2019 13:52, Dmitry Osipenko wrote:
09.07.2019 15:45, Maxime Ripard пишет:
Hi,
On Fri, Jul 05, 2019 at 07:54:47PM +0300, Dmitry Osipenko wrote:
17.06.2019 17:51, Maxime Ripard пишет:
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
drivers/gpu/drm/drm_modes.c | 325 +++++++++++++++++++++++-------------- 1 file changed, 210 insertions(+), 115 deletions(-)
I have a Tegra device that uses a stock android bootloader which passes "video=tegrafb" in the kernels cmdline. That wasn't a problem before this patch, but now Tegra DRM driver fails to probe because the mode is 0x0:0 and hence framebuffer allocation fails. Is it a legit regression that should be fixed in upstream?
Thierry indeed reported that issue, but the discussion pretty much stalled since then.
Yes Thierry is currently out and hence has not responded. I had been planning to look at this this week and responded.
Sorry, this doesn't answer my question. Where it was reported?
Same thread [0].
Correction, it was on patch 6/12 and not this one.
Thank you very much, Jon! So looks like this patch is simply broken because it silently copies the "mode's name" and then drm_connector_get_cmdline_mode() doesn't even try to validate the mode.
+CC: Thomas Graichen
Dne ponedeljek, 17. junij 2019 ob 16:51:32 CEST je Maxime Ripard napisal(a):
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
Thomas reported to me that this patch breaks "video=CONNECTOR:e" kernel parameter which he currently uses as a workaround for H6 HDMI monitor detection issue on one STB.
I suppose this is the same issue that Dmitry noticed.
Thomas Graichen (in CC) can provide more information if needed.
Best regards, Jernej
On Mon, Aug 19, 2019 at 8:54 PM Jernej Škrabec jernej.skrabec@gmail.com wrote:
+CC: Thomas Graichen
Dne ponedeljek, 17. junij 2019 ob 16:51:32 CEST je Maxime Ripard napisal(a):
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
Thomas reported to me that this patch breaks "video=CONNECTOR:e" kernel parameter which he currently uses as a workaround for H6 HDMI monitor detection issue on one STB.
I suppose this is the same issue that Dmitry noticed.
Thomas Graichen (in CC) can provide more information if needed.
Best regards, Jernej
as jernej already mentioned i am currently having to use the kernel cmdline option video=HDMI-A-1:e to get a working hdmi output on an eachlink h6 mini tv box and was wondering that i did not get any hdmi output even with this option when switching from the https://github.com/megous/linux oprange-pi-5.2 to the orange-pi-5.3 branch which seems to contain this patch. as i had no idea what might have caused the breakage of the hdmi output and did a full bisect of the kernel between those two versions, which ended reliably at exactly this patch - so i guess there is a regression at least with the video=CONNECTOR:e option (maybe others too?) with this patches code which makes it not working anymore.
best wishes - thomas
Hi,
On Mon, Aug 19, 2019 at 09:20:00PM +0200, Thomas Graichen wrote:
On Mon, Aug 19, 2019 at 8:54 PM Jernej Škrabec jernej.skrabec@gmail.com wrote:
+CC: Thomas Graichen
Dne ponedeljek, 17. junij 2019 ob 16:51:32 CEST je Maxime Ripard napisal(a):
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
Thomas reported to me that this patch breaks "video=CONNECTOR:e" kernel parameter which he currently uses as a workaround for H6 HDMI monitor detection issue on one STB.
I suppose this is the same issue that Dmitry noticed.
Thomas Graichen (in CC) can provide more information if needed.
as jernej already mentioned i am currently having to use the kernel cmdline option video=HDMI-A-1:e to get a working hdmi output on an eachlink h6 mini tv box and was wondering that i did not get any hdmi output even with this option when switching from the https://github.com/megous/linux oprange-pi-5.2 to the orange-pi-5.3 branch which seems to contain this patch.
Which kernel version is that based on?
as i had no idea what might have caused the breakage of the hdmi output and did a full bisect of the kernel between those two versions, which ended reliably at exactly this patch - so i guess there is a regression at least with the video=CONNECTOR:e option (maybe others too?) with this patches code which makes it not working anymore.
I'm not sure I'll have the time to look into it this week (or the next, unfortunately). However, the e parameter is supposed to be parsed by drm_mode_parse_cmdline_extra, which in turn is supposed to be called there: https://elixir.bootlin.com/linux/v5.3-rc5/source/drivers/gpu/drm/drm_modes.c...
If you can test that, having an idea of if that function is called, which return code it returns, and if it isn't if why would be super helpful.
Thanks! Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
hi maxim,
On Tue, Aug 20, 2019 at 5:00 PM Maxime Ripard maxime.ripard@bootlin.com wrote:
Hi,
On Mon, Aug 19, 2019 at 09:20:00PM +0200, Thomas Graichen wrote:
On Mon, Aug 19, 2019 at 8:54 PM Jernej Škrabec jernej.skrabec@gmail.com wrote:
+CC: Thomas Graichen
Dne ponedeljek, 17. junij 2019 ob 16:51:32 CEST je Maxime Ripard napisal(a):
From: Maxime Ripard maxime.ripard@free-electrons.com
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@free-electrons.com
Thomas reported to me that this patch breaks "video=CONNECTOR:e" kernel parameter which he currently uses as a workaround for H6 HDMI monitor detection issue on one STB.
I suppose this is the same issue that Dmitry noticed.
Thomas Graichen (in CC) can provide more information if needed.
as jernej already mentioned i am currently having to use the kernel cmdline option video=HDMI-A-1:e to get a working hdmi output on an eachlink h6 mini tv box and was wondering that i did not get any hdmi output even with this option when switching from the https://github.com/megous/linux oprange-pi-5.2 to the orange-pi-5.3 branch which seems to contain this patch.
Which kernel version is that based on?
5.3-rc3
as i had no idea what might have caused the breakage of the hdmi output and did a full bisect of the kernel between those two versions, which ended reliably at exactly this patch - so i guess there is a regression at least with the video=CONNECTOR:e option (maybe others too?) with this patches code which makes it not working anymore.
I'm not sure I'll have the time to look into it this week (or the next, unfortunately). However, the e parameter is supposed to be parsed by drm_mode_parse_cmdline_extra, which in turn is supposed to be called there: https://elixir.bootlin.com/linux/v5.3-rc5/source/drivers/gpu/drm/drm_modes.c...
If you can test that, having an idea of if that function is called, which return code it returns, and if it isn't if why would be super helpful.
i just added a printk and it looks like it is not getting called:
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index b0369e690f36..4c58fdb1d7be 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1813,6 +1813,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
ret = drm_mode_parse_cmdline_extra(extra_ptr, len, connector, mode); + printk(KERN_WARNING "DEBUG - drm_mode_parse_cmdline_extra %d", ret); if (ret) return false; }
no output from it in dmesg (my loglevel=8 and on the kernel cmdline and in /proc/cmdline i have "video=HDMI-A-1:e") - so looks like it really gets lost somewhere along the way ...
best wishes - thomas
From: Maxime Ripard maxime.ripard@free-electrons.com
The drm subsystem also uses the video= kernel parameter, and in the documentation refers to the fbdev documentation for that parameter.
However, that documentation also says that instead of giving the mode using its resolution we can also give a name. However, DRM doesn't handle that case at the moment. Even though in most case it shouldn't make any difference, it might be useful for analog modes, where different standards might have the same resolution, but still have a few different parameters that are not encoded in the modes (NTSC vs NTSC-J vs PAL-M for example).
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com --- drivers/gpu/drm/drm_client_modeset.c | 4 ++- drivers/gpu/drm/drm_connector.c | 3 +- drivers/gpu/drm/drm_modes.c | 62 +++++++++++++++++++++-------- include/drm/drm_connector.h | 7 +++- 4 files changed, 59 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 4869a0170bec..33d4988f22ae 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -149,6 +149,10 @@ drm_connector_pick_cmdline_mode(struct drm_connector *connector) prefer_non_interlace = !cmdline_mode->interlace; again: list_for_each_entry(mode, &connector->modes, head) { + /* Check (optional) mode name first */ + if (!strcmp(mode->name, cmdline_mode->name)) + return mode; + /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || mode->vdisplay != cmdline_mode->yres) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3ccdcf3dfcde..3afed5677946 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -139,8 +139,9 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector) connector->force = mode->force; }
- DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", + DRM_DEBUG_KMS("cmdline mode for connector %s %s %dx%d@%dHz%s%s%s\n", connector->name, + mode->name ? mode->name : "", mode->xres, mode->yres, mode->refresh_specified ? mode->refresh : 60, mode->rb ? " reduced blanking" : "", diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 6debbd6c1763..429d3be17800 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1580,7 +1580,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, struct drm_cmdline_mode *mode) { const char *name; - bool parse_extras = false; + bool named_mode = false, 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; @@ -1599,8 +1599,22 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
name = mode_option;
- if (!isdigit(name[0])) - return false; + /* + * 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. + * - However, if the X resolution is missing, then we end up + * with something like x<yres>, with our first character + * being an alpha-numerical character, which would be + * considered 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') + named_mode = true;
/* Try to locate the bpp and refresh specifiers, if any */ bpp_ptr = strchr(name, '-'); @@ -1611,6 +1625,9 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
refresh_ptr = strchr(name, '@'); if (refresh_ptr) { + if (named_mode) + return false; + refresh_off = refresh_ptr - name; mode->refresh_specified = true; } @@ -1627,12 +1644,16 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, parse_extras = true; }
- ret = drm_mode_parse_cmdline_res_mode(name, mode_end, - parse_extras, - connector, - mode); - if (ret) - return false; + if (named_mode) { + strncpy(mode->name, name, mode_end); + } 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) { @@ -1660,14 +1681,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, extra_ptr = refresh_end_ptr;
if (extra_ptr) { - int remaining = strlen(name) - (extra_ptr - name); + if (!named_mode) { + int len = strlen(name) - (extra_ptr - name);
- /* - * We still have characters to process, while - * we shouldn't have any - */ - if (remaining > 0) - return false; + ret = drm_mode_parse_cmdline_extra(extra_ptr, len, + connector, mode); + if (ret) + return false; + } else { + int remaining = strlen(name) - (extra_ptr - name); + + /* + * We still have characters to process, while + * we shouldn't have any + */ + if (remaining > 0) + return false; + } }
return true; diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c802780b0bfc..cdf2fb910010 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -933,6 +933,13 @@ struct drm_connector_funcs { */ struct drm_cmdline_mode { /** + * @name: + * + * Name of the mode. + */ + char name[DRM_DISPLAY_MODE_LEN]; + + /** * @specified: * * Has a mode been read from the command-line?
On Mon, Jun 17, 2019 at 04:51:33PM +0200, Maxime Ripard wrote:
From: Maxime Ripard maxime.ripard@free-electrons.com
The drm subsystem also uses the video= kernel parameter, and in the documentation refers to the fbdev documentation for that parameter.
However, that documentation also says that instead of giving the mode using its resolution we can also give a name. However, DRM doesn't handle that case at the moment. Even though in most case it shouldn't make any difference, it might be useful for analog modes, where different standards might have the same resolution, but still have a few different parameters that are not encoded in the modes (NTSC vs NTSC-J vs PAL-M for example).
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
drivers/gpu/drm/drm_client_modeset.c | 4 ++- drivers/gpu/drm/drm_connector.c | 3 +- drivers/gpu/drm/drm_modes.c | 62 +++++++++++++++++++++-------- include/drm/drm_connector.h | 7 +++- 4 files changed, 59 insertions(+), 17 deletions(-)
This patch causes an issue on various Tegra boards that have so far been running flawlessly. Here's an extract from the boot log:
[ 0.000000] Kernel command line: root=/dev/nfs rw netdevwait ip=:::::eth0:on nfsroot=192.168.23.1:/srv/nfs/tegra194 console=ttyTCU0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 rootfstype=ext4 video=tegrafb no_console_suspend=1 earlycon=tegra_comb_uart,mmio32,0x0c168000 gpt usbcore.old_scheme_first=1 tegraid=19.1.2.0.0 maxcpus=8 boot.slot_suffix= boot.ratchetvalues=0.2.2 vpr=0x8000000@0xf0000000 sdhci_tegra.en_boot_part_access=1 ... [ 18.597001] [drm:drm_connector_init [drm]] cmdline mode for connector DP-1 tegrafb 0x0@60Hz ... [ 18.627145] [drm:drm_connector_init [drm]] cmdline mode for connector DP-2 tegrafb 0x0@60Hz ... [ 18.673770] [drm:drm_connector_init [drm]] cmdline mode for connector HDMI-A-1 tegrafb 0x0@60Hz ... [ 19.057500] [drm:drm_mode_debug_printmodeline [drm]] Modeline "0x0": 0 0 0 0 0 0 0 1 4 1 0x20 0x6 [ 19.066341] [drm:drm_mode_prune_invalid [drm]] Not using 0x0 mode: CLOCK_LOW ... [ 19.677803] [drm:drm_client_modeset_probe [drm]] looking for cmdline mode on connector 60 [ 19.686019] [drm:drm_client_modeset_probe [drm]] found mode 0x0 ... [ 19.851843] drm drm: failed to set initial configuration: -28
So basically what's happening here is that the bootloader is passing a video= parameter on the command-line and after this patch, the DRM core will consider the tegrafb in that parameter to be a named video mode. The mode is then filtered out because it doesn't make any sense, but then drm_client_modeset_probe() still tries to use it, eventually leading to failure because we can't allocate memory for a 0x0 framebuffer.
Now, there are obviously a couple of places where things go wrong. On one hand I think if the mode specified on the command-line is already filtered out, then drm_client_modeset_probe() should not be trying to use it.
One could also argue that the bootloader shouldn't be passing that video=tegrafb parameter in the first place. Then again, this is nothing out of the ordinary (as documented in Documentation/fb/modedb.rst).
The problem with named modes, and you already highlighted this in your comment in the code, is that it's not possible to distinguish between a mode name and a video= option that defines the framebuffer device to use.
That said, I wouldn't be surprised if this change ended up breaking on other devices. I'm also not sure that under these circumstances it's a good idea to support named modes. At least not until we have a better way of determining what's a real mode name and what isn't. Looking at the old modedb from fb, not even the standard modes listed there have names associated with them, so I'm not sure how this was ever supposed to work. From the looks of it, some of the fbdev drivers seem to take a mode list from board-code (see for example the mx21ads_modes array from arch/arm/mach-imx/mach-mx21ads.c). The imxfb driver can then take a mode name from the command line and try to match it against a list of known modes. That seems to match what the documentation says.
However, that also really only works because this is all directly dealt with in the fbdev drivers. For DRM/KMS we don't do that and instead we rely on the core to provide this backwards-compatibility. However, at the time when we parse the mode from the command line we don't have the list of modes that are considered to be valid, so your patch currently needs to assume that it is a valid mode. I don't think that's a good idea, because clearly not all strings that currently make it through the filter are actually modes.
So if we really need this, I think we want some way for the connector to provide the list of named modes that it supports so that by the time we want to parse the command-line we can check whether it's actually a name to avoid false positives like the ones I'm seeing on Tegra.
For now it might just be easiest to avoid any of this and disable the named mode support until it's a bit more mature. The patch no longer reverts cleanly, but it should be fairly easy to disable the feature in a follow-up patch again.
Thierry
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 4869a0170bec..33d4988f22ae 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -149,6 +149,10 @@ drm_connector_pick_cmdline_mode(struct drm_connector *connector) prefer_non_interlace = !cmdline_mode->interlace; again: list_for_each_entry(mode, &connector->modes, head) {
/* Check (optional) mode name first */
if (!strcmp(mode->name, cmdline_mode->name))
return mode;
- /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || mode->vdisplay != cmdline_mode->yres)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3ccdcf3dfcde..3afed5677946 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -139,8 +139,9 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector) connector->force = mode->force; }
- DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
- DRM_DEBUG_KMS("cmdline mode for connector %s %s %dx%d@%dHz%s%s%s\n", connector->name,
mode->name ? mode->name : "", mode->xres, mode->yres, mode->refresh_specified ? mode->refresh : 60, mode->rb ? " reduced blanking" : "",
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 6debbd6c1763..429d3be17800 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1580,7 +1580,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, struct drm_cmdline_mode *mode) { const char *name;
- bool parse_extras = false;
- bool named_mode = false, 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;
@@ -1599,8 +1599,22 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
name = mode_option;
- if (!isdigit(name[0]))
return false;
/*
* 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.
* - However, if the X resolution is missing, then we end up
* with something like x<yres>, with our first character
* being an alpha-numerical character, which would be
* considered 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')
named_mode = true;
/* Try to locate the bpp and refresh specifiers, if any */ bpp_ptr = strchr(name, '-');
@@ -1611,6 +1625,9 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option,
refresh_ptr = strchr(name, '@'); if (refresh_ptr) {
if (named_mode)
return false;
- refresh_off = refresh_ptr - name; mode->refresh_specified = true; }
@@ -1627,12 +1644,16 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, parse_extras = true; }
- ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
parse_extras,
connector,
mode);
- if (ret)
return false;
if (named_mode) {
strncpy(mode->name, name, mode_end);
} 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) {
@@ -1660,14 +1681,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, extra_ptr = refresh_end_ptr;
if (extra_ptr) {
int remaining = strlen(name) - (extra_ptr - name);
if (!named_mode) {
int len = strlen(name) - (extra_ptr - name);
/*
* We still have characters to process, while
* we shouldn't have any
*/
if (remaining > 0)
return false;
ret = drm_mode_parse_cmdline_extra(extra_ptr, len,
connector, mode);
if (ret)
return false;
} else {
int remaining = strlen(name) - (extra_ptr - name);
/*
* We still have characters to process, while
* we shouldn't have any
*/
if (remaining > 0)
return false;
}
}
return true;
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c802780b0bfc..cdf2fb910010 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -933,6 +933,13 @@ struct drm_connector_funcs { */ struct drm_cmdline_mode { /**
* @name:
*
* Name of the mode.
*/
- char name[DRM_DISPLAY_MODE_LEN];
- /**
- @specified:
- Has a mode been read from the command-line?
-- git-series 0.9.1 _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Hi Thierry,
On Wed, Jun 26, 2019 at 05:26:59PM +0200, Thierry Reding wrote:
On Mon, Jun 17, 2019 at 04:51:33PM +0200, Maxime Ripard wrote:
From: Maxime Ripard maxime.ripard@free-electrons.com
The drm subsystem also uses the video= kernel parameter, and in the documentation refers to the fbdev documentation for that parameter.
However, that documentation also says that instead of giving the mode using its resolution we can also give a name. However, DRM doesn't handle that case at the moment. Even though in most case it shouldn't make any difference, it might be useful for analog modes, where different standards might have the same resolution, but still have a few different parameters that are not encoded in the modes (NTSC vs NTSC-J vs PAL-M for example).
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@free-electrons.com
drivers/gpu/drm/drm_client_modeset.c | 4 ++- drivers/gpu/drm/drm_connector.c | 3 +- drivers/gpu/drm/drm_modes.c | 62 +++++++++++++++++++++-------- include/drm/drm_connector.h | 7 +++- 4 files changed, 59 insertions(+), 17 deletions(-)
This patch causes an issue on various Tegra boards that have so far been running flawlessly. Here's an extract from the boot log:
[ 0.000000] Kernel command line: root=/dev/nfs rw netdevwait ip=:::::eth0:on nfsroot=192.168.23.1:/srv/nfs/tegra194 console=ttyTCU0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 rootfstype=ext4 video=tegrafb no_console_suspend=1 earlycon=tegra_comb_uart,mmio32,0x0c168000 gpt usbcore.old_scheme_first=1 tegraid=19.1.2.0.0 maxcpus=8 boot.slot_suffix= boot.ratchetvalues=0.2.2 vpr=0x8000000@0xf0000000 sdhci_tegra.en_boot_part_access=1 ... [ 18.597001] [drm:drm_connector_init [drm]] cmdline mode for connector DP-1 tegrafb 0x0@60Hz ... [ 18.627145] [drm:drm_connector_init [drm]] cmdline mode for connector DP-2 tegrafb 0x0@60Hz ... [ 18.673770] [drm:drm_connector_init [drm]] cmdline mode for connector HDMI-A-1 tegrafb 0x0@60Hz ... [ 19.057500] [drm:drm_mode_debug_printmodeline [drm]] Modeline "0x0": 0 0 0 0 0 0 0 1 4 1 0x20 0x6 [ 19.066341] [drm:drm_mode_prune_invalid [drm]] Not using 0x0 mode: CLOCK_LOW ... [ 19.677803] [drm:drm_client_modeset_probe [drm]] looking for cmdline mode on connector 60 [ 19.686019] [drm:drm_client_modeset_probe [drm]] found mode 0x0 ... [ 19.851843] drm drm: failed to set initial configuration: -28
So basically what's happening here is that the bootloader is passing a video= parameter on the command-line and after this patch, the DRM core will consider the tegrafb in that parameter to be a named video mode. The mode is then filtered out because it doesn't make any sense, but then drm_client_modeset_probe() still tries to use it, eventually leading to failure because we can't allocate memory for a 0x0 framebuffer.
What was the behaviour before? That it wouldn't set a mode at all?
Now, there are obviously a couple of places where things go wrong. On one hand I think if the mode specified on the command-line is already filtered out, then drm_client_modeset_probe() should not be trying to use it.
Yeah, that would make sense
One could also argue that the bootloader shouldn't be passing that video=tegrafb parameter in the first place. Then again, this is nothing out of the ordinary (as documented in Documentation/fb/modedb.rst).
I've read that documentation, and I'm not sure which section in there allows to do that?
The problem with named modes, and you already highlighted this in your comment in the code, is that it's not possible to distinguish between a mode name and a video= option that defines the framebuffer device to use.
That said, I wouldn't be surprised if this change ended up breaking on other devices. I'm also not sure that under these circumstances it's a good idea to support named modes. At least not until we have a better way of determining what's a real mode name and what isn't. Looking at the old modedb from fb, not even the standard modes listed there have names associated with them, so I'm not sure how this was ever supposed to work. From the looks of it, some of the fbdev drivers seem to take a mode list from board-code (see for example the mx21ads_modes array from arch/arm/mach-imx/mach-mx21ads.c). The imxfb driver can then take a mode name from the command line and try to match it against a list of known modes. That seems to match what the documentation says.
However, that also really only works because this is all directly dealt with in the fbdev drivers. For DRM/KMS we don't do that and instead we rely on the core to provide this backwards-compatibility. However, at the time when we parse the mode from the command line we don't have the list of modes that are considered to be valid, so your patch currently needs to assume that it is a valid mode. I don't think that's a good idea, because clearly not all strings that currently make it through the filter are actually modes.
We have a list of named modes in the connector, and we match against that. It already works for sunxi (minus the bugs..).
So if we really need this, I think we want some way for the connector to provide the list of named modes that it supports so that by the time we want to parse the command-line we can check whether it's actually a name to avoid false positives like the ones I'm seeing on Tegra.
I guess that could work yep
For now it might just be easiest to avoid any of this and disable the named mode support until it's a bit more mature. The patch no longer reverts cleanly, but it should be fairly easy to disable the feature in a follow-up patch again.
If we were to do that, I'd really like to have least get a unit test for that case, and some documentation on how we're supposed to deal with this case.
Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
Rotations and reflections setup are needed in some scenarios to initialise properly the initial framebuffer. Some drivers already had a bunch of quirks to deal with this, such as either a private kernel command line parameter (omapdss) or on the device tree (various panels).
In order to accomodate this, let's create a video mode parameter to deal with the rotation and reflexion.
Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- Documentation/fb/modedb.rst | 11 +++- drivers/gpu/drm/drm_client_modeset.c | 30 +++++++- drivers/gpu/drm/drm_modes.c | 114 +++++++++++++++++++++++----- include/drm/drm_connector.h | 10 ++- 4 files changed, 145 insertions(+), 20 deletions(-)
diff --git a/Documentation/fb/modedb.rst b/Documentation/fb/modedb.rst index 3c2397293977..3e8a6f79dcd7 100644 --- a/Documentation/fb/modedb.rst +++ b/Documentation/fb/modedb.rst @@ -53,6 +53,17 @@ Specifying the option multiple times for different ports is possible, e.g.::
video=LVDS-1:d video=HDMI-1:D
+Options can also be passed after the mode, using commas as separator. + + Sample usage: 720x480,rotate=180 - 720x480 mode, rotated by 180 degrees + +Valid options are: + + - reflect_x (boolean): Perform an axial symetry on the X axis + - reflect_y (boolean): Perform an axial symetry on the Y axis + - rotate (integer): Rotate the initial framebuffer by x + degrees. Valid values are 0, 90, 180 and 270. + -----------------------------------------------------------------------------
What is the VESA(TM) Coordinated Video Timings (CVT)? diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 33d4988f22ae..e95fceac8f8b 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -824,6 +824,7 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) { struct drm_connector *connector = modeset->connectors[0]; struct drm_plane *plane = modeset->crtc->primary; + struct drm_cmdline_mode *cmdline; u64 valid_mask = 0; unsigned int i;
@@ -844,6 +845,35 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) *rotation = DRM_MODE_ROTATE_0; }
+ /** + * The panel already defined the default rotation + * through its orientation. Whatever has been provided + * on the command line needs to be added to that. + * + * Unfortunately, the rotations are at different bit + * indices, so the math to add them up are not as + * trivial as they could. + * + * Reflections on the other hand are pretty trivial to deal with, a + * simple XOR between the two handle the addition nicely. + */ + cmdline = &connector->cmdline_mode; + if (cmdline->specified) { + unsigned int cmdline_rest, panel_rest; + unsigned int cmdline_rot, panel_rot; + unsigned int sum_rot, sum_rest; + + panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK); + cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK); + sum_rot = (panel_rot + cmdline_rot) % 4; + + panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK; + cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK; + sum_rest = panel_rest ^ cmdline_rest; + + *rotation = (1 << sum_rot) | sum_rest; + } + /* * TODO: support 90 / 270 degree hardware rotation, * depending on the hardware this may require the framebuffer diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 429d3be17800..dc6d11292685 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1554,6 +1554,71 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, return 0; }
+static int drm_mode_parse_cmdline_options(char *str, size_t len, + 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 { + return -EINVAL; + } + } + + mode->rotation_reflection = rotation; + + return 0; +} + /** * drm_mode_parse_command_line_for_connector - parse command line modeline for connector * @mode_option: optional per connector mode option @@ -1569,6 +1634,10 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, * * <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.txt. + * * The intermediate drm_cmdline_mode structure is required to store additional * options from the command line modline like the force-enable/disable flag. * @@ -1581,9 +1650,10 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, { const char *name; bool named_mode = false, parse_extras = false; - unsigned int bpp_off = 0, refresh_off = 0; + 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;
@@ -1632,13 +1702,18 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, 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 && refresh_ptr) { - mode_end = min(bpp_off, refresh_off); - } else if (bpp_ptr) { + 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; @@ -1680,24 +1755,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, else if (refresh_ptr) extra_ptr = refresh_end_ptr;
- if (extra_ptr) { - if (!named_mode) { - int len = strlen(name) - (extra_ptr - name); + if (extra_ptr && + extra_ptr != options_ptr) { + int len = strlen(name) - (extra_ptr - name);
- ret = drm_mode_parse_cmdline_extra(extra_ptr, len, - connector, mode); - if (ret) - return false; - } else { - int remaining = strlen(name) - (extra_ptr - name); + ret = drm_mode_parse_cmdline_extra(extra_ptr, len, + connector, mode); + if (ret) + return false; + }
- /* - * We still have characters to process, while - * we shouldn't have any - */ - if (remaining > 0) - 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; diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index cdf2fb910010..8eebe0432c73 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1025,6 +1025,16 @@ struct drm_cmdline_mode { * state to one of the DRM_FORCE_* values. */ enum drm_connector_force force; + + /** + * @rotation_reflection: + * + * Initial rotation and reflection of the mode setup from the + * command line. See DRM_MODE_ROTATE_* and + * DRM_MODE_REFLECT_*. The only rotations supported are + * DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180. + */ + unsigned int rotation_reflection; };
/**
Den 17.06.2019 16.51, skrev Maxime Ripard:
Rotations and reflections setup are needed in some scenarios to initialise properly the initial framebuffer. Some drivers already had a bunch of quirks to deal with this, such as either a private kernel command line parameter (omapdss) or on the device tree (various panels).
In order to accomodate this, let's create a video mode parameter to deal with the rotation and reflexion.
Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com
Documentation/fb/modedb.rst | 11 +++- drivers/gpu/drm/drm_client_modeset.c | 30 +++++++- drivers/gpu/drm/drm_modes.c | 114 +++++++++++++++++++++++----- include/drm/drm_connector.h | 10 ++- 4 files changed, 145 insertions(+), 20 deletions(-)
diff --git a/Documentation/fb/modedb.rst b/Documentation/fb/modedb.rst index 3c2397293977..3e8a6f79dcd7 100644 --- a/Documentation/fb/modedb.rst +++ b/Documentation/fb/modedb.rst @@ -53,6 +53,17 @@ Specifying the option multiple times for different ports is possible, e.g.::
video=LVDS-1:d video=HDMI-1:D
+Options can also be passed after the mode, using commas as separator.
Sample usage: 720x480,rotate=180 - 720x480 mode, rotated by 180 degrees
+Valid options are:
- reflect_x (boolean): Perform an axial symetry on the X axis
- reflect_y (boolean): Perform an axial symetry on the Y axis
2x s/symetry/symmetry/
Reviewed-by: Noralf Trønnes noralf@tronnes.org
- rotate (integer): Rotate the initial framebuffer by x
- degrees. Valid values are 0, 90, 180 and 270.
What is the VESA(TM) Coordinated Video Timings (CVT)? diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 33d4988f22ae..e95fceac8f8b 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -824,6 +824,7 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) { struct drm_connector *connector = modeset->connectors[0]; struct drm_plane *plane = modeset->crtc->primary;
- struct drm_cmdline_mode *cmdline; u64 valid_mask = 0; unsigned int i;
@@ -844,6 +845,35 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) *rotation = DRM_MODE_ROTATE_0; }
- /**
* The panel already defined the default rotation
* through its orientation. Whatever has been provided
* on the command line needs to be added to that.
*
* Unfortunately, the rotations are at different bit
* indices, so the math to add them up are not as
* trivial as they could.
*
* Reflections on the other hand are pretty trivial to deal with, a
* simple XOR between the two handle the addition nicely.
*/
- cmdline = &connector->cmdline_mode;
- if (cmdline->specified) {
unsigned int cmdline_rest, panel_rest;
unsigned int cmdline_rot, panel_rot;
unsigned int sum_rot, sum_rest;
panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK);
cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK);
sum_rot = (panel_rot + cmdline_rot) % 4;
panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK;
cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK;
sum_rest = panel_rest ^ cmdline_rest;
*rotation = (1 << sum_rot) | sum_rest;
- }
- /*
- TODO: support 90 / 270 degree hardware rotation,
- depending on the hardware this may require the framebuffer
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 429d3be17800..dc6d11292685 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1554,6 +1554,71 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, return 0; }
+static int drm_mode_parse_cmdline_options(char *str, size_t len,
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 {
return -EINVAL;
}
- }
- mode->rotation_reflection = rotation;
- return 0;
+}
/**
- drm_mode_parse_command_line_for_connector - parse command line modeline for connector
- @mode_option: optional per connector mode option
@@ -1569,6 +1634,10 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length,
- <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.txt.
- The intermediate drm_cmdline_mode structure is required to store additional
- options from the command line modline like the force-enable/disable flag.
@@ -1581,9 +1650,10 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, { const char *name; bool named_mode = false, parse_extras = false;
- unsigned int bpp_off = 0, refresh_off = 0;
- 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;
@@ -1632,13 +1702,18 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, 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 && refresh_ptr) {
mode_end = min(bpp_off, refresh_off);
- } else if (bpp_ptr) {
- if (bpp_ptr) { mode_end = bpp_off; } else if (refresh_ptr) { mode_end = refresh_off;
- } else if (options_ptr) {
} else { mode_end = strlen(name); parse_extras = true;mode_end = options_off;
@@ -1680,24 +1755,23 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, else if (refresh_ptr) extra_ptr = refresh_end_ptr;
- if (extra_ptr) {
if (!named_mode) {
int len = strlen(name) - (extra_ptr - name);
- if (extra_ptr &&
extra_ptr != options_ptr) {
int len = strlen(name) - (extra_ptr - name);
ret = drm_mode_parse_cmdline_extra(extra_ptr, len,
connector, mode);
if (ret)
return false;
} else {
int remaining = strlen(name) - (extra_ptr - name);
ret = drm_mode_parse_cmdline_extra(extra_ptr, len,
connector, mode);
if (ret)
return false;
- }
/*
* We still have characters to process, while
* we shouldn't have any
*/
if (remaining > 0)
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;
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index cdf2fb910010..8eebe0432c73 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1025,6 +1025,16 @@ struct drm_cmdline_mode { * state to one of the DRM_FORCE_* values. */ enum drm_connector_force force;
- /**
* @rotation_reflection:
*
* Initial rotation and reflection of the mode setup from the
* command line. See DRM_MODE_ROTATE_* and
* DRM_MODE_REFLECT_*. The only rotations supported are
* DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180.
*/
- unsigned int rotation_reflection;
};
/**
On Tue, Jun 18, 2019 at 07:47:04PM +0200, Noralf Trønnes wrote:
diff --git a/Documentation/fb/modedb.rst b/Documentation/fb/modedb.rst index 3c2397293977..3e8a6f79dcd7 100644 --- a/Documentation/fb/modedb.rst +++ b/Documentation/fb/modedb.rst @@ -53,6 +53,17 @@ Specifying the option multiple times for different ports is possible, e.g.::
video=LVDS-1:d video=HDMI-1:D
+Options can also be passed after the mode, using commas as separator.
Sample usage: 720x480,rotate=180 - 720x480 mode, rotated by 180 degrees
+Valid options are:
- reflect_x (boolean): Perform an axial symetry on the X axis
- reflect_y (boolean): Perform an axial symetry on the Y axis
2x s/symetry/symmetry/
Reviewed-by: Noralf Trønnes noralf@tronnes.org
I've fixed it and applied, thanks for your reviews! Maxime
-- Maxime Ripard, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
The TV margins has been defined as a structure inside the drm_connector_state structure so far. However, we will need it in other structures as well, so let's move that structure definition so that it can be reused.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- include/drm/drm_connector.h | 41 +++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-)
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8eebe0432c73..b22e3150e33d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -464,13 +464,37 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info, unsigned int num_formats);
/** + * struct drm_connector_tv_margins - TV connector related margins + * + * Describes the margins in pixels to put around the image on TV + * connectors to deal with overscan. + */ +struct drm_connector_tv_margins { + /** + * @bottom: Bottom margin in pixels. + */ + unsigned int bottom; + + /** + * @left: Left margin in pixels. + */ + unsigned int left; + + /** + * @right: Right margin in pixels. + */ + unsigned int right; + + /** + * @top: Top margin in pixels. + */ + unsigned int top; +}; + +/** * struct drm_tv_connector_state - TV connector related states * @subconnector: selected subconnector - * @margins: margins (all margins are expressed in pixels) - * @margins.left: left margin - * @margins.right: right margin - * @margins.top: top margin - * @margins.bottom: bottom margin + * @margins: TV margins * @mode: TV mode * @brightness: brightness in percent * @contrast: contrast in percent @@ -481,12 +505,7 @@ int drm_display_info_set_bus_formats(struct drm_display_info *info, */ struct drm_tv_connector_state { enum drm_mode_subconnector subconnector; - struct { - unsigned int left; - unsigned int right; - unsigned int top; - unsigned int bottom; - } margins; + struct drm_connector_tv_margins margins; unsigned int mode; unsigned int brightness; unsigned int contrast;
Properly configuring the overscan properties might be needed for the initial setup of the framebuffer for display that still have overscan. Let's allow for more properties on the kernel command line to setup each margin.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- Documentation/fb/modedb.rst | 2 ++- drivers/gpu/drm/drm_modes.c | 44 ++++++++++++++++++++++++++++++++++++++- include/drm/drm_connector.h | 5 ++++- 3 files changed, 51 insertions(+)
diff --git a/Documentation/fb/modedb.rst b/Documentation/fb/modedb.rst index 3e8a6f79dcd7..444bc329e5a8 100644 --- a/Documentation/fb/modedb.rst +++ b/Documentation/fb/modedb.rst @@ -59,6 +59,8 @@ Options can also be passed after the mode, using commas as separator.
Valid options are:
+ - margin_top, margin_bottom, margin_left, margin_right (integer): + Number of pixels in the margins, typically to deal with overscan on TVs - reflect_x (boolean): Perform an axial symetry on the X axis - reflect_y (boolean): Perform an axial symetry on the Y axis - rotate (integer): Rotate the initial framebuffer by x diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index dc6d11292685..57e6408288c8 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1609,6 +1609,50 @@ static int drm_mode_parse_cmdline_options(char *str, size_t len, } 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; } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index b22e3150e33d..ca745d9feaf5 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1054,6 +1054,11 @@ struct drm_cmdline_mode { * DRM_MODE_ROTATE_0 and DRM_MODE_ROTATE_180. */ unsigned int rotation_reflection; + + /** + * @tv_margins: TV margins to apply to the mode. + */ + struct drm_connector_tv_margins tv_margins; };
/**
During the connector reset, if that connector has a TV property, it needs to be reset to the value provided on the command line.
Provide a helper to do that.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- drivers/gpu/drm/drm_atomic_state_helper.c | 18 ++++++++++++++++++ include/drm/drm_atomic_state_helper.h | 1 + 2 files changed, 19 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 7d7347a6f194..46dc264a248b 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -380,6 +380,24 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
/** + * drm_atomic_helper_connector_tv_reset - Resets TV connector properties + * @connector: DRM connector + * + * Resets the TV-related properties attached to a connector. + */ +void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector) +{ + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; + struct drm_connector_state *state = connector->state; + + state->tv.margins.left = cmdline->tv_margins.left; + state->tv.margins.right = cmdline->tv_margins.right; + state->tv.margins.top = cmdline->tv_margins.top; + state->tv.margins.bottom = cmdline->tv_margins.bottom; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); + +/** * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state * @connector: connector object * @state: atomic connector state diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 4e6d2e7a40b8..e4577cc11689 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -62,6 +62,7 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, void __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector); void __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, struct drm_connector_state *state);
The command line parser is pretty tough to get right and very error prone, so let's add a selftest to try to catch any regression.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- drivers/gpu/drm/selftests/Makefile | 2 +- drivers/gpu/drm/selftests/drm_cmdline_selftests.h | 55 +- drivers/gpu/drm/selftests/test-drm_cmdline_parser.c | 918 +++++++++++++- 3 files changed, 974 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/selftests/drm_cmdline_selftests.h create mode 100644 drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 8ec64ecf0e36..aae88f8a016c 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -3,4 +3,4 @@ test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \ test-drm_format.o test-drm_framebuffer.o \ test-drm_damage_helper.o
-obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o +obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o test-drm_cmdline_parser.o diff --git a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h new file mode 100644 index 000000000000..b45824ec7c8f --- /dev/null +++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* List each unit test as selftest(function) + * + * The name is used as both an enum and expanded as igt__name to create + * a module parameter. It must be unique and legal for a C identifier. + * + * Tests are executed in order by igt/drm_mm + */ + +#define cmdline_test(test) selftest(test, test) + +cmdline_test(drm_cmdline_test_res) +cmdline_test(drm_cmdline_test_res_missing_x) +cmdline_test(drm_cmdline_test_res_missing_y) +cmdline_test(drm_cmdline_test_res_bad_y) +cmdline_test(drm_cmdline_test_res_missing_y_bpp) +cmdline_test(drm_cmdline_test_res_vesa) +cmdline_test(drm_cmdline_test_res_vesa_rblank) +cmdline_test(drm_cmdline_test_res_rblank) +cmdline_test(drm_cmdline_test_res_bpp) +cmdline_test(drm_cmdline_test_res_bad_bpp) +cmdline_test(drm_cmdline_test_res_refresh) +cmdline_test(drm_cmdline_test_res_bad_refresh) +cmdline_test(drm_cmdline_test_res_bpp_refresh) +cmdline_test(drm_cmdline_test_res_bpp_refresh_interlaced) +cmdline_test(drm_cmdline_test_res_bpp_refresh_margins) +cmdline_test(drm_cmdline_test_res_bpp_refresh_force_off) +cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_off) +cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on) +cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_analog) +cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_digital) +cmdline_test(drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on) +cmdline_test(drm_cmdline_test_res_margins_force_on) +cmdline_test(drm_cmdline_test_res_vesa_margins) +cmdline_test(drm_cmdline_test_res_invalid_mode) +cmdline_test(drm_cmdline_test_res_bpp_wrong_place_mode) +cmdline_test(drm_cmdline_test_name) +cmdline_test(drm_cmdline_test_name_bpp) +cmdline_test(drm_cmdline_test_name_refresh) +cmdline_test(drm_cmdline_test_name_bpp_refresh) +cmdline_test(drm_cmdline_test_name_refresh_wrong_mode) +cmdline_test(drm_cmdline_test_name_refresh_invalid_mode) +cmdline_test(drm_cmdline_test_name_option) +cmdline_test(drm_cmdline_test_name_bpp_option) +cmdline_test(drm_cmdline_test_rotate_0) +cmdline_test(drm_cmdline_test_rotate_90) +cmdline_test(drm_cmdline_test_rotate_180) +cmdline_test(drm_cmdline_test_rotate_270) +cmdline_test(drm_cmdline_test_rotate_invalid_val) +cmdline_test(drm_cmdline_test_rotate_truncated) +cmdline_test(drm_cmdline_test_hmirror) +cmdline_test(drm_cmdline_test_vmirror) +cmdline_test(drm_cmdline_test_margin_options) +cmdline_test(drm_cmdline_test_multiple_options) +cmdline_test(drm_cmdline_test_invalid_option) diff --git a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c new file mode 100644 index 000000000000..bef4edde6f9f --- /dev/null +++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Bootlin + */ + +#define pr_fmt(fmt) "drm_cmdline: " fmt + +#include <linux/kernel.h> +#include <linux/module.h> + +#include <drm/drm_connector.h> +#include <drm/drm_modes.h> + +#define TESTS "drm_cmdline_selftests.h" +#include "drm_selftest.h" +#include "test-drm_modeset_common.h" + +static int drm_cmdline_test_res(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_res_missing_x(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("x480", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_missing_y(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("1024x", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_bad_y(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("1024xtest", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_missing_y_bpp(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("1024x-24", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_vesa(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480M", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_res_vesa_rblank(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480MR", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_res_rblank(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480R", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_res_bpp(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + 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_res_bad_bpp(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480-test", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_refresh(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480@60", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(mode.bpp_specified); + + 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_res_bad_refresh(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480@refresh", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_bpp_refresh(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + 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_res_bpp_refresh_interlaced(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60i", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + 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_res_bpp_refresh_margins(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60m", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + 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_res_bpp_refresh_force_off(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60d", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + FAIL_ON(mode.rb); + FAIL_ON(mode.cvt); + FAIL_ON(mode.interlace); + FAIL_ON(mode.margins); + FAIL_ON(mode.force != DRM_FORCE_OFF); + + return 0; +} + +static int drm_cmdline_test_res_bpp_refresh_force_on_off(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480-24@60de", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_bpp_refresh_force_on(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60e", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + 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; +} + +static int drm_cmdline_test_res_bpp_refresh_force_on_analog(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60D", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + 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; +} + +static int drm_cmdline_test_res_bpp_refresh_force_on_digital(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + connector.connector_type = DRM_MODE_CONNECTOR_DVII; + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60D", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + FAIL_ON(mode.rb); + FAIL_ON(mode.cvt); + FAIL_ON(mode.interlace); + FAIL_ON(mode.margins); + FAIL_ON(mode.force != DRM_FORCE_ON_DIGITAL); + + return 0; +} + +static int drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60ime", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(!mode.refresh_specified); + FAIL_ON(mode.refresh != 60); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + 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; +} + +static int drm_cmdline_test_res_margins_force_on(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480me", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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; +} + +static int drm_cmdline_test_res_vesa_margins(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480Mm", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_res_invalid_mode(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480f", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_res_bpp_wrong_place_mode(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480e-24", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_name(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC", + &connector, + &mode)); + FAIL_ON(strcmp(mode.name, "NTSC")); + FAIL_ON(mode.refresh_specified); + FAIL_ON(mode.bpp_specified); + + return 0; +} + +static int drm_cmdline_test_name_bpp(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC-24", + &connector, + &mode)); + FAIL_ON(strcmp(mode.name, "NTSC")); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + return 0; +} + +static int drm_cmdline_test_name_bpp_refresh(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC-24@60", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_name_refresh(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_name_refresh_wrong_mode(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60m", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_name_refresh_invalid_mode(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60f", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_name_option(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC,rotate=180", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(strcmp(mode.name, "NTSC")); + FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); + + return 0; +} + +static int drm_cmdline_test_name_bpp_option(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC-24,rotate=180", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(strcmp(mode.name, "NTSC")); + FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); + FAIL_ON(!mode.bpp_specified); + FAIL_ON(mode.bpp != 24); + + return 0; +} + +static int drm_cmdline_test_rotate_0(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=0", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_0); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_rotate_90(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=90", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_90); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_rotate_180(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=180", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_rotate_270(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=270", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_270); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_rotate_invalid_val(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,rotate=42", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_rotate_truncated(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,rotate=", + &connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_hmirror(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,reflect_x", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + FAIL_ON(mode.rotation_reflection != DRM_MODE_REFLECT_X); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_vmirror(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,reflect_y", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + FAIL_ON(mode.rotation_reflection != DRM_MODE_REFLECT_Y); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_margin_options(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + 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.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_multiple_options(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=270,reflect_x", + &connector, + &mode)); + FAIL_ON(!mode.specified); + FAIL_ON(mode.xres != 720); + FAIL_ON(mode.yres != 480); + FAIL_ON(mode.rotation_reflection != (DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X)); + + FAIL_ON(mode.refresh_specified); + + FAIL_ON(mode.bpp_specified); + + 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_invalid_option(void *ignored) +{ + struct drm_connector connector = { }; + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,test=42", + &connector, + &mode)); + + return 0; +} + +#include "drm_selftest.c" + +static int __init test_drm_cmdline_init(void) +{ + int err; + + err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); + + return err > 0 ? 0 : err; +} +module_init(test_drm_cmdline_init); + +MODULE_AUTHOR("Maxime Ripard maxime.ripard@bootlin.com"); +MODULE_LICENSE("GPL");
Now that the TV margins are properly parsed and filled into drm_cmdline_mode, we just need to initialise the first state at reset to get those values and start using them.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com --- drivers/gpu/drm/vc4/vc4_hdmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 6beac1ca1f27..4a9c8c9fe2b1 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -258,11 +258,17 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) return ret; }
+static void vc4_hdmi_connector_reset(struct drm_connector *connector) +{ + drm_atomic_helper_connector_reset(connector); + drm_atomic_helper_connector_tv_reset(connector); +} + static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { .detect = vc4_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = vc4_hdmi_connector_destroy, - .reset = drm_atomic_helper_connector_reset, + .reset = vc4_hdmi_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, };
Maxime Ripard maxime.ripard@bootlin.com writes:
Now that the TV margins are properly parsed and filled into drm_cmdline_mode, we just need to initialise the first state at reset to get those values and start using them.
Reviewed-by: Noralf Trønnes noralf@tronnes.org Signed-off-by: Maxime Ripard maxime.ripard@bootlin.com
Acked-by: Eric Anholt eric@anholt.net
dri-devel@lists.freedesktop.org