<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<p style="font-family:Arial;font-size:11pt;color:#0078D7;margin:5pt;" align="Left">
[AMD Official Use Only - Internal Distribution Only]<br>
</p>
<br>
<div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0); background-color: rgb(255, 255, 255);">
This patch is a related change for the MST null pointer deref regression, so will be dropped.<br>
</div>
<div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div id="Signature">
<div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
--</div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Thanks & Regards,</div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Aurabindo Pillai<br>
</div>
</div>
</div>
</div>
<div id="appendonsend"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> Aurabindo Pillai <aurabindo.pillai@amd.com><br>
<b>Sent:</b> Friday, April 16, 2021 10:34 AM<br>
<b>To:</b> amd-gfx@lists.freedesktop.org <amd-gfx@lists.freedesktop.org><br>
<b>Cc:</b> Wentland, Harry <Harry.Wentland@amd.com>; Li, Sun peng (Leo) <Sunpeng.Li@amd.com>; Lakha, Bhawanpreet <Bhawanpreet.Lakha@amd.com>; Siqueira, Rodrigo <Rodrigo.Siqueira@amd.com>; Pillai, Aurabindo <Aurabindo.Pillai@amd.com>; Zhuo, Qingqing <Qingqing.Zhuo@amd.com>;
 Brol, Eryk <Eryk.Brol@amd.com>; R, Bindu <Bindu.R@amd.com>; Jacob, Anson <Anson.Jacob@amd.com>; Zhang, Dingchen (David) <Dingchen.Zhang@amd.com>; Zhang, Dingchen (David) <Dingchen.Zhang@amd.com><br>
<b>Subject:</b> [PATCH 14/19] drm/amd/display: fix HDCP drm prop update for MST</font>
<div> </div>
</div>
<div class="BodyFragment"><font size="2"><span style="font-size:11pt;">
<div class="PlainText">From: "Dingchen (David) Zhang" <dingchen.zhang@amd.com><br>
<br>
[why]<br>
For MST topology with 1 physical link and multiple connectors (>=2),<br>
e.g. daisy cahined MST + SST, or 1-to-multi MST hub, if userspace<br>
set to enable the HDCP simultaneously on all connected outputs, the<br>
commit tail iteratively call the hdcp_update_display() for each<br>
display (connector). However, the hdcp workqueue data structure for<br>
each link has only one DM connector and encryption status members,<br>
which means the work queue of property_validate/update() would only<br>
be triggered for the last connector within this physical link, and<br>
therefore the HDCP property value of other connectors would stay on<br>
DESIRED instead of switching to ENABLED, which is NOT as expected.<br>
<br>
[how]<br>
Use array of MAX_NUM_OF_DISPLAY for both aconnector and encryption<br>
status in hdcp workqueue data structure for each physical link.<br>
For property validate/update work queue, we iterates over the array<br>
and do similar operation/check for each connected display.<br>
<br>
Signed-off-by: Dingchen (David) Zhang <dingchen.zhang@amd.com><br>
Reviewed-by: Dingchen Zhang <Dingchen.Zhang@amd.com><br>
Acked-by: Aurabindo Pillai <aurabindo.pillai@amd.com><br>
---<br>
 .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c    | 109 +++++++++++++-----<br>
 .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.h    |   6 +-<br>
 2 files changed, 81 insertions(+), 34 deletions(-)<br>
<br>
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c<br>
index 50f6b3a86931..2ec076af9e89 100644<br>
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c<br>
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c<br>
@@ -171,9 +171,10 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,<br>
         struct mod_hdcp_display *display = &hdcp_work[link_index].display;<br>
         struct mod_hdcp_link *link = &hdcp_work[link_index].link;<br>
         struct mod_hdcp_display_query query;<br>
+       unsigned int conn_index = aconnector->base.index;<br>
 <br>
         mutex_lock(&hdcp_w->mutex);<br>
-       hdcp_w->aconnector = aconnector;<br>
+       hdcp_w->aconnector[conn_index] = aconnector;<br>
 <br>
         query.display = NULL;<br>
         mod_hdcp_query_display(&hdcp_w->hdcp, aconnector->base.index, &query);<br>
@@ -205,7 +206,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,<br>
                                               msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));<br>
                 } else {<br>
                         display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;<br>
-                       hdcp_w->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
+                       hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
                         cancel_delayed_work(&hdcp_w->property_validate_dwork);<br>
                 }<br>
 <br>
@@ -224,9 +225,10 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,<br>
 {<br>
         struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];<br>
         struct drm_connector_state *conn_state = aconnector->base.state;<br>
+       unsigned int conn_index = aconnector->base.index;<br>
 <br>
         mutex_lock(&hdcp_w->mutex);<br>
