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 <tatendac@codeaurora.org>
This commit is contained in:
@@ -397,6 +397,11 @@ static int dp_audio_info_setup(struct platform_device *pdev,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (audio->dp_audio.tui_active) {
|
||||||
|
DP_DEBUG("TUI session active\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&audio->ops_lock);
|
mutex_lock(&audio->ops_lock);
|
||||||
|
|
||||||
audio->channels = params->num_of_channels;
|
audio->channels = params->num_of_channels;
|
||||||
@@ -495,6 +500,11 @@ static void dp_audio_teardown_done(struct platform_device *pdev)
|
|||||||
if (IS_ERR(audio))
|
if (IS_ERR(audio))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (audio->dp_audio.tui_active) {
|
||||||
|
DP_DEBUG("TUI session active\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&audio->ops_lock);
|
mutex_lock(&audio->ops_lock);
|
||||||
dp_audio_enable(audio, false);
|
dp_audio_enable(audio, false);
|
||||||
mutex_unlock(&audio->ops_lock);
|
mutex_unlock(&audio->ops_lock);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
/* 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_
|
#ifndef _DP_AUDIO_H_
|
||||||
@@ -15,15 +15,18 @@
|
|||||||
* struct dp_audio
|
* struct dp_audio
|
||||||
* @lane_count: number of lanes configured in current session
|
* @lane_count: number of lanes configured in current session
|
||||||
* @bw_code: link rate's bandwidth code for 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 {
|
struct dp_audio {
|
||||||
u32 lane_count;
|
u32 lane_count;
|
||||||
u32 bw_code;
|
u32 bw_code;
|
||||||
|
bool tui_active;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* on()
|
* 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.
|
* @dp_audio: an instance of struct dp_audio.
|
||||||
*
|
*
|
||||||
@@ -34,7 +37,8 @@ struct dp_audio {
|
|||||||
/**
|
/**
|
||||||
* off()
|
* 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.
|
* @dp_audio: an instance of struct dp_audio.
|
||||||
*
|
*
|
||||||
|
@@ -63,6 +63,7 @@ enum dp_display_states {
|
|||||||
DP_STATE_ABORTED = BIT(8),
|
DP_STATE_ABORTED = BIT(8),
|
||||||
DP_STATE_HDCP_ABORTED = BIT(9),
|
DP_STATE_HDCP_ABORTED = BIT(9),
|
||||||
DP_STATE_SRC_PWRDN = BIT(10),
|
DP_STATE_SRC_PWRDN = BIT(10),
|
||||||
|
DP_STATE_TUI_ACTIVE = BIT(11),
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *dp_display_state_name(enum dp_display_states state)
|
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|",
|
len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
|
||||||
"SRC_PWRDN");
|
"SRC_PWRDN");
|
||||||
|
|
||||||
|
if (state & DP_STATE_TUI_ACTIVE)
|
||||||
|
len += scnprintf(buf + len, sizeof(buf) - len, "|%s|",
|
||||||
|
"TUI_ACTIVE");
|
||||||
|
|
||||||
if (!strlen(buf))
|
if (!strlen(buf))
|
||||||
return "DISCONNECTED";
|
return "DISCONNECTED";
|
||||||
|
|
||||||
@@ -667,6 +672,74 @@ error:
|
|||||||
return rc;
|
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,
|
static int dp_display_bind(struct device *dev, struct device *master,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
@@ -674,6 +747,10 @@ static int dp_display_bind(struct device *dev, struct device *master,
|
|||||||
struct dp_display_private *dp;
|
struct dp_display_private *dp;
|
||||||
struct drm_device *drm;
|
struct drm_device *drm;
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
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) {
|
if (!dev || !pdev || !master) {
|
||||||
DP_ERR("invalid param(s), dev %pK, pdev %pK, master %pK\n",
|
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->dp_display.drm_dev = drm;
|
||||||
dp->priv = drm->dev_private;
|
dp->priv = drm->dev_private;
|
||||||
|
msm_register_vm_event(master, dev, &vm_event_ops,
|
||||||
|
(void *)&dp->dp_display);
|
||||||
end:
|
end:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -827,6 +906,15 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp)
|
|||||||
reinit_completion(&dp->notification_comp);
|
reinit_completion(&dp->notification_comp);
|
||||||
dp_display_send_hpd_event(dp);
|
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)
|
if (hpd && dp->mst.mst_active)
|
||||||
goto skip_wait;
|
goto skip_wait;
|
||||||
|
|
||||||
@@ -1200,26 +1288,30 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
|
|||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
DP_ERR("invalid dev\n");
|
DP_ERR("invalid dev\n");
|
||||||
rc = -EINVAL;
|
return -EINVAL;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dp = dev_get_drvdata(dev);
|
dp = dev_get_drvdata(dev);
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
DP_ERR("no driver data found\n");
|
DP_ERR("no driver data found\n");
|
||||||
rc = -ENODEV;
|
return -ENODEV;
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dp->debug->sim_mode && !dp->parser->no_aux_switch
|
if (!dp->debug->sim_mode && !dp->parser->no_aux_switch
|
||||||
&& !dp->parser->gpio_aux_switch) {
|
&& !dp->parser->gpio_aux_switch) {
|
||||||
rc = dp->aux->aux_switch(dp->aux, true, dp->hpd->orientation);
|
rc = dp->aux->aux_switch(dp->aux, true, dp->hpd->orientation);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto end;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&dp->session_lock);
|
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_remove(DP_STATE_ABORTED);
|
||||||
dp_display_state_add(DP_STATE_CONFIGURED);
|
dp_display_state_add(DP_STATE_CONFIGURED);
|
||||||
|
|
||||||
@@ -1231,8 +1323,8 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
|
|||||||
else
|
else
|
||||||
dp->process_hpd_connect = true;
|
dp->process_hpd_connect = true;
|
||||||
mutex_unlock(&dp->session_lock);
|
mutex_unlock(&dp->session_lock);
|
||||||
end:
|
|
||||||
return rc;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dp_display_stream_pre_disable(struct dp_display_private *dp,
|
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);
|
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) &&
|
if (dp_display_is_hdcp_enabled(dp) &&
|
||||||
status->hdcp_state != HDCP_STATE_INACTIVE) {
|
status->hdcp_state != HDCP_STATE_INACTIVE) {
|
||||||
cancel_delayed_work_sync(&dp->hdcp_cb_work);
|
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) {
|
if (!dp->hpd->hpd_high) {
|
||||||
dp_display_disconnect_sync(dp);
|
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) {
|
dp->debug->mst_hpd_sim) {
|
||||||
queue_work(dp->wq, &dp->attention_work);
|
queue_work(dp->wq, &dp->attention_work);
|
||||||
} else if (dp->process_hpd_connect ||
|
} 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 *dp = container_of(work,
|
||||||
struct dp_display_private, connect_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)) {
|
if (dp_display_state_is(DP_STATE_ABORTED)) {
|
||||||
DP_WARN("HPD off requested\n");
|
DP_WARN("HPD off requested\n");
|
||||||
return;
|
return;
|
||||||
|
Reference in New Issue
Block a user