Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1b8792b authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'drm-mst-next' of ssh://people.freedesktop.org/~/linux into drm-next

Merge mst tiling patches and MST fixes

* 'drm-mst-next' of ssh://people.freedesktop.org/~/linux:
  drm/fb: add support for tiled monitor configurations. (v2)
  drm/tile: expose the tile property to userspace (v3)
  drm/connector: store tile information from displayid (v3)
  drm/mst: cached EDID for logical ports (v2)
  drm: add tile_group support. (v3)
  drm/displayid: add displayid defines and edid extension (v2)
  drm/dp-mst: Remove branches before dropping the reference
  drm/fb_helper: move deferred fb checking into restore mode (v2)
  drm/dp: retry AUX transactions 32 times (v1.1)
parents b75478d1 b0ee9e7f
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -2412,6 +2412,10 @@ void intel_crt_init(struct drm_device *dev)
!Edrivers/gpu/drm/drm_plane_helper.c
!Pdrivers/gpu/drm/drm_plane_helper.c overview
    </sect2>
    <sect2>
	  <title>Tile group</title>
!Pdrivers/gpu/drm/drm_crtc.c Tile group
    </sect2>
  </sect1>

  <!-- Internals: kms properties -->
@@ -2547,7 +2551,7 @@ void intel_crt_init(struct drm_device *dev)
	</tr>
	<tr>
	<td rowspan="23" valign="top" >DRM</td>
	<td rowspan="3" valign="top" >Generic</td>
	<td rowspan="4" valign="top" >Generic</td>
	<td valign="top" >“EDID”</td>
	<td valign="top" >BLOB | IMMUTABLE</td>
	<td valign="top" >0</td>
@@ -2569,6 +2573,13 @@ void intel_crt_init(struct drm_device *dev)
	<td valign="top" >Contains topology path to a connector.</td>
	</tr>
	<tr>
	<td valign="top" >“TILE”</td>
	<td valign="top" >BLOB | IMMUTABLE</td>
	<td valign="top" >0</td>
	<td valign="top" >Connector</td>
	<td valign="top" >Contains tiling information for a connector.</td>
	</tr>
	<tr>
	<td rowspan="1" valign="top" >Plane</td>
	<td valign="top" >“type”</td>
	<td valign="top" >ENUM | IMMUTABLE</td>
+155 −0
Original line number Diff line number Diff line
@@ -908,6 +908,11 @@ void drm_connector_cleanup(struct drm_connector *connector)
	struct drm_device *dev = connector->dev;
	struct drm_display_mode *mode, *t;

	if (connector->tile_group) {
		drm_mode_put_tile_group(dev, connector->tile_group);
		connector->tile_group = NULL;
	}

	list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
		drm_mode_remove(connector, mode);

@@ -1339,6 +1344,11 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
				       "PATH", 0);
	dev->mode_config.path_property = dev_path;

	dev->mode_config.tile_property = drm_property_create(dev,
							     DRM_MODE_PROP_BLOB |
							     DRM_MODE_PROP_IMMUTABLE,
							     "TILE", 0);

	return 0;
}

@@ -4082,6 +4092,52 @@ int drm_mode_connector_set_path_property(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_mode_connector_set_path_property);

/**
 * drm_mode_connector_set_tile_property - set tile property on connector
 * @connector: connector to set property on.
 *
 * This looks up the tile information for a connector, and creates a
 * property for userspace to parse if it exists. The property is of
 * the form of 8 integers using ':' as a separator.
 *
 * Returns:
 * Zero on success, errno on failure.
 */
int drm_mode_connector_set_tile_property(struct drm_connector *connector)
{
	struct drm_device *dev = connector->dev;
	int ret, size;
	char tile[256];

	if (connector->tile_blob_ptr)
		drm_property_destroy_blob(dev, connector->tile_blob_ptr);

	if (!connector->has_tile) {
		connector->tile_blob_ptr = NULL;
		ret = drm_object_property_set_value(&connector->base,
						    dev->mode_config.tile_property, 0);
		return ret;
	}

	snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d",
		 connector->tile_group->id, connector->tile_is_single_monitor,
		 connector->num_h_tile, connector->num_v_tile,
		 connector->tile_h_loc, connector->tile_v_loc,
		 connector->tile_h_size, connector->tile_v_size);
	size = strlen(tile) + 1;

	connector->tile_blob_ptr = drm_property_create_blob(connector->dev,
							    size, tile);
	if (!connector->tile_blob_ptr)
		return -EINVAL;

	ret = drm_object_property_set_value(&connector->base,
					    dev->mode_config.tile_property,
					    connector->tile_blob_ptr->base.id);
	return ret;
}
EXPORT_SYMBOL(drm_mode_connector_set_tile_property);

