drm/fb: add support for tiled monitor configurations. (v2)
authorDave Airlie <airlied@redhat.com>
Mon, 20 Oct 2014 06:31:53 +0000 (16:31 +1000)
committerDave Airlie <airlied@redhat.com>
Mon, 8 Dec 2014 23:56:49 +0000 (09:56 +1000)
This adds fbdev/con support for tiled monitors, so that we
only set a mode on the correct half of the monitor, or
span the two halves if needed.

v2: remove unneeded ERROR, fix | vs ||

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

index a467460f2aa623d02660edb773364501ae9ec07c..52ce26d6b4fb8aeda59bb6358f62a659bc0464b7 100644 (file)
@@ -1000,19 +1000,21 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
        crtc_count = 0;
        for (i = 0; i < fb_helper->crtc_count; i++) {
                struct drm_display_mode *desired_mode;
+               int x, y;
                desired_mode = fb_helper->crtc_info[i].desired_mode;
-
+               x = fb_helper->crtc_info[i].x;
+               y = fb_helper->crtc_info[i].y;
                if (desired_mode) {
                        if (gamma_size == 0)
                                gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
-                       if (desired_mode->hdisplay < sizes.fb_width)
-                               sizes.fb_width = desired_mode->hdisplay;
-                       if (desired_mode->vdisplay < sizes.fb_height)
-                               sizes.fb_height = desired_mode->vdisplay;
-                       if (desired_mode->hdisplay > sizes.surface_width)
-                               sizes.surface_width = desired_mode->hdisplay;
-                       if (desired_mode->vdisplay > sizes.surface_height)
-                               sizes.surface_height = desired_mode->vdisplay;
+                       if (desired_mode->hdisplay + x < sizes.fb_width)
+                               sizes.fb_width = desired_mode->hdisplay + x;
+                       if (desired_mode->vdisplay + y < sizes.fb_height)
+                               sizes.fb_height = desired_mode->vdisplay + y;
+                       if (desired_mode->hdisplay + x > sizes.surface_width)
+                               sizes.surface_width = desired_mode->hdisplay + x;
+                       if (desired_mode->vdisplay + y > sizes.surface_height)
+                               sizes.surface_height = desired_mode->vdisplay + y;
                        crtc_count++;
                }
        }
@@ -1312,6 +1314,7 @@ 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,
+                             struct drm_fb_offset *offsets,
                              bool *enabled, int width, int height)
 {
        int count, i, j;
@@ -1383,27 +1386,88 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
        return false;
 }
 
+static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
+                               struct drm_display_mode **modes,
+                               struct drm_fb_offset *offsets,
+                               int idx,
+                               int h_idx, int v_idx)
+{
+       struct drm_fb_helper_connector *fb_helper_conn;
+       int i;
+       int hoffset = 0, voffset = 0;
+
+       for (i = 0; i < fb_helper->connector_count; i++) {
+               fb_helper_conn = fb_helper->connector_info[i];
+               if (!fb_helper_conn->connector->has_tile)
+                       continue;
+
+               if (!modes[i] && (h_idx || v_idx)) {
+                       DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
+                                     fb_helper_conn->connector->base.id);
+                       continue;
+               }
+               if (fb_helper_conn->connector->tile_h_loc < h_idx)
+                       hoffset += modes[i]->hdisplay;
+
+               if (fb_helper_conn->connector->tile_v_loc < v_idx)
+                       voffset += modes[i]->vdisplay;
+       }
+       offsets[idx].x = hoffset;
+       offsets[idx].y = voffset;
+       DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
+       return 0;
+}
+
 static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
                                 struct drm_display_mode **modes,
+                                struct drm_fb_offset *offsets,
                                 bool *enabled, int width, int height)
 {
        struct drm_fb_helper_connector *fb_helper_conn;
        int i;
-
+       uint64_t conn_configured = 0, mask;
+       int tile_pass = 0;
+       mask = (1 << fb_helper->connector_count) - 1;
+retry:
        for (i = 0; i < fb_helper->connector_count; i++) {
                fb_helper_conn = fb_helper->connector_info[i];
 
-               if (enabled[i] == false)
+               if (conn_configured & (1 << i))
+                       continue;
+
+               if (enabled[i] == false) {
+                       conn_configured |= (1 << i);
+                       continue;
+               }
+
+               /* first pass over all the untiled connectors */
+               if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
                        continue;
 
+               if (tile_pass == 1) {
+                       if (fb_helper_conn->connector->tile_h_loc != 0 ||
+                           fb_helper_conn->connector->tile_v_loc != 0)
+                               continue;
+
+               } else {
+                       if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 &&
+                           fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
+                       /* if this tile_pass doesn't cover any of the tiles - keep going */
+                               continue;
+
+                       /* find the tile offsets for this pass - need
+                          to find all tiles left and above */
+                       drm_get_tile_offsets(fb_helper, modes, offsets,
+                                            i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
+               }
                DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
                              fb_helper_conn->connector->base.id);
 
                /* got for command line mode first */
                modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
                if (!modes[i]) {
-                       DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
-                                     fb_helper_conn->connector->base.id);
+                       DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
+                                     fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
                        modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
                }
                /* No preferred modes, pick one off the list */
@@ -1413,6 +1477,12 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
                }
                DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
                          "none");
+               conn_configured |= (1 << i);
+       }
+
+       if ((conn_configured & mask) != mask) {
+               tile_pass++;
+               goto retry;
        }
        return true;
 }
@@ -1502,6 +1572,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
        struct drm_device *dev = fb_helper->dev;
        struct drm_fb_helper_crtc **crtcs;
        struct drm_display_mode **modes;
+       struct drm_fb_offset *offsets;
        struct drm_mode_set *modeset;
        bool *enabled;
        int width, height;