-       hdcp_w->aconnector = aconnector;<br>
+       hdcp_w->aconnector[conn_index] = aconnector;<br>
 <br>
         /* the removal of display will invoke auth reset -> hdcp destroy and<br>
          * we'd expect the CP prop changed back to DESIRED if at the time ENABLED.<br>
@@ -247,13 +249,16 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,<br>
 void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index)<br>
 {<br>
         struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];<br>
+       unsigned int conn_index;<br>
 <br>
         mutex_lock(&hdcp_w->mutex);<br>
 <br>
         mod_hdcp_reset_connection(&hdcp_w->hdcp,  &hdcp_w->output);<br>
 <br>
         cancel_delayed_work(&hdcp_w->property_validate_dwork);<br>
-       hdcp_w->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
+<br>
+       for (conn_index = 0; conn_index < MAX_NUM_OF_DISPLAYS; ++conn_index)<br>
+               hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
 <br>
         process_output(hdcp_w);<br>
 <br>
@@ -290,38 +295,67 @@ static void event_callback(struct work_struct *work)<br>
 <br>
 <br>
 }<br>
+<br>
+static struct amdgpu_dm_connector *find_first_connected_output(struct hdcp_workqueue *hdcp_work)<br>
+{<br>
+       unsigned int conn_index;<br>
+<br>
+       for (conn_index = 0; conn_index < MAX_NUM_OF_DISPLAYS; ++conn_index) {<br>
+               if (hdcp_work->aconnector[conn_index])<br>
+                       return hdcp_work->aconnector[conn_index];<br>
+       }<br>
+<br>
+       return NULL;<br>
+}<br>
+<br>
 static void event_property_update(struct work_struct *work)<br>
 {<br>
 <br>
         struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue, property_update_work);<br>
-       struct amdgpu_dm_connector *aconnector = hdcp_work->aconnector;<br>
-       struct drm_device *dev = hdcp_work->aconnector->base.dev;<br>
+       struct amdgpu_dm_connector *aconnector = find_first_connected_output(hdcp_work);<br>
+       struct drm_device *dev;<br>
         long ret;<br>
+       unsigned int conn_index;<br>
+       struct drm_connector *connector;<br>
+       struct drm_connector_state *conn_state;<br>
+<br>
+       if (!aconnector)<br>
+               return;<br>
+<br>
+       dev = aconnector->base.dev;<br>
 <br>
         drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);<br>
         mutex_lock(&hdcp_work->mutex);<br>
 <br>
+       for (conn_index = 0; conn_index < MAX_NUM_OF_DISPLAYS; ++conn_index) {<br>
+               aconnector = hdcp_work->aconnector[conn_index];<br>
+<br>
+               if (!aconnector)<br>
+                       continue;<br>
 <br>
-       if (aconnector->base.state->commit) {<br>
-               ret = wait_for_completion_interruptible_timeout(&aconnector->base.state->commit->hw_done, 10 * HZ);<br>
+               connector = &aconnector->base;<br>
+               conn_state = aconnector->base.state;<br>
+               if (conn_state->commit) {<br>
+                       ret = wait_for_completion_interruptible_timeout(&conn_state->commit->hw_done, 10 * HZ);<br>
 <br>
-               if (ret == 0) {<br>
-                       DRM_ERROR("HDCP state unknown! Setting it to DESIRED");<br>
-                       hdcp_work->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
+                       if (ret == 0) {<br>
+                               DRM_ERROR("HDCP state unknown! Setting it to DESIRED");<br>
+                               hdcp_work->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
+                       }<br>
                 }<br>
-       }<br>
 <br>
-       if (hdcp_work->encryption_status != MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {<br>
-               if (aconnector->base.state->hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE0 &&<br>
-                   hdcp_work->encryption_status <= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON)<br>
-                       drm_hdcp_update_content_protection(&aconnector->base, DRM_MODE_CONTENT_PROTECTION_ENABLED);<br>
-               else if (aconnector->base.state->hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE1 &&<br>
-                        hdcp_work->encryption_status == MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON)<br>
-                       drm_hdcp_update_content_protection(&aconnector->base, DRM_MODE_CONTENT_PROTECTION_ENABLED);<br>
-       } else {<br>
-               drm_hdcp_update_content_protection(&aconnector->base, DRM_MODE_CONTENT_PROTECTION_DESIRED);<br>
-       }<br>
+               if (hdcp_work->encryption_status[conn_index] != MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {<br>
+                       if (conn_state->hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE0 &&<br>
+                           hdcp_work->encryption_status[conn_index] <= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON)<br>
+                               drm_hdcp_update_content_protection(connector, DRM_MODE_CONTENT_PROTECTION_ENABLED);<br>
+                       else if (conn_state->hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE1 &&<br>
+                                hdcp_work->encryption_status[conn_index] == MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON)<br>
+                               drm_hdcp_update_content_protection(connector, DRM_MODE_CONTENT_PROTECTION_ENABLED);<br>
+               } else {<br>
+                       drm_hdcp_update_content_protection(connector, DRM_MODE_CONTENT_PROTECTION_DESIRED);<br>
+               }<br>
 <br>
+       }<br>
 <br>
         mutex_unlock(&hdcp_work->mutex);<br>
         drm_modeset_unlock(&dev->mode_config.connection_mutex);<br>
@@ -332,19 +366,28 @@ static void event_property_validate(struct work_struct *work)<br>
         struct hdcp_workqueue *hdcp_work =<br>
                 container_of(to_delayed_work(work), struct hdcp_workqueue, property_validate_dwork);<br>
         struct mod_hdcp_display_query query;<br>
-       struct amdgpu_dm_connector *aconnector = hdcp_work->aconnector;<br>
-<br>
-       if (!aconnector)<br>
-               return;<br>
+       struct amdgpu_dm_connector *aconnector;<br>
+       unsigned int conn_index;<br>
 <br>
         mutex_lock(&hdcp_work->mutex);<br>
 <br>
-       query.encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
-       mod_hdcp_query_display(&hdcp_work->hdcp, aconnector->base.index, &query);<br>
+       for (conn_index = 0; conn_index < MAX_NUM_OF_DISPLAYS; ++conn_index) {<br>
+               aconnector = hdcp_work->aconnector[conn_index];<br>
+<br>
+               if (!aconnector)<br>
+                       continue;<br>
+<br>
+               query.encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;<br>
+               mod_hdcp_query_display(&hdcp_work->hdcp, aconnector->base.index, &query);<br>
 <br>
-       if (query.encryption_status != hdcp_work->encryption_status) {<br>
-               hdcp_work->encryption_status = query.encryption_status;<br>
-               schedule_work(&hdcp_work->property_update_work);<br>
+               pr_debug("[HDCP_DM] display %d, CP %u, (query->enc_st, work->enc_st): (%d, %d)\n",<br>
+                        aconnector->base.index, aconnector->base.state->content_protection,<br>
+                        query.encryption_status, hdcp_work->encryption_status[conn_index]);<br>
+<br>
+               if (query.encryption_status != hdcp_work->encryption_status[conn_index]) {<br>
+                       hdcp_work->encryption_status[conn_index] = query.encryption_status;<br>
+                       schedule_work(&hdcp_work->property_update_work);<br>
+               }<br>
         }<br>
 <br>
         mutex_unlock(&hdcp_work->mutex);<br>
@@ -655,6 +698,10 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct<br>
                 hdcp_work[i].hdcp.config.ddc.funcs.read_i2c = lp_read_i2c;<br>
                 hdcp_work[i].hdcp.config.ddc.funcs.write_dpcd = lp_write_dpcd;<br>
                 hdcp_work[i].hdcp.config.ddc.funcs.read_dpcd = lp_read_dpcd;<br>
+<br>
+               memset(hdcp_work[i].aconnector, 0, sizeof(struct amdgpu_dm_connector *) * MAX_NUM_OF_DISPLAYS);<br>
+               memset(hdcp_work[i].encryption_status, 0,<br>
+                       sizeof(enum mod_hdcp_encryption_status) * MAX_NUM_OF_DISPLAYS);<br>
         }<br>
 <br>
         cp_psp->funcs.update_stream_config = update_config;<br>
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h<br>
index 09294ff122fe..570863160d60 100644<br>
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h<br>
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h<br>
@@ -1,5 +1,5 @@<br>
 /*<br>
- * Copyright 2019 Advanced Micro Devices, Inc.<br>
+ * Copyright (C) 2019-2020 Advanced Micro Devices, Inc. All rights reserved.<br>
  *<br>
  * Permission is hereby granted, free of charge, to any person obtaining a<br>
  * copy of this software and associated documentation files (the "Software"),<br>
@@ -43,7 +43,7 @@ struct hdcp_workqueue {<br>
         struct delayed_work callback_dwork;<br>
         struct delayed_work watchdog_timer_dwork;<br>
         struct delayed_work property_validate_dwork;<br>
-       struct amdgpu_dm_connector *aconnector;<br>
+       struct amdgpu_dm_connector *aconnector[MAX_NUM_OF_DISPLAYS];<br>
         struct mutex mutex;<br>
 <br>
         struct mod_hdcp hdcp;<br>
@@ -51,7 +51,7 @@ struct hdcp_workqueue {<br>
         struct mod_hdcp_display display;<br>
         struct mod_hdcp_link link;<br>
 <br>
-       enum mod_hdcp_encryption_status encryption_status;<br>
+       enum mod_hdcp_encryption_status encryption_status[MAX_NUM_OF_DISPLAYS];<br>
         uint8_t max_link;<br>
 <br>
         uint8_t *srm;<br>
-- <br>
2.31.1<br>
<br>
</div>
</span></font></div>
</div>
</body>
</html>