/**
 * drm_mode_connector_update_edid_property - update the edid property of a connector
 * @connector: drm connector
@@ -5152,6 +5208,7 @@ void drm_mode_config_init(struct drm_device *dev)
	INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
	INIT_LIST_HEAD(&dev->mode_config.plane_list);
	idr_init(&dev->mode_config.crtc_idr);
	idr_init(&dev->mode_config.tile_idr);

	drm_modeset_lock_all(dev);
	drm_mode_create_standard_connector_properties(dev);
@@ -5239,6 +5296,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
		crtc->funcs->destroy(crtc);
	}

	idr_destroy(&dev->mode_config.tile_idr);
	idr_destroy(&dev->mode_config.crtc_idr);
	drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
}
@@ -5261,3 +5319,100 @@ struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
					   supported_rotations);
}
EXPORT_SYMBOL(drm_mode_create_rotation_property);

/**
 * DOC: Tile group
 *
 * Tile groups are used to represent tiled monitors with a unique
 * integer identifier. Tiled monitors using DisplayID v1.3 have
 * a unique 8-byte handle, we store this in a tile group, so we
 * have a common identifier for all tiles in a monitor group.
 */
static void drm_tile_group_free(struct kref *kref)
{
	struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount);
	struct drm_device *dev = tg->dev;
	mutex_lock(&dev->mode_config.idr_mutex);
	idr_remove(&dev->mode_config.tile_idr, tg->id);
	mutex_unlock(&dev->mode_config.idr_mutex);
	kfree(tg);
}

/**
 * drm_mode_put_tile_group - drop a reference to a tile group.
 * @dev: DRM device
 * @tg: tile group to drop reference to.
 *
 * drop reference to tile group and free if 0.
 */
void drm_mode_put_tile_group(struct drm_device *dev,
			     struct drm_tile_group *tg)
{
	kref_put(&tg->refcount, drm_tile_group_free);
}

/**
 * drm_mode_get_tile_group - get a reference to an existing tile group
 * @dev: DRM device
 * @topology: 8-bytes unique per monitor.
 *
 * Use the unique bytes to get a reference to an existing tile group.
 *
 * RETURNS:
 * tile group or NULL if not found.
 */
struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
					       char topology[8])
{
	struct drm_tile_group *tg;
	int id;
	mutex_lock(&dev->mode_config.idr_mutex);
	idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) {
		if (!memcmp(tg->group_data, topology, 8)) {
			if (!kref_get_unless_zero(&tg->refcount))
				tg = NULL;
			mutex_unlock(&dev->mode_config.idr_mutex);
			return tg;
		}
	}
	mutex_unlock(&dev->mode_config.idr_mutex);
	return NULL;
}

/**
 * drm_mode_create_tile_group - create a tile group from a displayid description
 * @dev: DRM device
 * @topology: 8-bytes unique per monitor.
 *
 * Create a tile group for the unique monitor, and get a unique
 * identifier for the tile group.
 *
 * RETURNS:
 * new tile group or error.
 */
struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
						  char topology[8])
{
	struct drm_tile_group *tg;
	int ret;

	tg = kzalloc(sizeof(*tg), GFP_KERNEL);
	if (!tg)
		return ERR_PTR(-ENOMEM);

	kref_init(&tg->refcount);
	memcpy(tg->group_data, topology, 8);
	tg->dev = dev;

	mutex_lock(&dev->mode_config.idr_mutex);
	ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL);
	if (ret >= 0) {
		tg->id = ret;
	} else {
		kfree(tg);
		tg = ERR_PTR(ret);
	}

	mutex_unlock(&dev->mode_config.idr_mutex);
	return tg;
}
+4 −3
Original line number Diff line number Diff line
@@ -186,10 +186,11 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,

	/*
	 * The specification doesn't give any recommendation on how often to
	 * retry native transactions, so retry 7 times like for I2C-over-AUX
	 * transactions.
	 * retry native transactions. We used to retry 7 times like for
	 * aux i2c transactions but real world devices this wasn't
	 * sufficient, bump to 32 which makes Dell 4k monitors happier.
	 */
	for (retry = 0; retry < 7; retry++) {
	for (retry = 0; retry < 32; retry++) {

		mutex_lock(&aux->hw_mutex);
		err = aux->transfer(aux, &msg);
+22 −3
Original line number Diff line number Diff line
@@ -839,6 +839,8 @@ static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)

static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
{
	struct drm_dp_mst_branch *mstb;

	switch (old_pdt) {
	case DP_PEER_DEVICE_DP_LEGACY_CONV:
	case DP_PEER_DEVICE_SST_SINK:
@@ -846,8 +848,9 @@ static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
		drm_dp_mst_unregister_i2c_bus(&port->aux);
		break;
	case DP_PEER_DEVICE_MST_BRANCHING:
		drm_dp_put_mst_branch_device(port->mstb);
		mstb = port->mstb;
		port->mstb = NULL;
		drm_dp_put_mst_branch_device(mstb);
		break;
	}
}
@@ -858,6 +861,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);
@@ -1097,6 +1102,10 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
		char proppath[255];
		build_mst_prop_path(port, mstb, proppath, sizeof(proppath));
		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath);

		if (port->port_num >= 8) {
			port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
		}
	}

	/* put reference to this port */
@@ -2167,7 +2176,8 @@ EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
 * This returns the current connection state for a port. It validates the
 * port pointer still exists so the caller doesn't require a reference
 */
enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector,
						 struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
{
	enum drm_connector_status status = connector_status_disconnected;

@@ -2186,6 +2196,10 @@ enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr

	case DP_PEER_DEVICE_SST_SINK:
		status = connector_status_connected;
		/* for logical ports - cache the EDID */
		if (port->port_num >= 8 && !port->cached_edid) {
			port->cached_edid = drm_get_edid(connector, &port->aux.ddc);
		}
		break;
	case DP_PEER_DEVICE_DP_LEGACY_CONV:
		if (port->ldps)
@@ -2217,7 +2231,12 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
	if (!port)
		return NULL;

	if (port->cached_edid)
		edid = drm_edid_duplicate(port->cached_edid);
	else
		edid = drm_get_edid(connector, &port->aux.ddc);

	drm_mode_connector_set_tile_property(connector);
	drm_dp_put_port(port);
	return edid;
}
+141 −3
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_edid.h>
#include <drm/drm_displayid.h>

