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

Using the tiling info attempt to set a mode across two crtcs
---
 drivers/gpu/drm/drm_crtc.c            | 100 +++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/drm_displayid.c       |  13 ++++-
 drivers/gpu/drm/drm_dp_mst_topology.c |  18 +++++-
 drivers/gpu/drm/drm_edid.c            |   2 +
 drivers/gpu/drm/drm_probe_helper.c    |   2 +-
 drivers/gpu/drm/i915/intel_modes.c    |   1 +
 include/drm/drm_crtc.h                |   8 +++
 7 files changed, 140 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 90e7730..99fa259 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -2506,6 +2506,93 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 }
 EXPORT_SYMBOL(drm_crtc_check_viewport);

+/* tiled variants */
+static int drm_mode_setcrtc_tiled(struct drm_mode_set *orig_set)
+{
+       struct drm_device *dev = orig_set->crtc->dev;
+       struct drm_mode_set set[2];
+       struct drm_crtc *crtc2, *pick_crtc = NULL;
+       struct drm_connector *connector, *pick_conn[2];
+       struct drm_display_mode *cur_mode, *pick_modes[2];
+       int ret;
+
+       /* first up we need to find another crtc to use */
+       list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) {
+               if (crtc2 == orig_set->crtc)
+                       continue;
+               if (crtc2->enabled)
+                       continue;
+               pick_crtc = crtc2;
+               break;
+       }
+
+       if (pick_crtc == NULL) {
+               DRM_DEBUG_KMS("unable to located second CRTC for tiling\n");
+               return -EINVAL;
+       }
+
+       pick_conn[0] = orig_set->connectors[0];
+       pick_conn[1] = NULL;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (!connector->has_tile)
+                       continue;
+
+               if (connector == pick_conn[0])
+                       continue;
+
+               if (connector->tile_group_id != pick_conn[0]->tile_group_id)
+                       continue;
+
+               pick_conn[1] = connector;
+       }
+
+       DRM_DEBUG_KMS("picked connectors %x and %x from tgid %d\n", 
pick_conn[0]->base.id,
+                     pick_conn[1]->base.id, pick_conn[0]->tile_group_id);
+       if (pick_conn[1] == NULL) {
+               DRM_DEBUG_KMS("unable to located second connector for tiling 
%d\n", pick_conn[0]->tile_group_id);
+               return -EINVAL;
+       }
+
+       pick_modes[0] = pick_modes[1] = NULL;
+       list_for_each_entry(cur_mode, &pick_conn[0]->modes, head) {
+               DRM_DEBUG_KMS("trying %d %d\n", cur_mode->hdisplay, 
cur_mode->vdisplay);
+               if (cur_mode->hdisplay == pick_conn[1]->tile_h_size + 1 &&
+                   cur_mode->vdisplay == pick_conn[1]->tile_v_size + 1) {
+                       pick_modes[0] = pick_modes[1] = cur_mode;
+                       break;
+               }
+       }
+       if (pick_modes[0] == NULL) {
+               DRM_DEBUG_KMS("unable to locate second mode for tiling %d 
%d\n", pick_conn[1]->tile_h_size, pick_conn[1]->tile_v_size);
+               return -EINVAL;
+       }
+
+       set[0].fb = set[1].fb = orig_set->fb;
+
+       set[0].crtc = orig_set->crtc;
+       set[1].crtc = pick_crtc;
+
+       set[0].connectors = &pick_conn[0];
+       set[0].num_connectors = 1;
+
+       set[1].connectors = &pick_conn[1];
+       set[1].num_connectors = 1;
+
+       set[0].x = orig_set->x;
+       set[0].y = orig_set->y;
+       set[1].x = orig_set->x + ((pick_conn[1]->tile_h_loc == 1) ? 
pick_conn[0]->tile_h_size + 1 : 0);
+       set[1].y = orig_set->y + ((pick_conn[1]->tile_v_loc == 1) ? 
pick_conn[0]->tile_v_size + 1 : 0);
+
+       /* find a mode to use on each head */
+       set[0].mode = pick_modes[0];
+       set[1].mode = pick_modes[1];
+
+       ret = drm_mode_set_config_internal(&set[0]);
+
+       ret = drm_mode_set_config_internal(&set[1]);
+
+       return ret;
+}
 /**
  * drm_mode_setcrtc - set CRTC configuration
  * @dev: drm device for the ioctl
@@ -2532,6 +2619,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        uint32_t __user *set_connectors_ptr;
        int ret;
        int i;
+       int num_tiles = 1;

        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
@@ -2640,6 +2728,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
                                        connector->base.id,
                                        connector->name);

+                       if (crtc_req->count_connectors == 1) {
+                               if (connector->has_tile && 
connector->tile_is_single_monitor) {
+                                       if (mode->hdisplay > 
connector->tile_h_size || mode->vdisplay > connector->tile_v_size)
+                                               num_tiles = 2;
+                               }
+                       }
                        connector_set[i] = connector;
                }
        }
@@ -2651,7 +2745,11 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
        set.connectors = connector_set;
        set.num_connectors = crtc_req->count_connectors;
        set.fb = fb;
-       ret = drm_mode_set_config_internal(&set);
+
+       if (num_tiles > 1) {
+               ret = drm_mode_setcrtc_tiled(&set);
+       } else
+               ret = drm_mode_set_config_internal(&set);

 out:
        if (fb)
diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c
index d434da1..63d57a6 100644
--- a/drivers/gpu/drm/drm_displayid.c
+++ b/drivers/gpu/drm/drm_displayid.c
@@ -53,7 +53,18 @@ int drm_parse_display_id(struct drm_connector *connector,
                tile_v_loc = (tile->topo[1] & 0xf) | ((tile->topo[2] & 0x3) << 
4);
                tile_h_loc = (tile->topo[1] >> 4) | (((tile->topo[2] >> 2) & 
0x3) << 4);

-               printk("tile cap %d\n", tile->tile_cap);
+               connector->has_tile = true;
+               if (tile->tile_cap & 0x80)
+                       connector->tile_is_single_monitor = true;
+
+               connector->num_h_tile = num_h_tile;
+               connector->num_v_tile = num_v_tile;
+               connector->tile_h_loc = tile_h_loc;
+               connector->tile_v_loc = tile_v_loc;
+               connector->tile_h_size = w;
+               connector->tile_v_size = h;
+
+               printk("tile cap 0x%x\n", tile->tile_cap);
                printk("tile_size %d x %d\n", w, h);
                printk("topo num tiles %dx%d, location %dx%d\n",
                       num_h_tile, num_v_tile, tile_h_loc, tile_v_loc);
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c 
b/drivers/gpu/drm/drm_dp_mst_topology.c
index 5d2a08e..1f15d85 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -858,6 +858,8 @@ static void drm_dp_destroy_port(struct kref *kref)
        struct drm_dp_mst_topology_mgr *mgr = port->mgr;
        if (!port->input) {
                port->vcpi.num_slots = 0;
+
+               kfree(port->cached_edid);
                if (port->connector)
                        (*port->mgr->cbs->destroy_connector)(mgr, 
port->connector);
                drm_dp_port_teardown_pdt(port, port->pdt);
@@ -1100,8 +1102,16 @@ static void drm_dp_add_port(struct drm_dp_mst_branch 
*mstb,
                if (port->mstb) {
                        port->mstb->conn_base_id = port->connector->base.id;
                }
-               if (port->port_num >= 8)
+               if (port->port_num >= 8) {
                        port->cached_edid = drm_get_edid(port->connector, 
&port->aux.ddc);
+                       if (port->cached_edid) {
+                               drm_get_displayid(port->connector,
+                                                 &port->aux.ddc,
+                                                 port->cached_edid,
+                                                 false);
+                       }
+
+               }
        }

        /* put reference to this port */
@@ -2210,10 +2220,16 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector 
*connector, struct drm_dp_
        if (!port)
                return NULL;

+       if (connector->has_tile && connector->tile_is_single_monitor) {
+               if (connector->tile_h_loc > 0 || connector->tile_v_loc > 0) {
+                       goto out;
+               }
+       }
        if (port->cached_edid)
                edid = drm_edid_duplicate(port->cached_edid);
        else
                edid = drm_get_edid(connector, &port->aux.ddc);
+ out:
        drm_dp_put_port(port);
        return edid;
 }
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 3d805aa..94e8a57 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -3881,6 +3881,8 @@ void drm_get_displayid(struct drm_connector *connector,
                        bool secondary)
 {
        void *displayid = NULL;
+
+       connector->has_tile = false;
        displayid = drm_find_displayid_extension(edid);
        if (!displayid) {
                return;
diff --git a/drivers/gpu/drm/drm_probe_helper.c 
b/drivers/gpu/drm/drm_probe_helper.c
index db7d250..3fa902a 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -139,7 +139,7 @@ static int 
drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
                        count = (*connector_funcs->get_modes)(connector);
        }

-       if (count == 0 && connector->status == connector_status_connected)
+       if (count == 0 && connector->status == connector_status_connected && 
!connector->has_tile)
                count = drm_add_modes_noedid(connector, 1024, 768);
        if (count == 0)
                goto prune;
diff --git a/drivers/gpu/drm/i915/intel_modes.c 
b/drivers/gpu/drm/i915/intel_modes.c
index 35a327e..52948c6 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -42,6 +42,7 @@ int intel_connector_update_modes(struct drm_connector 
*connector,
        int ret;

        drm_mode_connector_update_edid_property(connector, edid);
+
        ret = drm_add_edid_modes(connector, edid);
        drm_edid_to_eld(connector, edid);

diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 1efc007..67c06bd 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -564,6 +564,14 @@ struct drm_connector {
        unsigned bad_edid_counter;

        struct dentry *debugfs_entry;
+
+       /* DisplayID bits */
+       bool has_tile;
+       bool tile_is_single_monitor;
+       uint32_t tile_group_id;
+       uint8_t num_h_tile, num_v_tile;
+       uint8_t tile_h_loc, tile_v_loc;
+       uint16_t tile_h_size, tile_v_size;
 };

 /**
-- 
1.9.3

Reply via email to