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

Commit 84fc2163 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "disp: msm: dp: Improvements to dp mst simulator mode"

parents e5aaaf87 15bbd57e
Loading
Loading
Loading
Loading
+112 −0
Original line number Diff line number Diff line
@@ -511,6 +511,90 @@ static ssize_t dp_debug_write_mst_con_id(struct file *file,
	return len;
}

static ssize_t dp_debug_write_mst_con_add(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
	struct dp_debug_private *debug = file->private_data;
	char buf[SZ_32];
	size_t len = 0;
	const int dp_en = BIT(3), hpd_high = BIT(7), hpd_irq = BIT(8);
	int vdo = dp_en | hpd_high | hpd_irq;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	/* Leave room for termination char */
	len = min_t(size_t, count, SZ_32 - 1);
	if (copy_from_user(buf, user_buff, len))
		goto end;

	debug->dp_debug.mst_hpd_sim = true;
	debug->dp_debug.mst_sim_add_con = true;
	debug->hpd->simulate_attention(debug->hpd, vdo);
end:
	return len;
}

static ssize_t dp_debug_write_mst_con_remove(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
	struct dp_debug_private *debug = file->private_data;
	struct dp_mst_connector *mst_connector;
	char buf[SZ_32];
	size_t len = 0;
	int con_id = 0;
	bool in_list = false;
	const int dp_en = BIT(3), hpd_high = BIT(7), hpd_irq = BIT(8);
	int vdo = dp_en | hpd_high | hpd_irq;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	/* Leave room for termination char */
	len = min_t(size_t, count, SZ_32 - 1);
	if (copy_from_user(buf, user_buff, len))
		goto end;

	buf[len] = '\0';

	if (sscanf(buf, "%d", &con_id) != 1) {
		len = 0;
		goto end;
	}

	if (!con_id)
		goto end;

	/* Verify that the connector id is for a valid mst connector. */
	mutex_lock(&debug->dp_debug.dp_mst_connector_list.lock);
	list_for_each_entry(mst_connector,
			&debug->dp_debug.dp_mst_connector_list.list, list) {
		if (mst_connector->con_id == con_id) {
			in_list = true;
			break;
		}
	}
	mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock);

	if (!in_list) {
		DRM_ERROR("invalid connector id %u\n", con_id);
		goto end;
	}

	debug->dp_debug.mst_hpd_sim = true;
	debug->dp_debug.mst_sim_remove_con = true;
	debug->dp_debug.mst_sim_remove_con_id = con_id;
	debug->hpd->simulate_attention(debug->hpd, vdo);
end:
	return len;
}

