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