From: Dave Airlie <airl...@redhat.com>

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.

Signed-off-by: Dave Airlie <airlied at redhat.com>
---
 drivers/gpu/drm/drm_fb_helper.c    | 122 +++++++++++++++++++++++++++++++------
 drivers/gpu/drm/i915/intel_fbdev.c |  25 +++++++-
 include/drm/drm_fb_helper.h        |   6 ++
 3 files changed, 130 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 3144db9..095f9d5 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1042,19 +1042,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++;
                }
        }
@@ -1356,6 +1358,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;
@@ -1427,27 +1430,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 */
@@ -1457,6 +1521,13 @@ 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) {
+               DRM_ERROR("not all connectors configured  %d %llx %llx\n", 
tile_pass, conn_configured, mask);
+               tile_pass++;
+               goto retry;
        }
        return true;
 }
@@ -1546,6 +1617,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;
@@ -1560,9 +1632,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;
        }
@@ -1572,14 +1646,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",
@@ -1599,18 +1675,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;
                }
        }

@@ -1628,6 +1709,7 @@ static void drm_setup_crtcs(struct drm_fb_helper 
*fb_helper)
 out:
        kfree(crtcs);
        kfree(modes);
+       kfree(offsets);
        kfree(enabled);
 }

diff --git a/drivers/gpu/drm/i915/intel_fbdev.c 
b/drivers/gpu/drm/i915/intel_fbdev.c
index f475414..73faf85 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -322,6 +322,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;
@@ -330,6 +331,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;

        /*
         * If the user specified any force options, just bail here
@@ -355,7 +358,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;
@@ -365,12 +369,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;
                }

@@ -379,6 +390,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;
                }

@@ -407,8 +419,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);
                }
@@ -451,6 +463,13 @@ 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) {
+               DRM_ERROR("not all connectors configured  %d %llx %llx\n", 
pass, conn_configured, mask);
+               pass++;
+               goto retry;
        }

        /*
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index bfd329d..7fcb1e7 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -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);
 };

-- 
2.1.0

Reply via email to