static ssize_t dp_debug_bw_code_write(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -1699,6 +1783,16 @@ static const struct file_operations mst_con_id_fops = {
	.write = dp_debug_write_mst_con_id,
};

static const struct file_operations mst_con_add_fops = {
	.open = simple_open,
	.write = dp_debug_write_mst_con_add,
};

static const struct file_operations mst_con_remove_fops = {
	.open = simple_open,
	.write = dp_debug_write_mst_con_remove,
};

static const struct file_operations hpd_fops = {
	.open = simple_open,
	.write = dp_debug_write_hpd,
@@ -1855,6 +1949,24 @@ static int dp_debug_init(struct dp_debug *dp_debug)
		goto error_remove_dir;
	}

	file = debugfs_create_file("mst_con_add", 0644, dir,
					debug, &mst_con_add_fops);
	if (IS_ERR_OR_NULL(file)) {
		rc = PTR_ERR(file);
		DRM_ERROR("[%s] debugfs create mst_con_add failed, rc=%d\n",
		       DEBUG_NAME, rc);
		goto error_remove_dir;
	}

	file = debugfs_create_file("mst_con_remove", 0644, dir,
					debug, &mst_con_remove_fops);
	if (IS_ERR_OR_NULL(file)) {
		rc = PTR_ERR(file);
		DRM_ERROR("[%s] debugfs create mst_con_remove failed, rc=%d\n",
		       DEBUG_NAME, rc);
		goto error_remove_dir;
	}

	file = debugfs_create_file("hpd", 0644, dir,
					debug, &hpd_fops);
	if (IS_ERR_OR_NULL(file)) {
+14 −0
Original line number Diff line number Diff line
@@ -16,13 +16,24 @@
/**
 * struct dp_debug
 * @debug_en: specifies whether debug mode enabled
 * @sim_mode: specifies whether sim mode enabled
 * @psm_enabled: specifies whether psm enabled
 * @hdcp_disabled: specifies if hdcp is disabled
 * @hdcp_wait_sink_sync: used to wait for sink synchronization before HDCP auth
 * @aspect_ratio: used to filter out aspect_ratio value
 * @vdisplay: used to filter out vdisplay value
 * @hdisplay: used to filter out hdisplay value
 * @vrefresh: used to filter out vrefresh value
 * @tpg_state: specifies whether tpg feature is enabled
 * @max_pclk_khz: max pclk supported
 * @force_encryption: enable/disable forced encryption for HDCP 2.2
 * @hdcp_status: string holding hdcp status information
 * @dp_mst_connector_list: list containing all dp mst connectors
 * @mst_hpd_sim: specifies whether simulated hpd enabled
 * @mst_sim_add_con: specifies whether new sim connector is to be added
 * @mst_sim_remove_con: specifies whether sim connector is to be removed
 * @mst_sim_remove_con_id: specifies id of sim connector to be removed
 * @mst_port_cnt: number of mst ports to be added during hpd
 */
struct dp_debug {
	bool debug_en;
@@ -40,6 +51,9 @@ struct dp_debug {
	char hdcp_status[SZ_128];
	struct dp_mst_connector dp_mst_connector_list;
	bool mst_hpd_sim;
	bool mst_sim_add_con;
	bool mst_sim_remove_con;
	int mst_sim_remove_con_id;
	u32 mst_port_cnt;

	u8 *(*get_edid)(struct dp_debug *dp_debug);
+6 −0
Original line number Diff line number Diff line
@@ -1026,8 +1026,14 @@ static void dp_display_mst_attention(struct dp_display_private *dp)

	if (dp->mst.mst_active && dp->mst.cbs.hpd_irq) {
		hpd_irq.mst_hpd_sim = dp->debug->mst_hpd_sim;
		hpd_irq.mst_sim_add_con = dp->debug->mst_sim_add_con;
		hpd_irq.mst_sim_remove_con = dp->debug->mst_sim_remove_con;
		hpd_irq.mst_sim_remove_con_id = dp->debug->mst_sim_remove_con_id;
		hpd_irq.edid = dp->debug->get_edid(dp->debug);
		dp->mst.cbs.hpd_irq(&dp->dp_display, &hpd_irq);
		dp->debug->mst_hpd_sim = false;
		dp->debug->mst_sim_add_con = false;
		dp->debug->mst_sim_remove_con = false;
	}

	DP_MST_DEBUG("mst_attention_work. mst_active:%d\n", dp->mst.mst_active);
+4 −1
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@

#include "dp_panel.h"

#define DP_MST_SIM_MAX_PORTS	2
#define DP_MST_SIM_MAX_PORTS	8

enum dp_drv_state {
	PM_DEFAULT,
@@ -24,6 +24,9 @@ struct dp_mst_hpd_info {
	bool mst_hpd_sim;
	u32 mst_port_cnt;
	u8 *edid;
	bool mst_sim_add_con;
	bool mst_sim_remove_con;
	int mst_sim_remove_con_id;
};

struct dp_mst_drm_cbs {
+139 −3
Original line number Diff line number Diff line
@@ -94,9 +94,16 @@ struct dp_mst_sim_port_data {
	u8 num_sdp_stream_sinks;
};

struct dp_mst_sim_port_edid {
	u8 port_number;
	u8 edid[SZ_256];
	bool valid;
};

struct dp_mst_sim_mode {
	bool mst_state;
	struct edid *edid;
	struct dp_mst_sim_port_edid port_edids[DP_MST_SIM_MAX_PORTS];
	struct work_struct probe_work;
	const struct drm_dp_mst_topology_cbs *cbs;
	u32 port_cnt;
@@ -156,8 +163,22 @@ static void dp_mst_sim_destroy_port(struct kref *ref)
{
	struct drm_dp_mst_port *port = container_of(ref,
			struct drm_dp_mst_port, kref);
	struct drm_dp_mst_topology_mgr *mgr = port->mgr;

	if (port->cached_edid)
		kfree(port->cached_edid);

	if (port->connector) {
		mutex_lock(&mgr->destroy_connector_lock);
		kref_get(&port->parent->kref);
		list_add(&port->next, &mgr->destroy_connector_list);
		mutex_unlock(&mgr->destroy_connector_lock);
		schedule_work(&mgr->destroy_connector_work);
		return;
	} else {
		kfree(port);
	}
}

/* DRM DP MST Framework simulator OPs */
static void dp_mst_sim_add_port(struct dp_mst_private *mst,
@@ -218,7 +239,7 @@ static void dp_mst_sim_link_probe_work(struct work_struct *work)
	struct dp_mst_sim_mode *sim;
	struct dp_mst_private *mst;
	struct dp_mst_sim_port_data port_data;
	u8 cnt;
	u8 cnt, i;

	DP_MST_DEBUG("enter\n");
	sim = container_of(work, struct dp_mst_sim_mode, probe_work);
@@ -233,8 +254,21 @@ static void dp_mst_sim_link_probe_work(struct work_struct *work)
	port_data.num_sdp_streams = 0;
	port_data.num_sdp_stream_sinks = 0;

	for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++)
		sim->port_edids[i].valid = false;

	for (cnt = 0; cnt < sim->port_cnt; cnt++) {
		port_data.port_number = cnt;

		for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
			if (sim->port_edids[i].valid) continue;

			sim->port_edids[i].port_number = port_data.port_number;
			memcpy(sim->port_edids[i].edid, sim->edid, SZ_256);
			sim->port_edids[i].valid = true;
			break;
		}

		dp_mst_sim_add_port(mst, &port_data);
	}

@@ -351,8 +385,19 @@ static struct edid *dp_mst_sim_get_edid(struct drm_connector *connector,
{
	struct dp_mst_private *mst = container_of(mgr,
			struct dp_mst_private, mst_mgr);
	int i;

	return drm_edid_duplicate(mst->simulator.edid);
	for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
		if (mst->simulator.port_edids[i].valid &&
				mst->simulator.port_edids[i].port_number ==
				port->port_num) {
			return drm_edid_duplicate((struct edid *)
					(mst->simulator.port_edids[i].edid));
		}
	}

	DRM_ERROR("edid not found for connector %d\n", connector->base.id);
	return NULL;
}

static int dp_mst_sim_topology_mgr_set_mst(
@@ -370,6 +415,83 @@ static int dp_mst_sim_topology_mgr_set_mst(
	return 0;
}

static void dp_mst_sim_handle_hpd_irq(void *dp_display,
		struct dp_mst_hpd_info *info)
{
	struct dp_display *dp;
	struct dp_mst_private *mst;
	struct drm_dp_mst_port *port;
	struct dp_mst_sim_port_data port_data;
	struct drm_dp_mst_branch *mstb;
	int i;
	bool in_list, port_available;

	dp = dp_display;
	mst = dp->dp_mst_prv_info;

	if (info->mst_sim_add_con) {
		port_available = false;
		for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
			if (mst->simulator.port_edids[i].valid) continue;

			port_data.port_number = i;
			mst->simulator.port_edids[i].port_number = i;
			memcpy(mst->simulator.port_edids[i].edid, info->edid,
					SZ_256);
			mst->simulator.port_edids[i].valid = true;
			port_available = true;
			break;
		}

		if (!port_available) {
			DRM_ERROR("add port failed, limit (%d) reached\n",
					DP_MST_SIM_MAX_PORTS);
			return;
		}

		port_data.input_port = false;
		port_data.peer_device_type = DP_PEER_DEVICE_SST_SINK;
		port_data.mcs = false;
		port_data.ddps = true;
		port_data.legacy_device_plug_status = false;
		port_data.dpcd_revision = 0;
		port_data.num_sdp_streams = 0;
		port_data.num_sdp_stream_sinks = 0;

		dp_mst_sim_add_port(mst, &port_data);
	} else if (info->mst_sim_remove_con) {
		mstb = mst->mst_mgr.mst_primary;
		in_list = false;

		mutex_lock(&mst->mst_mgr.lock);
		list_for_each_entry(port,
				&mstb->ports, next) {
			if (port->connector && port->connector->base.id ==
					info->mst_sim_remove_con_id) {
				in_list = true;
				list_del(&port->next);
				break;
			}
		}
		mutex_unlock(&mst->mst_mgr.lock);

		if (!in_list) {
			DRM_ERROR("invalid connector id %d\n",
					info->mst_sim_remove_con_id);
			return;
		}

		for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
			if (mst->simulator.port_edids[i].port_number ==
					port->port_num) {
				mst->simulator.port_edids[i].valid = false;
			}
		}

		kref_put(&port->kref, dp_mst_sim_destroy_port);
	}
}

static void _dp_mst_get_vcpi_info(
		struct drm_dp_mst_topology_mgr *mgr,
		int vcpi, int *start_slot, int *num_slots)
@@ -1857,6 +1979,20 @@ static void dp_mst_display_hpd_irq(void *dp_display,
	bool handled;

	if (info->mst_hpd_sim) {
		if (info->mst_sim_add_con || info->mst_sim_remove_con) {
			dp_mst_sim_handle_hpd_irq(dp_display, info);

			/*
			 * When removing a connector, hpd_irq -> sim_destroy ->
			 * destroy_connector_work will be executed in a thread.
			 * This thread will perform the dp_mst_hotplug at the
			 * appropriate time. Do not perform hotplug here
			 * because it may be too early.
			 */
			if (info->mst_sim_remove_con)
				return;
		}

		dp_mst_hotplug(&mst->mst_mgr);
		return;
	}