@@ -1516,9 +1587,11 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
                        sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
        modes = kcalloc(dev->mode_config.num_connector,
                        sizeof(struct drm_display_mode *), GFP_KERNEL);
+       offsets = kcalloc(dev->mode_config.num_connector,
+                         sizeof(struct drm_fb_offset), GFP_KERNEL);
        enabled = kcalloc(dev->mode_config.num_connector,
                          sizeof(bool), GFP_KERNEL);
-       if (!crtcs || !modes || !enabled) {
+       if (!crtcs || !modes || !enabled || !offsets) {
                DRM_ERROR("Memory allocation failed\n");
                goto out;
        }
@@ -1528,14 +1601,16 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 
        if (!(fb_helper->funcs->initial_config &&
              fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
+                                              offsets,
                                               enabled, width, height))) {
                memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
                memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
+               memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0]));
 
-               if (!drm_target_cloned(fb_helper,
-                                      modes, enabled, width, height) &&
-                   !drm_target_preferred(fb_helper,
-                                         modes, enabled, width, height))
+               if (!drm_target_cloned(fb_helper, modes, offsets,
+                                      enabled, width, height) &&
+                   !drm_target_preferred(fb_helper, modes, offsets,
+                                         enabled, width, height))
                        DRM_ERROR("Unable to find initial modes\n");
 
                DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
@@ -1555,18 +1630,23 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
        for (i = 0; i < fb_helper->connector_count; i++) {
                struct drm_display_mode *mode = modes[i];
                struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
+               struct drm_fb_offset *offset = &offsets[i];
                modeset = &fb_crtc->mode_set;
 
                if (mode && fb_crtc) {
-                       DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
-                                     mode->name, fb_crtc->mode_set.crtc->base.id);
+                       DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
+                                     mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
                        fb_crtc->desired_mode = mode;
+                       fb_crtc->x = offset->x;
+                       fb_crtc->y = offset->y;
                        if (modeset->mode)
                                drm_mode_destroy(dev, modeset->mode);
                        modeset->mode = drm_mode_duplicate(dev,
                                                           fb_crtc->desired_mode);
                        modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
                        modeset->fb = fb_helper->fb;
+                       modeset->x = offset->x;
+                       modeset->y = offset->y;
                }
        }
 
@@ -1583,6 +1663,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 out:
        kfree(crtcs);
        kfree(modes);
+       kfree(offsets);
        kfree(enabled);
 }
 
index f2183b554cbc7708db5567f4cb65e32061de594d..850cf7d6578cef54dd2792852e247322995e5a68 100644 (file)
@@ -324,6 +324,7 @@ intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc)
 static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                                    struct drm_fb_helper_crtc **crtcs,
                                    struct drm_display_mode **modes,
+                                   struct drm_fb_offset *offsets,
                                    bool *enabled, int width, int height)
 {
        struct drm_device *dev = fb_helper->dev;
@@ -332,6 +333,8 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
        bool fallback = true;
        int num_connectors_enabled = 0;
        int num_connectors_detected = 0;
+       uint64_t conn_configured = 0, mask;
+       int pass = 0;
 
        save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool),
                               GFP_KERNEL);
@@ -339,7 +342,8 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                return false;
 
        memcpy(save_enabled, enabled, dev->mode_config.num_connector);
-
+       mask = (1 << fb_helper->connector_count) - 1;
+retry:
        for (i = 0; i < fb_helper->connector_count; i++) {
                struct drm_fb_helper_connector *fb_conn;
                struct drm_connector *connector;
@@ -349,12 +353,19 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                fb_conn = fb_helper->connector_info[i];
                connector = fb_conn->connector;
 
+               if (conn_configured & (1 << i))
+                       continue;
+
+               if (pass == 0 && !connector->has_tile)
+                       continue;
+
                if (connector->status == connector_status_connected)
                        num_connectors_detected++;
 
                if (!enabled[i]) {
                        DRM_DEBUG_KMS("connector %s not enabled, skipping\n",
                                      connector->name);
+                       conn_configured |= (1 << i);
                        continue;
                }
 
@@ -373,6 +384,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                        DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
                                      connector->name);
                        enabled[i] = false;
+                       conn_configured |= (1 << i);
                        continue;
                }
 
@@ -400,8 +412,8 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
 
                /* try for preferred next */
                if (!modes[i]) {
-                       DRM_DEBUG_KMS("looking for preferred mode on connector %s\n",
-                                     connector->name);
+                       DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n",
+                                     connector->name, connector->has_tile);
                        modes[i] = drm_has_preferred_mode(fb_conn, width,
                                                          height);
                }
@@ -444,6 +456,12 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
                              modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :"");
 
                fallback = false;
+               conn_configured |= (1 << i);
+       }
+
+       if ((conn_configured & mask) != mask) {
+               pass++;
+               goto retry;
        }
 
        /*
index f4ad254e3488ea4ebdcf98410757f7e5d01e7cd9..b597068103aa9d8d1bf66841f76858e904f72c34 100644 (file)
@@ -34,9 +34,14 @@ struct drm_fb_helper;
 
 #include <linux/kgdb.h>
 
+struct drm_fb_offset {
+       int x, y;
+};
+
 struct drm_fb_helper_crtc {
        struct drm_mode_set mode_set;
        struct drm_display_mode *desired_mode;
+       int x, y;
 };
 
 struct drm_fb_helper_surface_size {
@@ -72,6 +77,7 @@ struct drm_fb_helper_funcs {
        bool (*initial_config)(struct drm_fb_helper *fb_helper,
                               struct drm_fb_helper_crtc **crtcs,
                               struct drm_display_mode **modes,
+                              struct drm_fb_offset *offsets,
                               bool *enabled, int width, int height);
 };