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