#define version_greater(edid, maj, min) \
	(((edid)->version > (maj)) || \
@@ -1014,6 +1015,8 @@ module_param_named(edid_fixup, edid_fixup, int, 0400);
MODULE_PARM_DESC(edid_fixup,
		 "Minimum number of valid EDID header bytes (0-8, default 6)");

static void drm_get_displayid(struct drm_connector *connector,
			      struct edid *edid);
/**
 * drm_edid_block_valid - Sanity check the EDID block (base or extension)
 * @raw_edid: pointer to raw EDID block
@@ -1308,10 +1311,15 @@ EXPORT_SYMBOL(drm_probe_ddc);
struct edid *drm_get_edid(struct drm_connector *connector,
			  struct i2c_adapter *adapter)
{
	struct edid *edid;

	if (!drm_probe_ddc(adapter))
		return NULL;

	return drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
	edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter);
	if (edid)
		drm_get_displayid(connector, edid);
	return edid;
}
EXPORT_SYMBOL(drm_get_edid);

@@ -2406,7 +2414,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
/*
 * Search EDID for CEA extension block.
 */
static u8 *drm_find_cea_extension(struct edid *edid)
static u8 *drm_find_edid_extension(struct edid *edid, int ext_id)
{
	u8 *edid_ext = NULL;
	int i;
@@ -2418,7 +2426,7 @@ static u8 *drm_find_cea_extension(struct edid *edid)
	/* Find CEA extension */
	for (i = 0; i < edid->extensions; i++) {
		edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
		if (edid_ext[0] == CEA_EXT)
		if (edid_ext[0] == ext_id)
			break;
	}

@@ -2428,6 +2436,16 @@ static u8 *drm_find_cea_extension(struct edid *edid)
	return edid_ext;
}

static u8 *drm_find_cea_extension(struct edid *edid)
{
	return drm_find_edid_extension(edid, CEA_EXT);
}

static u8 *drm_find_displayid_extension(struct edid *edid)
{
	return drm_find_edid_extension(edid, DISPLAYID_EXT);
}

/*
 * Calculate the alternate clock for the CEA mode
 * (60Hz vs. 59.94Hz etc.)
@@ -3888,3 +3906,123 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
	return 0;
}
EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode);

static int drm_parse_display_id(struct drm_connector *connector,
				u8 *displayid, int length,
				bool is_edid_extension)
{
	/* if this is an EDID extension the first byte will be 0x70 */
	int idx = 0;
	struct displayid_hdr *base;
	struct displayid_block *block;
	u8 csum = 0;
	int i;

	if (is_edid_extension)
		idx = 1;

	base = (struct displayid_hdr *)&displayid[idx];

	DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
		      base->rev, base->bytes, base->prod_id, base->ext_count);

	if (base->bytes + 5 > length - idx)
		return -EINVAL;

	for (i = idx; i <= base->bytes + 5; i++) {
		csum += displayid[i];
	}
	if (csum) {
		DRM_ERROR("DisplayID checksum invalid, remainder is %d\n", csum);
		return -EINVAL;
	}

	block = (struct displayid_block *)&displayid[idx + 4];
	DRM_DEBUG_KMS("block id %d, rev %d, len %d\n",
		      block->tag, block->rev, block->num_bytes);

	switch (block->tag) {
	case DATA_BLOCK_TILED_DISPLAY: {
		struct displayid_tiled_block *tile = (struct displayid_tiled_block *)block;

		u16 w, h;
		u8 tile_v_loc, tile_h_loc;
		u8 num_v_tile, num_h_tile;
		struct drm_tile_group *tg;

		w = tile->tile_size[0] | tile->tile_size[1] << 8;
		h = tile->tile_size[2] | tile->tile_size[3] << 8;

		num_v_tile = (tile->topo[0] & 0xf) | (tile->topo[2] & 0x30);
		num_h_tile = (tile->topo[0] >> 4) | ((tile->topo[2] >> 2) & 0x30);
		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);

		connector->has_tile = true;
		if (tile->tile_cap & 0x80)
			connector->tile_is_single_monitor = true;

		connector->num_h_tile = num_h_tile + 1;
		connector->num_v_tile = num_v_tile + 1;
		connector->tile_h_loc = tile_h_loc;
		connector->tile_v_loc = tile_v_loc;
		connector->tile_h_size = w + 1;
		connector->tile_v_size = h + 1;

		DRM_DEBUG_KMS("tile cap 0x%x\n", tile->tile_cap);
		DRM_DEBUG_KMS("tile_size %d x %d\n", w + 1, h + 1);
		DRM_DEBUG_KMS("topo num tiles %dx%d, location %dx%d\n",
		       num_h_tile + 1, num_v_tile + 1, tile_h_loc, tile_v_loc);
		DRM_DEBUG_KMS("vend %c%c%c\n", tile->topology_id[0], tile->topology_id[1], tile->topology_id[2]);

		tg = drm_mode_get_tile_group(connector->dev, tile->topology_id);
		if (!tg) {
			tg = drm_mode_create_tile_group(connector->dev, tile->topology_id);
		}
		if (!tg)
			return -ENOMEM;

		if (connector->tile_group != tg) {
			/* if we haven't got a pointer,
			   take the reference, drop ref to old tile group */
			if (connector->tile_group) {
				drm_mode_put_tile_group(connector->dev, connector->tile_group);
			}
			connector->tile_group = tg;
		} else
			/* if same tile group, then release the ref we just took. */
			drm_mode_put_tile_group(connector->dev, tg);
	}
		break;
	default:
		printk("unknown displayid tag %d\n", block->tag);
		break;
	}
	return 0;
}

static void drm_get_displayid(struct drm_connector *connector,
			      struct edid *edid)
{
	void *displayid = NULL;
	int ret;
	connector->has_tile = false;
	displayid = drm_find_displayid_extension(edid);
	if (!displayid) {
		/* drop reference to any tile group we had */
		goto out_drop_ref;
	}

	ret = drm_parse_display_id(connector, displayid, EDID_LENGTH, true);
	if (ret < 0)
		goto out_drop_ref;
	if (!connector->has_tile)
		goto out_drop_ref;
	return;
out_drop_ref:
	if (connector->tile_group) {
		drm_mode_put_tile_group(connector->dev, connector->tile_group);
		connector->tile_group = NULL;
	}
	return;
}
Loading