drm/fbdev: fix cloning on fbcon
authorDave Airlie <airlied@redhat.com>
Fri, 7 May 2010 05:02:30 +0000 (05:02 +0000)
committerDave Airlie <airlied@redhat.com>
Tue, 18 May 2010 07:40:22 +0000 (17:40 +1000)
Simple cloning rules compared to server:
(a) single crtc
(b) > 1 connector active
(c) check command line mode
(d) try and find 1024x768 DMT mode if no command line.
(e) fail to clone

Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_fb_helper.c
include/drm/drm_crtc.h

index 0acb83a637276685856f26f58813ea5df1f51ffa..dfd4f3677f3b79d69f6e65545c23632be1bea96c 100644 (file)
@@ -658,8 +658,8 @@ static struct drm_display_mode drm_dmt_modes[] = {
 static const int drm_num_dmt_modes =
        sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
 
-static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
-                       int hsize, int vsize, int fresh)
+struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
+                                          int hsize, int vsize, int fresh)
 {
        int i;
        struct drm_display_mode *ptr, *mode;
@@ -677,6 +677,7 @@ static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
        }
        return mode;
 }
+EXPORT_SYMBOL(drm_mode_find_dmt);
 
 typedef void detailed_cb(struct detailed_timing *timing, void *closure);
 
@@ -866,7 +867,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
        }
 
        /* check whether it can be found in default mode table */
-       mode = drm_find_dmt(dev, hsize, vsize, vrefresh_rate);
+       mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate);
        if (mode)
                return mode;
 
@@ -1386,11 +1387,11 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
                        if (m >= num_est3_modes)
                                break;
                        if (est[i] & (1 << j)) {
-                               mode = drm_find_dmt(connector->dev,
-                                                   est3_modes[m].w,
-                                                   est3_modes[m].h,
-                                                   est3_modes[m].r
-                                                   /*, est3_modes[m].rb */);
+                               mode = drm_mode_find_dmt(connector->dev,
+                                                        est3_modes[m].w,
+                                                        est3_modes[m].h,
+                                                        est3_modes[m].r
+                                                        /*, est3_modes[m].rb */);
                                if (mode) {
                                        drm_mode_probed_add(connector, mode);
                                        modes++;
index f7b8fca4bbbc74d1465b17f21d07114f171c6bc8..b3779d243aefc30f48768c67c2f5a261dd825ce2 100644 (file)
@@ -1070,6 +1070,79 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
        }
 }
 
+static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
+                             struct drm_display_mode **modes,
+                             bool *enabled, int width, int height)
+{
+       int count, i, j;
+       bool can_clone = false;
+       struct drm_fb_helper_connector *fb_helper_conn;
+       struct drm_display_mode *dmt_mode, *mode;
+
+       /* only contemplate cloning in the single crtc case */
+       if (fb_helper->crtc_count > 1)
+               return false;
+
+       count = 0;
+       for (i = 0; i < fb_helper->connector_count; i++) {
+               if (enabled[i])
+                       count++;
+       }
+
+       /* only contemplate cloning if more than one connector is enabled */
+       if (count <= 1)
+               return false;
+
+       /* check the command line or if nothing common pick 1024x768 */
+       can_clone = true;
+       for (i = 0; i < fb_helper->connector_count; i++) {
+               if (!enabled[i])
+                       continue;
+               fb_helper_conn = fb_helper->connector_info[i];
+               modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
+               if (!modes[i]) {
+                       can_clone = false;
+                       break;
+               }
+               for (j = 0; j < i; j++) {
+                       if (!enabled[j])
+                               continue;
+                       if (!drm_mode_equal(modes[j], modes[i]))
+                               can_clone = false;
+               }
+       }
+
+       if (can_clone) {
+               DRM_DEBUG_KMS("can clone using command line\n");
+               return true;
+       }
+
+       /* try and find a 1024x768 mode on each connector */
+       can_clone = true;
+       dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60);
+
+       for (i = 0; i < fb_helper->connector_count; i++) {
+
+               if (!enabled[i])
+                       continue;
+
+               fb_helper_conn = fb_helper->connector_info[i];
+               list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
+                       if (drm_mode_equal(mode, dmt_mode))
+                               modes[i] = mode;
+               }
+               if (!modes[i])
+                       can_clone = false;
+       }
+
+       if (can_clone) {
+               DRM_DEBUG_KMS("can clone using 1024x768\n");
+               return true;
+       }
+       DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
+       return false;
+}
+
 static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
                                 struct drm_display_mode **modes,
                                 bool *enabled, int width, int height)
@@ -1163,8 +1236,12 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
                                break;
 
                if (o < n) {
-                       /* ignore cloning for now */
-                       continue;
+                       /* ignore cloning unless only a single crtc */
+                       if (fb_helper->crtc_count > 1)
+                               continue;
+
+                       if (!drm_mode_equal(modes[o], modes[n]))
+                               continue;
                }
 
                crtcs[n] = crtc;
@@ -1214,9 +1291,12 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 
        drm_enable_connectors(fb_helper, enabled);
 
-       ret = drm_target_preferred(fb_helper, modes, enabled, width, height);
-       if (!ret)
-               DRM_ERROR("Unable to find initial modes\n");
+       ret = drm_target_cloned(fb_helper, modes, enabled, width, height);
+       if (!ret) {
+               ret = drm_target_preferred(fb_helper, modes, enabled, width, height);
+               if (!ret)
+                       DRM_ERROR("Unable to find initial modes\n");
+       }
 
        DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
 
index 2e4bf92faa85cc16c21651ce7f5f0e4af03f4210..93a1a31b9c2d4020bc1c9600704a400ae4a48893 100644 (file)
@@ -804,4 +804,6 @@ extern int drm_add_modes_noedid(struct drm_connector *connector,
                                int hdisplay, int vdisplay);
 
 extern bool drm_edid_is_valid(struct edid *edid);
+struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
+                                          int hsize, int vsize, int fresh);
 #endif /* __DRM_CRTC_H__ */