If a CRT doesn't deign to provide a preferred mode, we should try to
pick a mode with a vaguely reasonable DPI instead of whatever happens to
be biggest.

Bugzilla: https://bugzilla.redhat.com/522155

Signed-off-by: Adam Jackson <a...@redhat.com>
---
 drivers/gpu/drm/drm_edid.c      |   52 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_fb_helper.c |   14 +++++++++-
 include/drm/drm_crtc.h          |    3 ++
 3 files changed, 68 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index ece03fc..7d7f4dc 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1810,3 +1810,55 @@ int drm_add_modes_noedid(struct drm_connector *connector,
        return num_modes;
 }
 EXPORT_SYMBOL(drm_add_modes_noedid);
+
+/**
+ * drm_edid_analog_mode_guess - guess a mode somewhere near 96dpi
+ * @connector: connector to inspect
+ * @max_width: maximum width
+ * @max_height: maximum height
+ *
+ * Some old (usually CRT) monitors do not specify a preferred mode.  Usually
+ * they also expose very high resolutions, which leads to unreadably small
+ * text.  Instead pick something reasonable.
+ *
+ * Returns the supported mode closest to 96dpi within some tolerance, if any.
+ *
+ * Only looks at horizontal DPI, but, whatever.
+ */
+struct drm_display_mode *
+drm_edid_analog_mode_guess(struct drm_connector *connector,
+                          int max_width, int max_height)
+{
+       struct drm_display_mode *mode, *best = NULL;
+       int best_dpi = 0;
+       struct edid *edid;
+       
+       edid = connector->edid_blob_ptr->data;
+       if (!edid)
+               return NULL;
+
+       if ((edid->features & DRM_EDID_INPUT_DIGITAL))
+               return NULL;
+
+       if (!edid->width_cm || !edid->height_cm)
+               return NULL;
+
+       list_for_each_entry(mode, &connector->probed_modes, head) {
+               int dpi = (mode->hdisplay * 254) / (edid->width_cm * 100);
+
+               if (mode->hdisplay > max_width || mode->vdisplay > max_height)
+                       continue;
+
+               if (abs(96 - dpi) < abs(96 - best_dpi)) {
+                       best_dpi = dpi;
+                       best = mode;
+               }
+       }
+
+       /* if we can't get anywhere near 96dpi, we probably shouldn't try */
+       if (abs(96 - best_dpi) > 10)
+               return NULL;
+
+       return best;
+}
+EXPORT_SYMBOL(drm_edid_analog_mode_guess);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index aada26f..14d544e 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1177,15 +1177,27 @@ static bool drm_target_preferred(struct drm_fb_helper 
*fb_helper,
                DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
                              fb_helper_conn->connector->base.id);
 
-               /* got for command line mode first */
+               /* go for command line mode first */
                modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
+
+               /* then edid-preferred, if any */
                if (!modes[i]) {
                        DRM_DEBUG_KMS("looking for preferred mode on connector 
%d\n",
                                      fb_helper_conn->connector->base.id);
                        modes[i] = drm_has_preferred_mode(fb_helper_conn, 
width, height);
                }
+
+               /* then something maybe near 96dpi if possible */
+               if (!modes[i] && fb_helper_conn->connector->edid_blob_ptr) {
+                       DRM_DEBUG_KMS("looking for 96dpi mode on connector 
%d\n",
+                                     fb_helper_conn->connector->base.id);
+                       modes[i] = 
drm_edid_analog_mode_guess(fb_helper_conn->connector, width, height);
+               }
+
                /* No preferred modes, pick one off the list */
                if (!modes[i] && 
!list_empty(&fb_helper_conn->connector->modes)) {
+                       DRM_DEBUG_KMS("guessing mode on connector %d\n",
+                                     fb_helper_conn->connector->base.id);
                        list_for_each_entry(modes[i], 
&fb_helper_conn->connector->modes, head)
                                break;
                }
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 4cd4be2..1d04589 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -993,6 +993,9 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct 
drm_device *dev,
                                int GTF_2C, int GTF_K, int GTF_2J);
 extern int drm_add_modes_noedid(struct drm_connector *connector,
                                int hdisplay, int vdisplay);
+extern struct drm_display_mode *drm_edid_analog_mode_guess(
+                               struct drm_connector *connector,
+                               int max_width, int max_height);
 
 extern int drm_edid_header_is_valid(const u8 *raw_edid);
 extern bool drm_edid_is_valid(struct edid *edid);
-- 
1.7.7.6

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to