diff --git a/drm-qxl-validate-monitors-config-modes.patch b/drm-qxl-validate-monitors-config-modes.patch new file mode 100644 index 000000000..0b2c93804 --- /dev/null +++ b/drm-qxl-validate-monitors-config-modes.patch @@ -0,0 +1,148 @@ +From 9851cf5d02703ab60b58a2335481cc195c72ff9c Mon Sep 17 00:00:00 2001 +From: Jonathon Jongsma +Date: Thu, 20 Aug 2015 12:04:32 -0700 +Subject: [PATCH] drm/qxl: validate monitors config modes + +Due to some recent changes in +drm_helper_probe_single_connector_modes_merge_bits(), old custom modes +were not being pruned properly. In current kernels, +drm_mode_validate_basic() is called to sanity-check each mode in the +list. If the sanity-check passes, the mode's status gets set to to +MODE_OK. In older kernels this check was not done, so old custom modes +would still have a status of MODE_UNVERIFIED at this point, and would +therefore be pruned later in the function. + +As a result of this new behavior, the list of modes for a device always +includes every custom mode ever configured for the device, with the +largest one listed first. Since desktop environments usually choose the +first preferred mode when a hotplug event is emitted, this had the +result of making it very difficult for the user to reduce the size of +the display. + +The qxl driver did implement the mode_valid connector function, but it +was empty. In order to restore the old behavior where old custom modes +are pruned, we implement a proper mode_valid function for the qxl +driver. This function now checks each mode against the last configured +custom mode and the list of standard modes. If the mode doesn't match +any of these, its status is set to MODE_BAD so that it will be pruned as +expected. + +Signed-off-by: Jonathon Jongsma +Cc: stable at vger.kernel.org +--- + drivers/gpu/drm/qxl/qxl_display.c | 66 ++++++++++++++++++++++++--------------- + drivers/gpu/drm/qxl/qxl_drv.h | 2 ++ + 2 files changed, 42 insertions(+), 26 deletions(-) + +diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c +index a8dbb3ef4e3c..7c6225c84ba6 100644 +--- a/drivers/gpu/drm/qxl/qxl_display.c ++++ b/drivers/gpu/drm/qxl/qxl_display.c +@@ -160,9 +160,35 @@ static int qxl_add_monitors_config_modes(struct drm_connector *connector, + *pwidth = head->width; + *pheight = head->height; + drm_mode_probed_add(connector, mode); ++ /* remember the last custom size for mode validation */ ++ qdev->monitors_config_width = mode->hdisplay; ++ qdev->monitors_config_height = mode->vdisplay; + return 1; + } + ++static struct mode_size { ++ int w; ++ int h; ++} common_modes[] = { ++ { 640, 480}, ++ { 720, 480}, ++ { 800, 600}, ++ { 848, 480}, ++ {1024, 768}, ++ {1152, 768}, ++ {1280, 720}, ++ {1280, 800}, ++ {1280, 854}, ++ {1280, 960}, ++ {1280, 1024}, ++ {1440, 900}, ++ {1400, 1050}, ++ {1680, 1050}, ++ {1600, 1200}, ++ {1920, 1080}, ++ {1920, 1200} ++}; ++ + static int qxl_add_common_modes(struct drm_connector *connector, + unsigned pwidth, + unsigned pheight) +@@ -170,29 +196,6 @@ static int qxl_add_common_modes(struct drm_connector *connector, + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode = NULL; + int i; +- struct mode_size { +- int w; +- int h; +- } common_modes[] = { +- { 640, 480}, +- { 720, 480}, +- { 800, 600}, +- { 848, 480}, +- {1024, 768}, +- {1152, 768}, +- {1280, 720}, +- {1280, 800}, +- {1280, 854}, +- {1280, 960}, +- {1280, 1024}, +- {1440, 900}, +- {1400, 1050}, +- {1680, 1050}, +- {1600, 1200}, +- {1920, 1080}, +- {1920, 1200} +- }; +- + for (i = 0; i < ARRAY_SIZE(common_modes); i++) { + mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, + 60, false, false, false); +@@ -823,11 +826,22 @@ static int qxl_conn_get_modes(struct drm_connector *connector) + static int qxl_conn_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) + { ++ struct drm_device *ddev = connector->dev; ++ struct qxl_device *qdev = ddev->dev_private; ++ int i; ++ + /* TODO: is this called for user defined modes? (xrandr --add-mode) + * TODO: check that the mode fits in the framebuffer */ +- DRM_DEBUG("%s: %dx%d status=%d\n", mode->name, mode->hdisplay, +- mode->vdisplay, mode->status); +- return MODE_OK; ++ ++ if(qdev->monitors_config_width == mode->hdisplay && ++ qdev->monitors_config_height == mode->vdisplay) ++ return MODE_OK; ++ ++ for (i = 0; i < ARRAY_SIZE(common_modes); i++) { ++ if (common_modes[i].w == mode->hdisplay && common_modes[i].h == mode->vdisplay) ++ return MODE_OK; ++ } ++ return MODE_BAD; + } + + static struct drm_encoder *qxl_best_encoder(struct drm_connector *connector) +diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h +index d8549690801d..01a86948eb8c 100644 +--- a/drivers/gpu/drm/qxl/qxl_drv.h ++++ b/drivers/gpu/drm/qxl/qxl_drv.h +@@ -325,6 +325,8 @@ struct qxl_device { + struct work_struct fb_work; + + struct drm_property *hotplug_mode_update_property; ++ int monitors_config_width; ++ int monitors_config_height; + }; + + /* forward declaration for QXL_INFO_IO */ +-- +2.4.3 + diff --git a/kernel.spec b/kernel.spec index 5a05391d0..c4d7f984a 100644 --- a/kernel.spec +++ b/kernel.spec @@ -599,6 +599,9 @@ Patch512: ext4-dont-manipulate-recovery-flag-when-freezing.patch #rhbz 1257534 Patch513: nv46-Change-mc-subdev-oclass-from-nv44-to-nv4c.patch +#rhbz 1212201 +Patch514: drm-qxl-validate-monitors-config-modes.patch + Patch904: kdbus.patch # END OF PATCH DEFINITIONS @@ -2039,6 +2042,7 @@ fi * Thu Aug 27 2015 Josh Boyer - 4.2.0-0.rc8.git2.1 - Linux v4.2-rc8-10-gf9ed72dde34e - Add patch from Hans de Goede to fix nv46 based cards (rhbz 1257534) +- Add patch from Jonathon Jongsma to fix modes in qxl (rhbz 1212201) * Wed Aug 26 2015 Josh Boyer - 4.2.0-0.rc8.git1.1 - Linux v4.2-rc8-7-gf5db4b31b315