aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathon Jongsma <jjongsma@redhat.com>2015-08-20 15:04:32 -0400
committerDave Airlie <airlied@redhat.com>2015-09-10 20:51:33 -0400
commitbd3e1c7c6de9f5f70d97cdb6c817151c0477c5e3 (patch)
treee9e2cf4828ba1e7bbcfac6c198d8dcc09105c2fc
parent73bf1b7be7aab60d7c651402441dd0b0b4991098 (diff)
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 <jjongsma@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c66
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.h2
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,
160 *pwidth = head->width; 160 *pwidth = head->width;
161 *pheight = head->height; 161 *pheight = head->height;
162 drm_mode_probed_add(connector, mode); 162 drm_mode_probed_add(connector, mode);
163 /* remember the last custom size for mode validation */
164 qdev->monitors_config_width = mode->hdisplay;
165 qdev->monitors_config_height = mode->vdisplay;
163 return 1; 166 return 1;
164} 167}
165 168
169static struct mode_size {
170 int w;
171 int h;
172} common_modes[] = {
173 { 640, 480},
174 { 720, 480},
175 { 800, 600},
176 { 848, 480},
177 {1024, 768},
178 {1152, 768},
179 {1280, 720},
180 {1280, 800},
181 {1280, 854},
182 {1280, 960},
183 {1280, 1024},
184 {1440, 900},
185 {1400, 1050},
186 {1680, 1050},
187 {1600, 1200},
188 {1920, 1080},
189 {1920, 1200}
190};
191
166static int qxl_add_common_modes(struct drm_connector *connector, 192static int qxl_add_common_modes(struct drm_connector *connector,
167 unsigned pwidth, 193 unsigned pwidth,
168 unsigned pheight) 194 unsigned pheight)
@@ -170,29 +196,6 @@ static int qxl_add_common_modes(struct drm_connector *connector,
170 struct drm_device *dev = connector->dev; 196 struct drm_device *dev = connector->dev;
171 struct drm_display_mode *mode = NULL; 197 struct drm_display_mode *mode = NULL;
172 int i; 198 int i;
173 struct mode_size {
174 int w;
175 int h;
176 } common_modes[] = {
177 { 640, 480},
178 { 720, 480},
179 { 800, 600},
180 { 848, 480},
181 {1024, 768},
182 {1152, 768},
183 {1280, 720},
184 {1280, 800},
185 {1280, 854},
186 {1280, 960},
187 {1280, 1024},
188 {1440, 900},
189 {1400, 1050},
190 {1680, 1050},
191 {1600, 1200},
192 {1920, 1080},
193 {1920, 1200}
194 };
195
196 for (i = 0; i < ARRAY_SIZE(common_modes); i++) { 199 for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
197 mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 200 mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h,
198 60, false, false, false); 201 60, false, false, false);
@@ -823,11 +826,22 @@ static int qxl_conn_get_modes(struct drm_connector *connector)
823static int qxl_conn_mode_valid(struct drm_connector *connector, 826static int qxl_conn_mode_valid(struct drm_connector *connector,
824 struct drm_display_mode *mode) 827 struct drm_display_mode *mode)
825{ 828{
829 struct drm_device *ddev = connector->dev;
830 struct qxl_device *qdev = ddev->dev_private;
831 int i;
832
826 /* TODO: is this called for user defined modes? (xrandr --add-mode) 833 /* TODO: is this called for user defined modes? (xrandr --add-mode)
827 * TODO: check that the mode fits in the framebuffer */ 834 * TODO: check that the mode fits in the framebuffer */
828 DRM_DEBUG("%s: %dx%d status=%d\n", mode->name, mode->hdisplay, 835
829 mode->vdisplay, mode->status); 836 if(qdev->monitors_config_width == mode->hdisplay &&
830 return MODE_OK; 837 qdev->monitors_config_height == mode->vdisplay)
838 return MODE_OK;
839
840 for (i = 0; i < ARRAY_SIZE(common_modes); i++) {
841 if (common_modes[i].w == mode->hdisplay && common_modes[i].h == mode->vdisplay)
842 return MODE_OK;
843 }
844 return MODE_BAD;
831} 845}
832 846
833static struct drm_encoder *qxl_best_encoder(struct drm_connector *connector) 847static 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 {
325 struct work_struct fb_work; 325 struct work_struct fb_work;
326 326
327 struct drm_property *hotplug_mode_update_property; 327 struct drm_property *hotplug_mode_update_property;
328 int monitors_config_width;
329 int monitors_config_height;
328}; 330};
329 331
330/* forward declaration for QXL_INFO_IO */ 332/* forward declaration for QXL_INFO_IO */