Prechádzať zdrojové kódy

disp: msm: dp: add support for Trusted UI transitions

We add support for Trusted UI (TUI) transitions and address the
following use cases:

1. Display was active before TUI start
    - Power off on TUI start, power off on TUI stop
    - Register access not allowed after TUI start (skip all
      events except disconnect)
2. Hotplug while TUI is active
    - Connect: skip sending connect uevent
    - Disconnect: send disconnect uevent and skip any controller
      programming
3. TUI start while processing HPD High
    - Complete all connect work (and therefore any register access)
      then send connect uevent
4. Audio
    - Disable audio at TUI start and skip audio programming if TUI
      is active

Change-Id: I553e5fa9f3b8265dd0410bf2d616a9accf90605f
Signed-off-by: Tatenda Chipeperekwa <[email protected]>
Tatenda Chipeperekwa 4 rokov pred
rodič
commit
f3b2c5f89c
3 zmenil súbory, kde vykonal 144 pridanie a 11 odobranie
  1. 10 0
      msm/dp/dp_audio.c
  2. 7 3
      msm/dp/dp_audio.h
  3. 127 8
      msm/dp/dp_display.c

+ 10 - 0
msm/dp/dp_audio.c

@@ -397,6 +397,11 @@ static int dp_audio_info_setup(struct platform_device *pdev,
 		return rc;
 	}
 
+	if (audio->dp_audio.tui_active) {
+		DP_DEBUG("TUI session active\n");
+		return 0;
+	}
+
 	mutex_lock(&audio->ops_lock);
 
 	audio->channels = params->num_of_channels;
@@ -495,6 +500,11 @@ static void dp_audio_teardown_done(struct platform_device *pdev)
 	if (IS_ERR(audio))
 		return;
 
+	if (audio->dp_audio.tui_active) {
+		DP_DEBUG("TUI session active\n");
+		return;
+	}
+
 	mutex_lock(&audio->ops_lock);
 	dp_audio_enable(audio, false);
 	mutex_unlock(&audio->ops_lock);

+ 7 - 3
msm/dp/dp_audio.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DP_AUDIO_H_
@@ -15,15 +15,18 @@
  * struct dp_audio
  * @lane_count: number of lanes configured in current session
  * @bw_code: link rate's bandwidth code for current session
+ * @tui_active: set to true if TUI is active in the system
  */
 struct dp_audio {
 	u32 lane_count;
 	u32 bw_code;
+	bool tui_active;
 
 	/**
 	 * on()
 	 *
-	 * Enables the audio by notifying the user module.
+	 * Notifies user mode clients that DP is powered on, and that audio
+	 * playback can start on the external display.
 	 *
 	 * @dp_audio: an instance of struct dp_audio.
 	 *
@@ -34,7 +37,8 @@ struct dp_audio {
 	/**
 	 * off()
 	 *
-	 * Disables the audio by notifying the user module.
+	 * Notifies user mode clients that DP is shutting down, and audio
+	 * playback should be stopped on the external display.
 	 *
 	 * @dp_audio: an instance of struct dp_audio.
 	 *

+ 127 - 8
msm/dp/dp_display.c

@@ -63,6 +63,7 @@ enum dp_display_states {
 	DP_STATE_ABORTED                = BIT(8),
 	DP_STATE_HDCP_ABORTED           = BIT(9),
 	DP_STATE_SRC_PWRDN              = BIT(10),
+	DP_STATE_TUI_ACTIVE             = BIT(11),
 };
 
 static char *dp_display_state_name(enum dp_display_states state)
@@ -116,6 +117,10 @@ static char *dp_display_state_name(enum dp_display_states state)
 		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
 			"SRC_PWRDN");
 
+	if (state & DP_STATE_TUI_ACTIVE)
+		len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
+			"TUI_ACTIVE");
+
 	if (!strlen(buf))
 		return "DISCONNECTED";
 
@@ -667,6 +672,74 @@ error:
 	return rc;
 }
 
+static void dp_display_pause_audio(struct dp_display_private *dp, bool pause)
+{
+	struct dp_panel *dp_panel;
+	int idx;
+
+	for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) {
+		if (!dp->active_panels[idx])
+			continue;
+		dp_panel = dp->active_panels[idx];
+
+		if (dp_panel->audio_supported)
+			dp_panel->audio->tui_active = pause;
+	}
+}
+
+static int dp_display_pre_hw_release(void *data)
+{
+	struct dp_display_private *dp;
+	struct dp_display *dp_display = data;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+
+	if (!dp_display)
+		return -EINVAL;
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	mutex_lock(&dp->session_lock);
+
+	dp_display_state_add(DP_STATE_TUI_ACTIVE);
+	cancel_work_sync(&dp->connect_work);
+	cancel_work_sync(&dp->attention_work);
+	flush_workqueue(dp->wq);
+
+	dp_display_pause_audio(dp, true);
+	disable_irq(dp->irq);
+
+	mutex_unlock(&dp->session_lock);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT);
+	return 0;
+}
+
+static int dp_display_post_hw_acquire(void *data)
+{
+	struct dp_display_private *dp;
+	struct dp_display *dp_display = data;
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
+
+	if (!dp_display)
+		return -EINVAL;
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	mutex_lock(&dp->session_lock);
+
+	dp_display_state_remove(DP_STATE_TUI_ACTIVE);
+	dp_display_pause_audio(dp, false);
+	enable_irq(dp->irq);
+
+	mutex_unlock(&dp->session_lock);
+
+	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT);
+	return 0;
+}
+
+
 static int dp_display_bind(struct device *dev, struct device *master,
 		void *data)
 {
@@ -674,6 +747,10 @@ static int dp_display_bind(struct device *dev, struct device *master,
 	struct dp_display_private *dp;
 	struct drm_device *drm;
 	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_vm_ops vm_event_ops = {
+		.vm_pre_hw_release = dp_display_pre_hw_release,
+		.vm_post_hw_acquire = dp_display_post_hw_acquire,
+	};
 
 	if (!dev || !pdev || !master) {
 		DP_ERR("invalid param(s), dev %pK, pdev %pK, master %pK\n",
@@ -693,6 +770,8 @@ static int dp_display_bind(struct device *dev, struct device *master,
 
 	dp->dp_display.drm_dev = drm;
 	dp->priv = drm->dev_private;
+	msm_register_vm_event(master, dev, &vm_event_ops,
+			(void *)&dp->dp_display);
 end:
 	return rc;
 }
@@ -827,6 +906,15 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp)
 	reinit_completion(&dp->notification_comp);
 	dp_display_send_hpd_event(dp);
 
+	/*
+	 * Skip the wait if TUI is active considering that the user mode will
+	 * not act on the notification until after the TUI session is over.
+	 */
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		dp_display_state_log("[TUI is active, skipping wait]");
+		goto skip_wait;
+	}
+
 	if (hpd && dp->mst.mst_active)
 		goto skip_wait;
 
