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

Commit 9299070a authored by Ajay Singh Parmar's avatar Ajay Singh Parmar
Browse files

drm/msm/dp: listen to USB events to terminate simulation



In case USB cable is removed while DP simulation is running,
as USB may re-program PHY, DP hardware may go into bad state.
Listen to USB cable notifications with high priority. If
simulation is running and cable is removed, tear down the
DP session before USB re-programs the PHY.

CRs-Fixed: 2338665
Change-Id: I55955583dd44cb42c778c2ab32a190ddd9a7e72e
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent 8a12c558
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -679,10 +679,8 @@ static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
	reinit_completion(&audio->hpd_comp);
	rc = ext->intf_ops.audio_notify(audio->ext_pdev,
			&ext->codec, state);
	if (rc) {
		pr_err("failed to notify audio. state=%d err=%d\n", state, rc);
	if (rc)
		goto end;
	}

	if (atomic_read(&audio->acked))
		goto end;
+70 −31
Original line number Diff line number Diff line
@@ -163,6 +163,13 @@ static ssize_t dp_debug_write_edid(struct file *file,
	kfree(buf);
	debug->panel->set_edid(debug->panel, edid);

	/*
	 * print edid status as this code is executed
	 * only while running in debug mode which is manually
	 * triggered by a tester or a script.
	 */
	pr_info("[%s]\n", edid ? "SET" : "CLEAR");

	return rc;
}

@@ -250,10 +257,18 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
	 * Reset panel's dpcd in case of any failure. Also, set the
	 * panel's dpcd only if a full dpcd is provided with offset as 0.
	 */
	if (!dpcd || (!offset && (data_len == dp_receiver_cap_size)))
	if (!dpcd || (!offset && (data_len == dp_receiver_cap_size))) {
		debug->panel->set_dpcd(debug->panel, dpcd);
	else

		/*
		 * print dpcd status as this code is executed
		 * only while running in debug mode which is manually
		 * triggered by a tester or a script.
		 */
		pr_info("[%s]\n", dpcd ? "SET" : "CLEAR");
	} else {
		debug->aux->dpcd_updated(debug->aux);
	}

	return rc;
}
@@ -1392,36 +1407,16 @@ static ssize_t dp_debug_read_hdr(struct file *file,
	return rc;
}

static ssize_t dp_debug_write_sim(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
{
	struct dp_debug_private *debug = file->private_data;
	char buf[SZ_8];
	size_t len = 0;
	int sim;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

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

	buf[len] = '\0';

	if (kstrtoint(buf, 10, &sim) != 0)
		goto end;

	if (sim) {
		if (dp_debug_get_edid_buf(debug))
			goto end;
			return;

		if (dp_debug_get_dpcd_buf(debug))
			goto error;
		if (dp_debug_get_dpcd_buf(debug)) {
			devm_kfree(debug->dev, debug->edid);
			return;
		}

		debug->dp_debug.sim_mode = true;
		debug->aux->set_sim_mode(debug->aux, true,
@@ -1442,11 +1437,42 @@ static ssize_t dp_debug_write_sim(struct file *file,
			debug->dpcd = NULL;
		}
	}

	/*
	 * print simulation status as this code is executed
	 * only while running in debug mode which is manually
	 * triggered by a tester or a script.
	 */
	pr_info("%s\n", sim ? "[ON]" : "[OFF]");
}

static ssize_t dp_debug_write_sim(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_8];
	size_t len = 0;
	int sim;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

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

	buf[len] = '\0';

	if (kstrtoint(buf, 10, &sim) != 0)
		goto end;

	dp_debug_set_sim_mode(debug, sim);
end:
	return len;
error:
	devm_kfree(debug->dev, debug->edid);
	return len;
}

static ssize_t dp_debug_write_attention(struct file *file,
@@ -1898,6 +1924,18 @@ u8 *dp_debug_get_edid(struct dp_debug *dp_debug)
	return debug->edid;
}

static void dp_debug_abort(struct dp_debug *dp_debug)
{
	struct dp_debug_private *debug;

	if (!dp_debug)
		return;

	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);

	dp_debug_set_sim_mode(debug, false);
}

struct dp_debug *dp_debug_get(struct dp_debug_in *in)
{
	int rc = 0;
@@ -1938,6 +1976,7 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
	}

	dp_debug->get_edid = dp_debug_get_edid;
	dp_debug->abort = dp_debug_abort;

	INIT_LIST_HEAD(&dp_debug->dp_mst_connector_list.list);

+1 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ struct dp_debug {
	struct dp_mst_connector dp_mst_connector_list;

	u8 *(*get_edid)(struct dp_debug *dp_debug);
	void (*abort)(struct dp_debug *dp_debug);
};

