diff mbox

[06/11] drm/tile: attempt to set tiled crtcs up

Message ID 1410244096-9854-7-git-send-email-airlied@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dave Airlie Sept. 9, 2014, 6:28 a.m. UTC
From: Dave Airlie <airlied@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 mbox

Patch

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;
 };
 
 /**