@@ -1200,26 +1288,30 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
 
 	if (!dev) {
 		DP_ERR("invalid dev\n");
-		rc = -EINVAL;
-		goto end;
+		return -EINVAL;
 	}
 
 	dp = dev_get_drvdata(dev);
 	if (!dp) {
 		DP_ERR("no driver data found\n");
-		rc = -ENODEV;
-		goto end;
+		return -ENODEV;
 	}
 
 	if (!dp->debug->sim_mode && !dp->parser->no_aux_switch
 	    && !dp->parser->gpio_aux_switch) {
 		rc = dp->aux->aux_switch(dp->aux, true, dp->hpd->orientation);
 		if (rc)
-			goto end;
+			return rc;
 	}
 
 	mutex_lock(&dp->session_lock);
 
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		dp_display_state_log("[TUI is active]");
+		mutex_unlock(&dp->session_lock);
+		return 0;
+	}
+
 	dp_display_state_remove(DP_STATE_ABORTED);
 	dp_display_state_add(DP_STATE_CONFIGURED);
 
@@ -1231,8 +1323,8 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
 	else
 		dp->process_hpd_connect = true;
 	mutex_unlock(&dp->session_lock);
-end:
-	return rc;
+
+	return 0;
 }
 
 static int dp_display_stream_pre_disable(struct dp_display_private *dp,
@@ -1268,6 +1360,11 @@ static void dp_display_clean(struct dp_display_private *dp)
 
 	SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state);
 
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		DP_WARN("TUI is active\n");
+		return;
+	}
+
 	if (dp_display_is_hdcp_enabled(dp) &&
 			status->hdcp_state != HDCP_STATE_INACTIVE) {
 		cancel_delayed_work_sync(&dp->hdcp_cb_work);
@@ -1550,7 +1647,24 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
 
 	if (!dp->hpd->hpd_high) {
 		dp_display_disconnect_sync(dp);
-	} else if ((dp->hpd->hpd_irq && dp_display_state_is(DP_STATE_READY)) ||
+		return 0;
+	}
+
+	/*
+	 * Ignore all the attention messages except HPD LOW when TUI is
+	 * active, so user mode can be notified of the disconnect event. This
+	 * allows user mode to tear down the control path after the TUI
+	 * session is over. Ideally this should never happen, but on the off
+	 * chance that there is a race condition in which there is a IRQ HPD
+	 * during tear down of DP at TUI start then this check might help avoid
+	 * a potential issue accessing registers in attention processing.
+	 */
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		DP_WARN("TUI is active\n");
+		return 0;
+	}
+
+	if ((dp->hpd->hpd_irq && dp_display_state_is(DP_STATE_READY)) ||
 			dp->debug->mst_hpd_sim) {
 		queue_work(dp->wq, &dp->attention_work);
 	} else if (dp->process_hpd_connect ||
@@ -1570,6 +1684,11 @@ static void dp_display_connect_work(struct work_struct *work)
 	struct dp_display_private *dp = container_of(work,
 			struct dp_display_private, connect_work);
 
+	if (dp_display_state_is(DP_STATE_TUI_ACTIVE)) {
+		dp_display_state_log("[TUI is active]");
+		return;
+	}
+
 	if (dp_display_state_is(DP_STATE_ABORTED)) {
 		DP_WARN("HPD off requested\n");
 		return;