/**
+68 −36
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/debugfs.h>
#include <linux/component.h>
#include <linux/of_irq.h>
#include <linux/extcon.h>
#include <linux/soc/qcom/fsa4480-i2c.h>

#include "sde_connector.h"
@@ -107,6 +108,8 @@ struct dp_display_private {

	u32 active_stream_cnt;
	struct dp_mst mst;

	struct notifier_block usb_nb;
};

static const struct of_device_id dp_dt_match[] = {
@@ -556,6 +559,9 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp)
	if (hpd && dp->mst.mst_active)
		goto skip_wait;

	if (!dp->mst.mst_active && (dp->power_on == hpd))
		goto skip_wait;

	if (!wait_for_completion_timeout(&dp->notification_comp,
						HZ * timeout_sec)) {
		pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
@@ -816,6 +822,24 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp)
	return rc;
}

static void dp_display_disconnect_sync(struct dp_display_private *dp)
{
	/* cancel any pending request */
	atomic_set(&dp->aborted, 1);
	dp->ctrl->abort(dp->ctrl);
	dp->aux->abort(dp->aux);

	/* wait for idle state */
	cancel_delayed_work(&dp->connect_work);
	cancel_work(&dp->attention_work);
	flush_workqueue(dp->wq);

	dp_display_handle_disconnect(dp);

	/* Reset abort value to allow future connections */
	atomic_set(&dp->aborted, 0);
}

static int dp_display_usbpd_disconnect_cb(struct device *dev)
{
	int rc = 0;
@@ -844,21 +868,7 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
	if (dp->debug->psm_enabled)
		dp->link->psm_config(dp->link, &dp->panel->link_info, true);

	/* cancel any pending request */
	atomic_set(&dp->aborted, 1);
	dp->ctrl->abort(dp->ctrl);
	dp->aux->abort(dp->aux);

	/* wait for idle state */
	cancel_delayed_work(&dp->connect_work);
	cancel_work(&dp->attention_work);
	flush_workqueue(dp->wq);

	dp_display_handle_disconnect(dp);

	/* Reset abort value to allow future connections */
	atomic_set(&dp->aborted, 0);

	dp_display_disconnect_sync(dp);
	dp->dp_display.post_open = NULL;
end:
	return rc;
@@ -976,29 +986,14 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
			dp->hpd->hpd_irq, dp->hpd->hpd_high,
			dp->power_on);

	if (!dp->hpd->hpd_high) {
		if (!dp->is_connected) {
			pr_debug("already disconnected\n");
			return 0;
		}

		/* cancel any pending request */
		atomic_set(&dp->aborted, 1);
		dp->ctrl->abort(dp->ctrl);
		dp->aux->abort(dp->aux);

		/* wait for idle state */
		cancel_delayed_work(&dp->connect_work);
		cancel_work(&dp->attention_work);
		flush_workqueue(dp->wq);

		dp_display_handle_disconnect(dp);
		atomic_set(&dp->aborted, 0);
	} else if (dp->hpd->hpd_irq && dp->core_initialized) {
	if (!dp->hpd->hpd_high)
		dp_display_disconnect_sync(dp);
	else if (dp->hpd->hpd_irq && dp->core_initialized)
		queue_work(dp->wq, &dp->attention_work);
	} else {
	else if (!dp->power_on)
		queue_delayed_work(dp->wq, &dp->connect_work, 0);
	}
	else
		pr_debug("ignored\n");

	return 0;
}
@@ -1026,6 +1021,41 @@ static void dp_display_connect_work(struct work_struct *work)
		dp->link->send_test_response(dp->link);
}

static int dp_display_usb_notifier(struct notifier_block *nb,
	unsigned long event, void *ptr)
{
	struct extcon_dev *edev = ptr;
	struct dp_display_private *dp = container_of(nb,
			struct dp_display_private, usb_nb);
	if (!edev)
		goto end;

	if (!event && dp->debug->sim_mode) {
		dp_display_disconnect_sync(dp);
		dp->debug->abort(dp->debug);
	}
end:
	return NOTIFY_DONE;
}

static int dp_display_get_usb_extcon(struct dp_display_private *dp)
{
	struct extcon_dev *edev;
	int rc;

	edev = extcon_get_edev_by_phandle(&dp->pdev->dev, 0);
	if (IS_ERR(edev))
		return PTR_ERR(edev);

	dp->usb_nb.notifier_call = dp_display_usb_notifier;
	dp->usb_nb.priority = 2;
	rc = extcon_register_notifier(edev, EXTCON_USB, &dp->usb_nb);
	if (rc)
		pr_err("failed to register for usb event: %d\n", rc);

	return rc;
}

static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
{
	dp_audio_put(dp->panel->audio);
@@ -1195,6 +1225,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
	dp->debug->hdcp_disabled = hdcp_disabled;
	dp_display_update_hdcp_status(dp, true);

	dp_display_get_usb_extcon(dp);

	return rc;
error_debug:
	dp_hpd_put(dp->hpd);
+1 −0
Original line number Diff line number Diff line
@@ -985,6 +985,7 @@ static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid)
		panel->custom_edid = true;
	} else {
		panel->custom_edid = false;
		dp_panel->edid_ctrl->edid = NULL;
	}

	return 0;