Skip to contentSkip to conversation

drm/hdcp: Pull HDCP auth/exchange/check into helpers

PATCH Superseded Mark Yacoub 5 participants 21 messages Mar 31, 2023 – Apr 7, 2023

Subsystems (9)

v8 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

v8 cover drm/hdcp: Pull HDCP auth/exchange/check into helpers

Hi all,
This is v7 of the HDCP patches. The patches are authored by Sean Paul. I rebased and addressed the review comments in v6-v8.

Patches 1-4 focus on moving the common HDCP helpers to common DRM. This introduces a slight change in the original intel flow as it splits the unique driver protocol from the generic implementation.

Patches 5-7 split the HDCP flow on the i915 driver to make use of the common DRM helpers.

Patches 8-10 implement HDCP on MSM driver.

Thanks,
-Mark Yacoub

Sean Paul (10):
drm/hdcp: Add drm_hdcp_atomic_check()
drm/hdcp: Avoid changing crtc state in hdcp atomic check drm/hdcp: Update property value on content type and user changes drm/hdcp: Expand HDCP helper library for enable/disable/check drm/i915/hdcp: Consolidate HDCP setup/state cache drm/i915/hdcp: Retain hdcp_capable return codes drm/i915/hdcp: Use HDCP helpers for i915
dt-bindings: msm/dp: Add bindings for HDCP registers
arm64: dts: qcom: sc7180: Add support for HDCP in dp-controller drm/msm: Implement HDCP 1.x using the new drm HDCP helpers

 .../bindings/display/msm/dp-controller.yaml   |    7 +-
 arch/arm64/boot/dts/qcom/sc7180-trogdor.dtsi  |    8 +
 drivers/gpu/drm/display/drm_hdcp_helper.c     | 1303 +++++++++++++++++
 drivers/gpu/drm/i915/display/intel_atomic.c   |    8 +-
 drivers/gpu/drm/i915/display/intel_ddi.c      |   32 +-
 .../drm/i915/display/intel_display_debugfs.c  |   11 +-
 .../drm/i915/display/intel_display_types.h    |   51 +-
 drivers/gpu/drm/i915/display/intel_dp_hdcp.c  |  368 ++---
 drivers/gpu/drm/i915/display/intel_dp_mst.c   |   16 +-
 drivers/gpu/drm/i915/display/intel_hdcp.c     | 1036 +++----------
 drivers/gpu/drm/i915/display/intel_hdcp.h     |   42 +-
 drivers/gpu/drm/i915/display/intel_hdmi.c     |  276 ++--
 drivers/gpu/drm/msm/Kconfig                   |    1 +
 drivers/gpu/drm/msm/Makefile                  |    1 +
 drivers/gpu/drm/msm/dp/dp_catalog.c           |  156 ++
 drivers/gpu/drm/msm/dp/dp_catalog.h           |   18 +
 drivers/gpu/drm/msm/dp/dp_debug.c             |   46 +-
 drivers/gpu/drm/msm/dp/dp_debug.h             |   11 +-
 drivers/gpu/drm/msm/dp/dp_display.c           |   39 +-
 drivers/gpu/drm/msm/dp/dp_display.h           |    5 +
 drivers/gpu/drm/msm/dp/dp_drm.c               |   39 +-
 drivers/gpu/drm/msm/dp/dp_drm.h               |    7 +
 drivers/gpu/drm/msm/dp/dp_hdcp.c              |  397 +++++
 drivers/gpu/drm/msm/dp/dp_hdcp.h              |   33 +
 drivers/gpu/drm/msm/dp/dp_parser.c            |   14 +
 drivers/gpu/drm/msm/dp/dp_parser.h            |    4 +
 drivers/gpu/drm/msm/dp/dp_reg.h               |   30 +-
 drivers/gpu/drm/msm/msm_atomic.c              |   19 +
 include/drm/display/drm_hdcp.h                |  287 ++++
 include/drm/display/drm_hdcp_helper.h         |   52 +
 30 files changed, 2983 insertions(+), 1334 deletions(-)
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hdcp.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hdcp.h
Signature · 1 line
2.40.0.348.gf938b09366-goog
#1 lore raw

PATCH v8 1/10 drm/hdcp: Add drm_hdcp_atomic_check()

From: Sean Paul <[email protected]>

Move the hdcp atomic check from i915 to drm_hdcp so other drivers can use it. No functional changes, just cleaned up some of the code when moving it over.

---
Changes in v2:
-None
Changes in v3:
-None
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebase: move helper from drm_hdcp.c to drm_hdcp_helper.c Changes in v7:
-Removed links to patch from commit msg (Dmitry Baryshkov) Changes in v8:
-None

 drivers/gpu/drm/display/drm_hdcp_helper.c   | 64 +++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_atomic.c |  4 +-
 drivers/gpu/drm/i915/display/intel_hdcp.c   | 47 ---------------
 drivers/gpu/drm/i915/display/intel_hdcp.h   |  3 -
 include/drm/display/drm_hdcp_helper.h       |  3 +
 5 files changed, 69 insertions(+), 52 deletions(-)
drivers/gpu/drm/display/drm_hdcp_helper.c +64 -0
index e78999c72bd77..7ca390b3ea106 100644
@@ -20,6 +20,7 @@
 #include <drm/drm_property.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_connector.h>
+#include <drm/drm_atomic.h>
 
 static inline void drm_hdcp_print_ksv(const u8 *ksv)
 {
@@ -419,3 +420,66 @@ void drm_hdcp_update_content_protection(struct drm_connector *connector,
 				 dev->mode_config.content_protection_property);
 }
 EXPORT_SYMBOL(drm_hdcp_update_content_protection);
+
+/**
+ * drm_hdcp_atomic_check - Helper for drivers to call during connector->atomic_check
+ *
+ * @state: pointer to the atomic state being checked
+ * @connector: drm_connector on which content protection state needs an update
+ *
+ * This function can be used by display drivers to perform an atomic check on the
+ * hdcp state elements. If hdcp state has changed, this function will set
+ * mode_changed on the crtc driving the connector so it can update its hardware
+ * to match the hdcp state.
+ */
+void drm_hdcp_atomic_check(struct drm_connector *connector,
+			   struct drm_atomic_state *state)
+{
+	struct drm_connector_state *new_conn_state, *old_conn_state;
+	struct drm_crtc_state *new_crtc_state;
+	u64 old_hdcp, new_hdcp;
+
+	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+	old_hdcp = old_conn_state->content_protection;
+
+	new_conn_state = drm_atomic_get_new_connector_state(state, connector);
+	new_hdcp = new_conn_state->content_protection;
+
+	if (!new_conn_state->crtc) {
+		/*
+		 * If the connector is being disabled with CP enabled, mark it
+		 * desired so it's re-enabled when the connector is brought back
+		 */
+		if (old_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+			new_conn_state->content_protection =
+				DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		return;
+	}
+
+	new_crtc_state =
+		drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
+	if (drm_atomic_crtc_needs_modeset(new_crtc_state) &&
+	    (old_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED &&
+	     new_hdcp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED))
+		new_conn_state->content_protection =
+			DRM_MODE_CONTENT_PROTECTION_DESIRED;
+
+	/*
+	 * Nothing to do if content type is unchanged and one of:
+	 *  - state didn't change
+	 *  - HDCP was activated since the last commit
+	 *  - attempting to set to desired while already enabled
+	 */
+	if (old_hdcp == new_hdcp ||
+	    (old_hdcp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
+	     new_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED) ||
+	    (old_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED &&
+	     new_hdcp == DRM_MODE_CONTENT_PROTECTION_DESIRED)) {
+		if (old_conn_state->hdcp_content_type ==
+		    new_conn_state->hdcp_content_type)
+			return;
+	}
+
+	new_crtc_state->mode_changed = true;
+}
+EXPORT_SYMBOL(drm_hdcp_atomic_check);
drivers/gpu/drm/i915/display/intel_atomic.c +2 -2
index a9a3f3715279d..e9d00b6a63d39 100644
@@ -32,6 +32,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/display/drm_hdcp_helper.h>
 
 #include "i915_drv.h"
 #include "i915_reg.h"
@@ -39,7 +40,6 @@
 #include "intel_cdclk.h"
 #include "intel_display_types.h"
 #include "intel_global_state.h"
-#include "intel_hdcp.h"
 #include "intel_psr.h"
 #include "intel_fb.h"
 #include "skl_universal_plane.h"
@@ -124,7 +124,7 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 		to_intel_digital_connector_state(old_state);
 	struct drm_crtc_state *crtc_state;
 
-	intel_hdcp_atomic_check(conn, old_state, new_state);
+	drm_hdcp_atomic_check(conn, state);
 
 	if (!new_state->crtc)
 		return 0;
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -47

53 lines not on this page · raw message

drivers/gpu/drm/i915/display/intel_hdcp.h +0 -3
index 8f53b0c7fe5cf..7c5fd84a7b65a 100644
@@ -22,9 +22,6 @@ struct intel_digital_port;
 enum port;
 enum transcoder;
 
-void intel_hdcp_atomic_check(struct drm_connector *connector,
-			     struct drm_connector_state *old_state,
-			     struct drm_connector_state *new_state);
 int intel_hdcp_init(struct intel_connector *connector,
 		    struct intel_digital_port *dig_port,
 		    const struct intel_hdcp_shim *hdcp_shim);
include/drm/display/drm_hdcp_helper.h +3 -0
index 8aaf87bf27351..dd02b2e72a502 100644
@@ -11,6 +11,7 @@
 
 #include <drm/display/drm_hdcp.h>
 
+struct drm_atomic_state;
 struct drm_device;
 struct drm_connector;
 
@@ -18,5 +19,7 @@ int drm_hdcp_check_ksvs_revoked(struct drm_device *dev, u8 *ksvs, u32 ksv_count)
 int drm_connector_attach_content_protection_property(struct drm_connector *connector,
 						     bool hdcp_content_type);
 void drm_hdcp_update_content_protection(struct drm_connector *connector, u64 val);
+void drm_hdcp_atomic_check(struct drm_connector *connector,
+			   struct drm_atomic_state *state);
 
 #endif
Signature · 1 line
2.40.0.348.gf938b09366-goog
#2 lore raw

PATCH v8 2/10 drm/hdcp: Avoid changing crtc state in hdcp atomic check

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Instead of forcing a modeset in the hdcp atomic check, rename to drm_hdcp_has_changed and return true if the content protection value is changing and let the driver decide whether a modeset is required or not.

---
Changes in v2:
-None
Changes in v3:
-None
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebase: modifications in drm_hdcp_helper.c instead of drm_hdcp.c Changes in v7:
-Renamed the function from drm_hdcp_atomic_check to drm_hdcp_has_changed (Dmitry Baryshkov)
Changes in v8:
-None

 drivers/gpu/drm/display/drm_hdcp_helper.c   | 39 ++++++++++++++-------
 drivers/gpu/drm/i915/display/intel_atomic.c |  6 ++--
 include/drm/display/drm_hdcp_helper.h       |  2 +-
 3 files changed, 30 insertions(+), 17 deletions(-)
drivers/gpu/drm/display/drm_hdcp_helper.c +27 -12
index 7ca390b3ea106..34baf2b97cd87 100644
@@ -422,18 +422,21 @@ void drm_hdcp_update_content_protection(struct drm_connector *connector,
 EXPORT_SYMBOL(drm_hdcp_update_content_protection);
 
 /**
- * drm_hdcp_atomic_check - Helper for drivers to call during connector->atomic_check
+ * drm_hdcp_has_changed - Helper for drivers to call during connector->atomic_check
  *
  * @state: pointer to the atomic state being checked
  * @connector: drm_connector on which content protection state needs an update
  *
  * This function can be used by display drivers to perform an atomic check on the
- * hdcp state elements. If hdcp state has changed, this function will set
- * mode_changed on the crtc driving the connector so it can update its hardware
- * to match the hdcp state.
+ * hdcp state elements. If hdcp state has changed in a manner which requires the
+ * driver to enable or disable content protection, this function will return
+ * true.
+ *
+ * Returns:
+ * true if the driver must enable/disable hdcp, false otherwise
  */
-void drm_hdcp_atomic_check(struct drm_connector *connector,
-			   struct drm_atomic_state *state)
+bool drm_hdcp_has_changed(struct drm_connector *connector,
+			  struct drm_atomic_state *state)
 {
 	struct drm_connector_state *new_conn_state, *old_conn_state;
 	struct drm_crtc_state *new_crtc_state;
@@ -450,19 +453,31 @@ void drm_hdcp_atomic_check(struct drm_connector *connector,
 		 * If the connector is being disabled with CP enabled, mark it
 		 * desired so it's re-enabled when the connector is brought back
 		 */
-		if (old_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		if (old_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
 			new_conn_state->content_protection =
 				DRM_MODE_CONTENT_PROTECTION_DESIRED;
-		return;
+			return true;
+		}
+		return false;
 	}
 
 	new_crtc_state =
 		drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
 	if (drm_atomic_crtc_needs_modeset(new_crtc_state) &&
 	    (old_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED &&
-	     new_hdcp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED))
+	     new_hdcp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) {
 		new_conn_state->content_protection =
 			DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		return true;
+	}
+
+	/*
+	 * Coming back from UNDESIRED state, CRTC change or re-enablement requires
+	 * the driver to try CP enable.
+	 */
+	if (new_hdcp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
+	    new_conn_state->crtc != old_conn_state->crtc)
+		return true;
 
 	/*
 	 * Nothing to do if content type is unchanged and one of:
@@ -477,9 +492,9 @@ void drm_hdcp_atomic_check(struct drm_connector *connector,
 	     new_hdcp == DRM_MODE_CONTENT_PROTECTION_DESIRED)) {
 		if (old_conn_state->hdcp_content_type ==
 		    new_conn_state->hdcp_content_type)
-			return;
+			return false;
 	}
 
-	new_crtc_state->mode_changed = true;
+	return true;
 }
-EXPORT_SYMBOL(drm_hdcp_atomic_check);
+EXPORT_SYMBOL(drm_hdcp_has_changed);
drivers/gpu/drm/i915/display/intel_atomic.c +2 -4
index e9d00b6a63d39..23a6ba315a22e 100644
@@ -124,8 +124,6 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 		to_intel_digital_connector_state(old_state);
 	struct drm_crtc_state *crtc_state;
 
-	drm_hdcp_atomic_check(conn, state);
-
 	if (!new_state->crtc)
 		return 0;
 
@@ -141,8 +139,8 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
 	    new_conn_state->base.picture_aspect_ratio != old_conn_state->base.picture_aspect_ratio ||
 	    new_conn_state->base.content_type != old_conn_state->base.content_type ||
 	    new_conn_state->base.scaling_mode != old_conn_state->base.scaling_mode ||
-	    new_conn_state->base.privacy_screen_sw_state != old_conn_state->base.privacy_screen_sw_state ||
-	    !drm_connector_atomic_hdr_metadata_equal(old_state, new_state))
+	    !drm_connector_atomic_hdr_metadata_equal(old_state, new_state) ||
+	    drm_hdcp_has_changed(conn, state))
 		crtc_state->mode_changed = true;
 
 	return 0;
include/drm/display/drm_hdcp_helper.h +1 -1
index dd02b2e72a502..703421fcdf96c 100644
@@ -19,7 +19,7 @@ int drm_hdcp_check_ksvs_revoked(struct drm_device *dev, u8 *ksvs, u32 ksv_count)
 int drm_connector_attach_content_protection_property(struct drm_connector *connector,
 						     bool hdcp_content_type);
 void drm_hdcp_update_content_protection(struct drm_connector *connector, u64 val);
-void drm_hdcp_atomic_check(struct drm_connector *connector,
+bool drm_hdcp_has_changed(struct drm_connector *connector,
 			   struct drm_atomic_state *state);
 
 #endif
Signature · 1 line
2.40.0.348.gf938b09366-goog
#3 lore raw

PATCH v8 3/10 drm/hdcp: Update property value on content type and user changes

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Update the connector's property value in 2 cases which were previously missed:

1- Content type changes. The value should revert back to DESIRED from
ENABLED in case the driver must re-authenticate the link due to the new content type.

2- Userspace sets value to DESIRED while ENABLED. In this case, the
value should be reset immediately to ENABLED since the link is actively being encrypted.

To accommodate these changes, I've split up the conditionals to make things a bit more clear (as much as one can with this mess of state).

---
Changes in v2:
-None
Changes in v3:
-Fixed indentation issue identified by 0-day
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebased: modifications in drm_hdcp_helper.c instead of drm_hdcp.c Changes in v7:
-Rebased as function name has changed.
Changes in v8:
-None

 drivers/gpu/drm/display/drm_hdcp_helper.c | 29 +++++++++++++++--------
 1 file changed, 19 insertions(+), 10 deletions(-)
drivers/gpu/drm/display/drm_hdcp_helper.c +19 -10
index 34baf2b97cd87..3ee1a6ae26c53 100644
@@ -480,21 +480,30 @@ bool drm_hdcp_has_changed(struct drm_connector *connector,
 		return true;
 
 	/*
-	 * Nothing to do if content type is unchanged and one of:
-	 *  - state didn't change
-	 *  - HDCP was activated since the last commit
-	 *  - attempting to set to desired while already enabled
+	 * Content type changes require an HDCP disable/enable cycle.
 	 */
-	if (old_hdcp == new_hdcp ||
-	    (old_hdcp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
+	if (new_conn_state->hdcp_content_type !=
+	    old_conn_state->hdcp_content_type) {
+		new_conn_state->content_protection =
+			DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		return true;
+	}
+
+	/*
+	 * Ignore meaningless state changes:
+ 	 *  - HDCP was activated since the last commit
+	 *  - Attempting to set to desired while already enabled
+ 	 */
+	if ((old_hdcp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
 	     new_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED) ||
 	    (old_hdcp == DRM_MODE_CONTENT_PROTECTION_ENABLED &&
 	     new_hdcp == DRM_MODE_CONTENT_PROTECTION_DESIRED)) {
-		if (old_conn_state->hdcp_content_type ==
-		    new_conn_state->hdcp_content_type)
-			return false;
+		new_conn_state->content_protection =
+			DRM_MODE_CONTENT_PROTECTION_ENABLED;
+		return false;
 	}
 
-	return true;
+	/* Finally, if state changes, we need action */
+	return old_hdcp != new_hdcp;
 }
 EXPORT_SYMBOL(drm_hdcp_has_changed);
Signature · 1 line
2.40.0.348.gf938b09366-goog
#4 lore raw

PATCH v8 4/10 drm/hdcp: Expand HDCP helper library for enable/disable/check

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Expand upon the HDCP helper library to manage HDCP enable, disable, and check.

Previous to this patch, the majority of the state management and sink interaction is tucked inside the Intel driver with the understanding that once a new platform supported HDCP we could make good decisions about what should be centralized. With the addition of HDCP support for Qualcomm, it's time to migrate the protocol-specific bits of HDCP authentication, key exchange, and link checks to the HDCP helper.

In terms of functionality, this migration is 1:1 with the Intel driver, however things are laid out a bit differently than with intel_hdcp.c, which is why this is a separate patch from the i915 transition to the helper. On i915, the shim vtable is used to account for HDMI vs. DP vs. DP-MST differences whereas the helper library uses a LUT to account for the register offsets and a remote read function to route the messages. On i915, storing the sink information in the source is done inline whereas now we use the new drm_hdcp_helper_funcs vtable to store and fetch information to/from source hw. Finally, instead of calling enable/disable directly from the driver, we'll leave that decision to the helper and by calling drm_hdcp_helper_atomic_commit() from the driver. All told, this will centralize the protocol and state handling in the helper, ensuring we collect all of our bugs^Wlogic in one place.

---
Changes in v2:
-Fixed set-but-unused variable identified by 0-day
Changes in v3:
-Fixed uninitialized variable warning identified by 0-day Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Fixed typo in function descriptions
-Rebased: Moved the new code between drm_hdcp.h and drm_hdcp_helper.c/h -Add missing headers. Reported-by: kernel test robot <[email protected]> Changes in v7:
- Add a |driver_data| field to some functions in drm_hdcp_helper_funcs that are called by the driver so drivers can pass anything they such as bridges
- Isolate all non-common code between HDMI and DP into separate functions instead of manually checking for datra->aux
Changes in v8 (suraj):
-Try hdcp 1.x if hdcp2_capable returns an error instead of goto out. -set the enabled type to either 1 or 0 depending on the prop as hdcp2 can support either.

 drivers/gpu/drm/display/drm_hdcp_helper.c | 1215 +++++++++++++++++++++
 include/drm/display/drm_hdcp.h            |  287 +++++
 include/drm/display/drm_hdcp_helper.h     |   51 +-
 3 files changed, 1552 insertions(+), 1 deletion(-)
drivers/gpu/drm/display/drm_hdcp_helper.c +1215 -0
index 3ee1a6ae26c53..3bc0308cc95d8 100644
@@ -6,13 +6,18 @@
  * Ramalingam C <[email protected]>
  */
 
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/gfp.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/firmware.h>
+#include <linux/workqueue.h>
 
+#include <drm/display/drm_dp_helper.h>
 #include <drm/display/drm_hdcp_helper.h>
 #include <drm/drm_sysfs.h>
 #include <drm/drm_print.h>
@@ -507,3 +512,1213 @@ bool drm_hdcp_has_changed(struct drm_connector *connector,
 	return old_hdcp != new_hdcp;
 }
 EXPORT_SYMBOL(drm_hdcp_has_changed);
+
+struct drm_hdcp_hdcp1_receiver_reg_lut {
+	unsigned int bksv;
+	unsigned int ri;
+	unsigned int aksv;
+	unsigned int an;
+	unsigned int ainfo;
+	unsigned int v[5];
+	unsigned int bcaps;
+	unsigned int bcaps_mask_repeater_present;
+	unsigned int bstatus;
+};
+
+static const struct drm_hdcp_hdcp1_receiver_reg_lut drm_hdcp_hdcp1_ddc_lut = {
+	.bksv = DRM_HDCP_DDC_BKSV,
+	.ri = DRM_HDCP_DDC_RI_PRIME,
+	.aksv = DRM_HDCP_DDC_AKSV,
+	.an = DRM_HDCP_DDC_AN,
+	.ainfo = DRM_HDCP_DDC_AINFO,
+	.v = { DRM_HDCP_DDC_V_PRIME(0), DRM_HDCP_DDC_V_PRIME(1),
+	       DRM_HDCP_DDC_V_PRIME(2), DRM_HDCP_DDC_V_PRIME(3),
+	       DRM_HDCP_DDC_V_PRIME(4) },
+	.bcaps = DRM_HDCP_DDC_BCAPS,
+	.bcaps_mask_repeater_present = DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT,
+	.bstatus = DRM_HDCP_DDC_BSTATUS,
+};
+
+static const struct drm_hdcp_hdcp1_receiver_reg_lut drm_hdcp_hdcp1_dpcd_lut = {
+	.bksv = DP_AUX_HDCP_BKSV,
+	.ri = DP_AUX_HDCP_RI_PRIME,
+	.aksv = DP_AUX_HDCP_AKSV,
+	.an = DP_AUX_HDCP_AN,
+	.ainfo = DP_AUX_HDCP_AINFO,
+	.v = { DP_AUX_HDCP_V_PRIME(0), DP_AUX_HDCP_V_PRIME(1),
+	       DP_AUX_HDCP_V_PRIME(2), DP_AUX_HDCP_V_PRIME(3),
+	       DP_AUX_HDCP_V_PRIME(4) },
+	.bcaps = DP_AUX_HDCP_BCAPS,
+	.bcaps_mask_repeater_present = DP_BCAPS_REPEATER_PRESENT,
+
+	/*
+	 * For some reason the HDMI and DP HDCP specs call this register
+	 * definition by different names. In the HDMI spec, it's called BSTATUS,
+	 * but in DP it's called BINFO.
+	 */
+	.bstatus = DP_AUX_HDCP_BINFO,
+};
+
+/*
+ * Read a DPCD register.
+ *
+ * @data: drm_hdcp_helper_data containing the DisplayPort AUX channel (SST or MST)
+ * @offset: address of the (first) register to read
+ * @value: buffer to store the register values
+ * @len: number of bytes in @value
+ * 
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_hdcp_remote_dpcd_read(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *value, size_t len)
+{
+	ssize_t ret;
+	struct drm_dp_aux *aux = data->aux;
+
+	ret = drm_dp_dpcd_read(aux, offset, value, len);
+	if (ret != len) {
+		if (ret >= 0)
+			return -EIO;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_dpcd_read);
+
+/*
+ * Read a value from a remote DDC bus.
+ *
+ * @data: drm_hdcp_helper_data containing the i2c_adapter.
+ * @offset: Offset of the value to read.
+ * @value: Pointer to the value to read.
+ * @len: Length of the value to read.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+
+int drm_hdcp_remote_ddc_read(struct drm_hdcp_helper_data *data,
+			     unsigned int offset, u8 *value, size_t len)
+{
+	int ret;
+	struct i2c_adapter *i2c = data->connector->ddc;
+	u8 start = offset & 0xff;
+	struct i2c_msg msgs[] = { { .addr = DRM_HDCP_DDC_ADDR,
+				    .flags = 0,
+				    .len = 1,
+				    .buf = &start },
+				  { .addr = DRM_HDCP_DDC_ADDR,
+				    .flags = I2C_M_RD,
+				    .len = len,
+				    .buf = value } };
+	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
+	if (ret == ARRAY_SIZE(msgs))
+		return 0;
+	return ret >= 0 ? -EIO : ret;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_ddc_read);
+
+/*
+ * Write to a DPCD register.
+ *
+ * @data: drm_hdcp_helper_data containing the DisplayPort AUX channel (SST or MST)
+ * @offset: Offset of the DPCD register to read from
+ * @value: Pointer to store the DPCD register data
+ * @len: Number of bytes to read
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure
+ */
+int drm_hdcp_remote_dpcd_write(struct drm_hdcp_helper_data *data,
+			       unsigned int offset, u8 *value, size_t len)
+{
+	ssize_t ret;
+	struct drm_dp_aux *aux = data->aux;
+
+	ret = drm_dp_dpcd_write(aux, offset, value, len);
+	if (ret != len) {
+		if (ret >= 0)
+			return -EIO;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_dpcd_write);
+
+/*
+ * Write data to the HDCP DDC slave device
+ *
+ * @data: drm_hdcp_helper_data containing the i2c_adapter.
+ * @offset: the offset to write to
+ * @buffer: the data to write
+ * @size: the size of the data
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_hdcp_remote_ddc_write(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *buffer, size_t len)
+{
+	int ret;
+	u8 *write_buf;
+	struct i2c_msg msg;
+	struct i2c_adapter *i2c = data->connector->ddc;
+
+	write_buf = kzalloc(len + 1, GFP_KERNEL);
+	if (!write_buf)
+		return -ENOMEM;
+
+	write_buf[0] = offset & 0xff;
+	memcpy(&write_buf[1], buffer, len);
+
+	msg.addr = DRM_HDCP_DDC_ADDR;
+	msg.flags = 0, msg.len = len + 1, msg.buf = write_buf;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+	if (ret == 1)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+
+	kfree(write_buf);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_ddc_write);
+
+static bool drm_hdcp_is_ksv_valid(struct drm_hdcp_ksv *ksv)
+{
+	/* Valid Ksv has 20 0's and 20 1's */
+	return hweight32(ksv->words[0]) + hweight32(ksv->words[1]) == 20;
+}
+
+static int drm_hdcp_read_valid_bksv(struct drm_hdcp_helper_data *data,
+				    struct drm_hdcp_ksv *bksv)
+{
+	int ret, i, tries = 2;
+
+	/* HDCP spec states that we must retry the bksv if it is invalid */
+	for (i = 0; i < tries; i++) {
+		ret = data->funcs->remote_read(data, data->hdcp1_lut->bksv,
+					       bksv->bytes, DRM_HDCP_KSV_LEN);
+		if (ret)
+			return ret;
+
+		if (drm_hdcp_is_ksv_valid(bksv))
+			break;
+	}
+	if (i == tries) {
+		drm_dbg_kms(data->connector->dev, "Bksv is invalid %*ph\n",
+			    DRM_HDCP_KSV_LEN, bksv->bytes);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * Check if the sink is capable of HDCP 1.x. DisplayPort has a dedicated bit
+ * for this in DPCD.
+ *
+ * @data: pointer to the HDCP helper data.
+ * @capable: pointer to a bool which will contain true if the sink is capable.
+ *
+ * Returns:
+ * -errno if the transacation between source and sink fails.
+ */
+int drm_hdcp_helper_hdcp1_capable_dp(struct drm_hdcp_helper_data *data,
+				     bool *capable)
+{
+	int ret;
+	u8 bcaps;
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->bcaps, &bcaps, 1);
+	*capable = !ret && (bcaps & DP_BCAPS_HDCP_CAPABLE);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_capable_dp);
+
+/**
+ * Check if the sink is capable of HDCP 1.x. HDMI spec states that transmitters
+ * should use bksv to determine capability
+ *
+ * @data: pointer to the HDCP helper data.
+ * @capable: pointer to a bool which will contain true if the sink is capable.
+ *
+ * Returns:
+ * -errno if the transacation between source and sink fails.
+ */
+int drm_hdcp_helper_hdcp1_capable_hdmi(struct drm_hdcp_helper_data *data,
+				       bool *capable)
+{
+	struct drm_hdcp_ksv bksv;
+	*capable = drm_hdcp_read_valid_bksv(data, &bksv) == 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_capable_hdmi);
+
+static void drm_hdcp_update_value(struct drm_hdcp_helper_data *data, u64 value,
+				  bool update_property)
+{
+	WARN_ON(!mutex_is_locked(&data->mutex));
+
+	data->value = value;
+	if (update_property) {
+		drm_connector_get(data->connector);
+		schedule_work(&data->prop_work);
+	}
+}
+
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp(struct drm_hdcp_helper_data *data)
+{
+	u8 val;
+	u8 mask = DP_BSTATUS_READY;
+	int ret = drm_hdcp_remote_dpcd_read(data, DP_AUX_HDCP_BSTATUS, &val, 1);
+
+	if (ret)
+		return ret;
+	if (val & mask)
+		return 0;
+
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp);
+
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi(struct drm_hdcp_helper_data *data)
+{
+	u8 val;
+	u8 mask = DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
+	int ret = drm_hdcp_remote_ddc_read(data, DRM_HDCP_DDC_BCAPS, &val, 1);
+
+	if (ret)
+		return ret;
+	if (val & mask)
+		return 0;
+
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi);
+
+/* Read the KSV FIFO of the HDCP receive of DP.
+ * Read via 15 byte window (3 entries @ 5 bytes each)
+ *
+ * @fifo: buffer to store the KSV FIFO
+ * @num_downstream: number of devices in the KSV FIFO
+ */
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_dp(struct drm_hdcp_helper_data *data,
+					   u8 *fifo, u8 num_downstream)
+{
+	struct drm_device *dev = data->connector->dev;
+	int i;
+
+	for (i = 0; i < num_downstream; i += 3) {
+		size_t len = min(num_downstream - i, 3) * DRM_HDCP_KSV_LEN;
+		int ret = drm_hdcp_remote_dpcd_read(data, DP_AUX_HDCP_KSV_FIFO,
+						    fifo + i * DRM_HDCP_KSV_LEN,
+						    len);
+		if (ret) {
+			drm_err(dev, "Read ksv[%d] from DP/AUX failed (%d)\n",
+				i, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_read_ksv_fifo_dp);
+
+/* Read the KSV FIFO of the HDCP receive of HDMI.
+ * Over HDMI, read the whole thing at once.
+ *
+ * @fifo: buffer to store the KSV FIFO
+ * @num_downstream: number of devices in the KSV FIFO
+ */
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi(struct drm_hdcp_helper_data *data,
+					     u8 *fifo, u8 num_downstream)
+{
+	struct drm_device *dev = data->connector->dev;
+
+	int ret = drm_hdcp_remote_ddc_read(data, DRM_HDCP_DDC_KSV_FIFO, fifo,
+					   num_downstream * DRM_HDCP_KSV_LEN);
+	if (ret)
+		drm_err(dev, "DDC ksv fifo read failed (%d)\n", ret);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi);
+
+static int drm_hdcp_helper_hdcp1_read_v_prime(struct drm_hdcp_helper_data *data,
+					      u32 *v_prime)
+{
+	struct drm_device *dev = data->connector->dev;
+	int ret, i;
+
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
+		ret = data->funcs->remote_read(data, data->hdcp1_lut->v[i],
+					       (u8 *)&v_prime[i],
+					       DRM_HDCP_V_PRIME_PART_LEN);
+		if (ret) {
+			drm_dbg_kms(dev, "Read v'[%d] from failed (%d)\n", i,
+				    ret);
+			return ret >= 0 ? -EIO : ret;
+		}
+	}
+	return 0;
+}
+
+static int
+drm_hdcp_helper_hdcp1_authenticate_downstream(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	u32 v_prime[DRM_HDCP_V_PRIME_NUM_PARTS];
+	u8 bstatus[DRM_HDCP_BSTATUS_LEN];
+	u8 num_downstream, *ksv_fifo;
+	int ret, i, tries = 3;
+
+	ret = read_poll_timeout(data->funcs->hdcp1_ksv_fifo_ready, ret, !ret,
+				10 * 1000, 5 * 1000 * 1000, false, data);
+	if (ret) {
+		drm_err(dev, "Failed to poll ksv ready, %d\n", ret);
+		return ret;
+	}
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->bstatus, bstatus,
+				       DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		return ret;
+
+	/*
+	 * When repeater reports 0 device count, HDCP1.4 spec allows disabling
+	 * the HDCP encryption. That implies that repeater can't have its own
+	 * display. As there is no consumption of encrypted content in the
+	 * repeater with 0 downstream devices, we are failing the
+	 * authentication.
+	 */
+	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
+	if (num_downstream == 0) {
+		drm_err(dev, "Repeater with zero downstream devices, %*ph\n",
+			DRM_HDCP_BSTATUS_LEN, bstatus);
+		return -EINVAL;
+	}
+
+	ksv_fifo = kcalloc(DRM_HDCP_KSV_LEN, num_downstream, GFP_KERNEL);
+	if (!ksv_fifo)
+		return -ENOMEM;
+
+	ret = data->funcs->hdcp1_read_ksv_fifo(data, ksv_fifo, num_downstream);
+	if (ret) {
+		drm_err(dev, "Failed to read ksv fifo, %d/%d\n", num_downstream,
+			ret);
+		goto out;
+	}
+
+	if (drm_hdcp_check_ksvs_revoked(dev, ksv_fifo, num_downstream)) {
+		drm_err(dev, "Revoked Ksv(s) in ksv_fifo\n");
+		ret = -EPERM;
+		goto out;
+	}
+
+	/*
+	 * When V prime mismatches, DP Spec mandates re-read of
+	 * V prime at least twice.
+	 */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_read_v_prime(data, v_prime);
+		if (ret)
+			continue;
+
+		ret = data->funcs->hdcp1_store_ksv_fifo(connector, ksv_fifo,
+							num_downstream, bstatus,
+							v_prime,
+							data->driver_data);
+		if (!ret)
+			break;
+	}
+	if (ret)
+		drm_err(dev, "Could not validate KSV FIFO with V' %d\n", ret);
+
+out:
+	if (!ret)
+		drm_dbg_kms(dev, "HDCP is enabled (%d downstream devices)\n",
+			    num_downstream);
+
+	kfree(ksv_fifo);
+	return ret;
+}
+
+static int drm_hdcp_helper_hdcp1_validate_ri(struct drm_hdcp_helper_data *data)
+{
+	union {
+		u32 word;
+		u8 bytes[DRM_HDCP_RI_LEN];
+	} ri_prime = { .word = 0 };
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->ri,
+				       ri_prime.bytes, DRM_HDCP_RI_LEN);
+	if (ret) {
+		drm_err(dev, "Failed to read R0' %d\n", ret);
+		return ret;
+	}
+
+	return data->funcs->hdcp1_match_ri(connector, ri_prime.word,
+					   data->driver_data);
+}
+
+/* 
+ * Wait for the DP receiver to set the R0' ready bit in the Bstatus register.
+ * This bit is set to 1 when the receiver has received and validated the
+ * receiver ID list and receiver capability list.
+ * The receiver must set this bit to 1 within 200 ms of the receiver ID
+ * list and receiver capability list being received.
+ *
+ * @data: DRM HDCP helper data structure
+ * @r0_prime_remaining_us: timeout for the R0' ready bit
+ *
+ * Return: 0 on success, error code on failure
+ */
+int drm_hdcp_helper_wait_r0_dp(struct drm_hdcp_helper_data *data,
+			       unsigned long r0_prime_remaining_us)
+{
+	u8 val;
+	int ret = 0;
+
+	ret = read_poll_timeout(drm_hdcp_remote_dpcd_read, ret,
+				!ret && (val & DP_BSTATUS_R0_PRIME_READY), 1000,
+				r0_prime_remaining_us, false, data,
+				DP_AUX_HDCP_BSTATUS, &val, 1);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_wait_r0_dp);
+
+/*
+ * drm_hdcp_helper_wait_r0_hdmi - Wait for R0 to be ready on HDMI
+ * @data: DRM HDCP helper data
+ * @r0_prime_remaining_us: Remaining time to wait for R0 in microseconds
+ *
+ * HDMI doesn't have a R0_READY bit, so we just have to wait the remainder of the
+ * 300 ms timeout.
+ *
+ * Returns:
+ * 0 on success, negative error code on failure
+ */
+
+int drm_hdcp_helper_wait_r0_hdmi(struct drm_hdcp_helper_data *data,
+				 unsigned long r0_prime_remaining_us)
+{
+	usleep_range(r0_prime_remaining_us, r0_prime_remaining_us + 1000);
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_wait_r0_hdmi);
+
+static int drm_hdcp_helper_hdcp1_authenticate(struct drm_hdcp_helper_data *data)
+{
+	union {
+		u32 word;
+		u8 bytes[DRM_HDCP_BSTATUS_LEN];
+	} bstatus;
+	const struct drm_hdcp_helper_funcs *funcs = data->funcs;
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	unsigned long r0_prime_timeout, r0_prime_remaining_us = 0, tmp_jiffies;
+	struct drm_hdcp_ksv aksv;
+	struct drm_hdcp_ksv bksv;
+	struct drm_hdcp_an an;
+	bool repeater_present;
+	int ret, i, tries = 3;
+	u8 bcaps;
+
+	if (funcs->hdcp1_read_an_aksv) {
+		ret = funcs->hdcp1_read_an_aksv(connector, an.words, aksv.words,
+						data->driver_data);
+		if (ret) {
+			drm_err(dev, "Failed to read An/Aksv values, %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = funcs->remote_write(data, data->hdcp1_lut->an, an.bytes,
+					  DRM_HDCP_AN_LEN);
+		if (ret) {
+			drm_err(dev, "Failed to write An to receiver, %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = funcs->remote_write(data, data->hdcp1_lut->aksv,
+					  aksv.bytes, DRM_HDCP_KSV_LEN);
+		if (ret) {
+			drm_err(dev, "Failed to write Aksv to receiver, %d\n",
+				ret);
+			return ret;
+		}
+	} else {
+		ret = funcs->hdcp1_send_an_aksv(connector, data->driver_data);
+		if (ret) {
+			drm_err(dev, "Failed to read An/Aksv values, %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Timeout for R0' to become available. The spec says 100ms from Aksv,
+	 * but some monitors can take longer than this. We'll set the timeout at
+	 * 300ms just to be sure.
+	 */
+	r0_prime_timeout = jiffies + msecs_to_jiffies(300);
+
+	memset(&bksv, 0, sizeof(bksv));
+
+	ret = drm_hdcp_read_valid_bksv(data, &bksv);
+	if (ret < 0)
+		return ret;
+
+	if (drm_hdcp_check_ksvs_revoked(dev, bksv.bytes, 1)) {
+		drm_err(dev, "BKSV is revoked\n");
+		return -EPERM;
+	}
+
+	ret = funcs->remote_read(data, data->hdcp1_lut->bcaps, &bcaps, 1);
+	if (ret)
+		return ret;
+
+	memset(&bstatus, 0, sizeof(bstatus));
+
+	ret = funcs->remote_read(data, data->hdcp1_lut->bstatus, bstatus.bytes,
+				 DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		return ret;
+
+	if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus.bytes[0]) ||
+	    DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus.bytes[1])) {
+		drm_err(dev, "Max Topology Limit Exceeded, bstatus=%*ph\n",
+			DRM_HDCP_BSTATUS_LEN, bstatus.bytes);
+		return -EPERM;
+	}
+
+	repeater_present = bcaps & data->hdcp1_lut->bcaps_mask_repeater_present;
+
+	ret = funcs->hdcp1_store_receiver_info(connector, bksv.words,
+					       bstatus.word, bcaps,
+					       repeater_present,
+					       data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to store bksv, %d\n", ret);
+		return ret;
+	}
+
+	ret = funcs->hdcp1_enable_encryption(connector, data->driver_data);
+	if (ret)
+		return ret;
+
+	ret = funcs->hdcp1_wait_for_r0(connector, data->driver_data);
+	if (ret)
+		return ret;
+
+	tmp_jiffies = jiffies;
+	if (time_before(tmp_jiffies, r0_prime_timeout))
+		r0_prime_remaining_us =
+			jiffies_to_usecs(r0_prime_timeout - tmp_jiffies);
+
+	ret = funcs->wait_r0(data, r0_prime_remaining_us);
+	if (ret) {
+		drm_err(dev, "R0' did not become ready %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * DP HDCP Spec mandates the two more reattempt to read R0, in case
+	 * of R0 mismatch.
+	 */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_validate_ri(data);
+		if (!ret)
+			break;
+	}
+	if (ret) {
+		drm_err(dev, "Failed to match R0/R0', aborting HDCP %d\n", ret);
+		return ret;
+	}
+
+	if (repeater_present)
+		return drm_hdcp_helper_hdcp1_authenticate_downstream(data);
+
+	drm_dbg_kms(dev, "HDCP is enabled (no repeater present)\n");
+	return 0;
+}
+
+static int drm_hdcp_helper_hdcp1_enable(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int i, ret, tries = 3;
+
+	drm_dbg_kms(dev, "[%s:%d] HDCP is being enabled...\n", connector->name,
+		    connector->base.id);
+
+	/* In case of authentication failures, HDCP spec expects reauth. */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_authenticate(data);
+		if (!ret)
+			return 0;
+
+		drm_dbg_kms(dev, "HDCP Auth failure (%d)\n", ret);
+
+		/* Ensuring HDCP encryption and signalling are stopped. */
+		data->funcs->hdcp1_disable(data->connector, data->driver_data);
+	}
+
+	drm_err(dev, "HDCP authentication failed (%d tries/%d)\n", tries, ret);
+	return ret;
+}
+
+static inline void
+drm_hdcp_helper_driver_lock(struct drm_hdcp_helper_data *data)
+{
+	if (data->driver_mutex)
+		mutex_lock(data->driver_mutex);
+}
+
+static inline void
+drm_hdcp_helper_driver_unlock(struct drm_hdcp_helper_data *data)
+{
+	if (data->driver_mutex)
+		mutex_unlock(data->driver_mutex);
+}
+
+static int drm_hdcp_helper_enable_hdcp(struct drm_hdcp_helper_data *data,
+				       struct drm_atomic_state *state,
+				       struct mutex *driver_mutex)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_connector_state *conn_state;
+	struct drm_device *dev = connector->dev;
+	unsigned long check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
+	bool capable;
+	int ret = 0;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+
+	mutex_lock(&data->mutex);
+
+	if (data->value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_ENABLED,
+				      true);
+		goto out_data_mutex;
+	}
+
+	drm_WARN_ON(dev, data->driver_mutex != NULL);
+	data->driver_mutex = driver_mutex;
+
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->funcs->setup) {
+		ret = data->funcs->setup(connector, state, data->driver_data);
+		if (ret) {
+			drm_err(dev, "Failed to setup HDCP %d\n", ret);
+			goto out;
+		}
+	}
+
+	if (!data->funcs->are_keys_valid ||
+	    !data->funcs->are_keys_valid(connector, data->driver_data)) {
+		if (data->funcs->load_keys) {
+			ret = data->funcs->load_keys(connector,
+						     data->driver_data);
+			if (ret) {
+				drm_err(dev, "Failed to load HDCP keys %d\n",
+					ret);
+				goto out;
+			}
+		}
+	}
+
+	/*
+	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
+	 * is capable of HDCP2.2, it is preferred to use HDCP2.2.
+	 */
+	ret = data->funcs->hdcp2_capable(connector, &capable,
+					 data->driver_data);
+	if (ret)
+		drm_err(dev, "HDCP 2.x capability check failed %d\n", ret);
+
+	if (!ret && capable) {
+		if (conn_state->content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+			data->enabled_type = DRM_MODE_HDCP_CONTENT_TYPE1;
+		} else {
+			data->enabled_type = DRM_MODE_HDCP_CONTENT_TYPE0;
+		}
+
+		ret = data->funcs->hdcp2_enable(connector, data->driver_data);
+		if (!ret) {
+			check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
+			goto out;
+		}
+	}
+
+	/*
+	 * When HDCP2.2 fails and Content Type is not Type1, HDCP1.4 will
+	 * be attempted.
+	 */
+	ret = data->funcs->hdcp1_capable(data, &capable);
+	if (ret) {
+		drm_err(dev, "HDCP 1.x capability check failed %d\n", ret);
+		goto out;
+	}
+	if (capable &&
+	    conn_state->content_type != DRM_MODE_HDCP_CONTENT_TYPE1) {
+		data->enabled_type = DRM_MODE_HDCP_CONTENT_TYPE0;
+		ret = drm_hdcp_helper_hdcp1_enable(data);
+		if (!ret)
+			check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
+	}
+
+out:
+	if (!ret) {
+		schedule_delayed_work(&data->check_work, check_link_interval);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_ENABLED,
+				      true);
+	}
+
+	drm_hdcp_helper_driver_unlock(data);
+	if (ret)
+		data->driver_mutex = NULL;
+
+out_data_mutex:
+	mutex_unlock(&data->mutex);
+	return ret;
+}
+
+static int drm_hdcp_helper_disable_hdcp(struct drm_hdcp_helper_data *data)
+{
+	int ret = 0;
+
+	mutex_lock(&data->mutex);
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		goto out;
+
+	drm_dbg_kms(data->connector->dev, "[%s:%d] HDCP is being disabled...\n",
+		    data->connector->name, data->connector->base.id);
+
+	drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_UNDESIRED,
+			      true);
+
+	if (data->enabled_type == DRM_MODE_HDCP_CONTENT_TYPE1)
+		ret = data->funcs->hdcp2_disable(data->connector,
+						 data->driver_data);
+	else
+		ret = data->funcs->hdcp1_disable(data->connector,
+						 data->driver_data);
+
+	drm_dbg_kms(data->connector->dev, "HDCP is disabled\n");
+
+out:
+	drm_hdcp_helper_driver_unlock(data);
+	data->driver_mutex = NULL;
+	mutex_unlock(&data->mutex);
+	cancel_delayed_work_sync(&data->check_work);
+	return ret;
+}
+
+/**
+ * drm_hdcp_helper_atomic_commit - Helper for drivers to call during commit to
+ * enable/disable HDCP
+ *
+ * @data: pointer to the @drm_hdcp_helper_data for the connector
+ * @state: pointer to the atomic state being committed
+ * @driver_mutex: driver-provided lock to be used while interacting with the driver
+ *
+ * This function can be used by display drivers to determine when HDCP should be
+ * enabled or disabled based on the connector state. It should be called during
+ * steady-state commits as well as connector enable/disable. The function will
+ * handle the HDCP authentication/encryption logic, calling back into the driver
+ * when source operations are necessary.
+ *
+ * @driver_mutex will be retained and used for the duration of the HDCP session
+ * since it will be needed for link checks and retries. This mutex is useful if
+ * the driver has shared resources across connectors which must be serialized.
+ * For example, driver_mutex can be used for MST connectors sharing a common
+ * encoder which should not be accessed/changed concurrently. When the
+ * connector's session is torn down, the mutex will be forgotten by the helper
+ * for this connector until the next session.
+ */
+void drm_hdcp_helper_atomic_commit(struct drm_hdcp_helper_data *data,
+				   struct drm_atomic_state *state,
+				   struct mutex *driver_mutex)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_connector_state *conn_state;
+	bool type_changed;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!conn_state)
+		return;
+
+	type_changed = conn_state->hdcp_content_type != data->enabled_type;
+
+	if (conn_state->content_protection ==
+	    DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+		drm_hdcp_helper_disable_hdcp(data);
+		return;
+	}
+
+	if (!conn_state->crtc) {
+		drm_hdcp_helper_disable_hdcp(data);
+
+		/* Restore property to DESIRED so it's retried later */
+		if (conn_state->content_protection ==
+		    DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+			mutex_lock(&data->mutex);
+			drm_hdcp_update_value(
+				data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				true);
+			mutex_unlock(&data->mutex);
+		}
+		return;
+	}
+
+	/* Already enabled */
+	if (conn_state->content_protection ==
+	    DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		return;
+
+	/* Disable and re-enable HDCP on content type change */
+	if (type_changed)
+		drm_hdcp_helper_disable_hdcp(data);
+
+	drm_hdcp_helper_enable_hdcp(data, state, driver_mutex);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_atomic_commit);
+
+static void drm_hdcp_helper_prop_work(struct work_struct *work)
+{
+	struct drm_hdcp_helper_data *data =
+		container_of(work, struct drm_hdcp_helper_data, prop_work);
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	mutex_lock(&data->mutex);
+
+	/*
+	 * This worker is only used to flip between ENABLED/DESIRED. Either of
+	 * those to UNDESIRED is handled by core. If value == UNDESIRED,
+	 * we're running just after hdcp has been disabled, so just exit
+	 */
+	if (data->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		drm_hdcp_update_content_protection(connector, data->value);
+
+	mutex_unlock(&data->mutex);
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+}
+
+/*
+ * Check the link registers for HDCP 1.x for DP.
+ */
+int drm_hdcp_hdcp1_check_link_registers_dp(struct drm_device *dev,
+					   struct drm_hdcp_helper_data *data,
+					   bool *shoud_retry)
+{
+	u8 bstatus;
+	int ret = drm_hdcp_remote_dpcd_read(data, DP_AUX_HDCP_BSTATUS, &bstatus,
+					    1);
+	*shoud_retry = true;
+
+	if (ret) {
+		*shoud_retry = false;
+		drm_err(dev, "Failed to read dpcd bstatus, %d\n", ret);
+		return ret;
+	}
+	if (bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ))
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_hdcp1_check_link_registers_dp);
+
+/*
+ * Check the link registers for HDCP 1.x for HDMI.
+ */
+int drm_hdcp_hdcp1_check_link_registers_hdmi(struct drm_device *dev,
+					     struct drm_hdcp_helper_data *data,
+					     bool *shoud_retry)
+{
+	int ret = drm_hdcp_helper_hdcp1_validate_ri(data);
+	*shoud_retry = true;
+
+	if (ret)
+		drm_err(dev, "Ri' mismatch, check failed (%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_hdcp1_check_link_registers_hdmi);
+
+static int drm_hdcp_hdcp1_check_link(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+	bool should_retry = false;
+
+	if (data->funcs->hdcp1_check_link) {
+		ret = data->funcs->hdcp1_check_link(connector,
+						    data->driver_data);
+		if (ret)
+			goto retry;
+	}
+
+	ret = data->funcs->hdcp1_check_link_registers(connector->dev, data,
+						      &should_retry);
+	if (!ret)
+		return 0;
+	else if (!should_retry)
+		return ret;
+
+retry:
+	drm_err(dev, "[%s:%d] HDCP link failed, retrying authentication\n",
+		connector->name, connector->base.id);
+
+	ret = data->funcs->hdcp1_disable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to disable hdcp (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	ret = drm_hdcp_helper_hdcp1_enable(data);
+	if (ret) {
+		drm_err(dev, "Failed to enable hdcp (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int drm_hdcp_hdcp2_check_link(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+
+	ret = data->funcs->hdcp2_check_link(connector, data->driver_data);
+	if (!ret)
+		return 0;
+
+	drm_err(dev, "[%s:%d] HDCP2 link failed, retrying authentication\n",
+		connector->name, connector->base.id);
+
+	ret = data->funcs->hdcp2_disable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to disable hdcp2 (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	ret = data->funcs->hdcp2_enable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to enable hdcp2 (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void drm_hdcp_helper_check_work(struct work_struct *work)
+{
+	struct drm_hdcp_helper_data *data = container_of(
+		to_delayed_work(work), struct drm_hdcp_helper_data, check_work);
+	unsigned long check_link_interval;
+
+	mutex_lock(&data->mutex);
+	if (data->value != DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		goto out_data_mutex;
+
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->enabled_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+		if (drm_hdcp_hdcp2_check_link(data))
+			goto out;
+		check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
+	} else {
+		if (drm_hdcp_hdcp1_check_link(data))
+			goto out;
+		check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
+	}
+	schedule_delayed_work(&data->check_work, check_link_interval);
+
+out:
+	drm_hdcp_helper_driver_unlock(data);
+out_data_mutex:
+	mutex_unlock(&data->mutex);
+}
+
+/**
+ * drm_hdcp_helper_schedule_hdcp_check - Schedule a check link cycle.
+ *
+ * @data: Pointer to the HDCP helper data.
+ *
+ * This function will kick off a check link cycle on behalf of the caller. This
+ * can be used by DP short hpd interrupt handlers, where the driver must poke
+ * the helper to check the link is still valid.
+ */
+void drm_hdcp_helper_schedule_hdcp_check(struct drm_hdcp_helper_data *data)
+{
+	schedule_delayed_work(&data->check_work, 0);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_schedule_hdcp_check);
+
+static struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize(struct drm_connector *connector,
+			   const struct drm_hdcp_helper_funcs *funcs,
+			   bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+	int ret;
+
+	out = kzalloc(sizeof(*out), GFP_KERNEL);
+	if (!out)
+		return ERR_PTR(-ENOMEM);
+
+	out->connector = connector;
+	out->funcs = funcs;
+
+	mutex_init(&out->mutex);
+	out->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+
+	INIT_DELAYED_WORK(&out->check_work, drm_hdcp_helper_check_work);
+	INIT_WORK(&out->prop_work, drm_hdcp_helper_prop_work);
+
+	ret = drm_connector_attach_content_protection_property(
+		connector, hdcp_content_type);
+	if (ret) {
+		drm_hdcp_helper_destroy(out);
+		return ERR_PTR(ret);
+	}
+
+	return out;
+}
+
+/**
+ * drm_hdcp_helper_initialize_dp - Initializes the HDCP helpers for a
+ * DisplayPort connector
+ *
+ * @connector: pointer to the DisplayPort connector.
+ * @funcs: pointer to the vtable of HDCP helper funcs for this connector.
+ * @hdcp_content_type: True if the content_type property should be attached.
+ *
+ * This function initializes the HDCP helper for the given DisplayPort connector.
+ * This involves creating the Content Protection property as well as the Content
+ * Type property (if desired). Upon success, it will return a pointer to the
+ * HDCP helper data. Ownership of the underlying memory is ttransferredto the
+ * caller and should be freed using drm_hdcp_helper_destroy().
+ *
+ * Returns:
+ * Pointer to newly created HDCP helper data. PTR_ERR on failure.
+ */
+struct drm_hdcp_helper_data *drm_hdcp_helper_initialize_dp(
+	struct drm_connector *connector, struct drm_dp_aux *aux,
+	const struct drm_hdcp_helper_funcs *funcs, bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+
+	out = drm_hdcp_helper_initialize(connector, funcs, hdcp_content_type);
+	if (IS_ERR(out))
+		return out;
+
+	out->aux = aux;
+	out->hdcp1_lut = &drm_hdcp_hdcp1_dpcd_lut;
+
+	return out;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_initialize_dp);
+
+/**
+ * drm_hdcp_helper_initialize_hdmi - Initializes the HDCP helpers for an HDMI
+ * connector
+ *
+ * @connector: pointer to the HDMI connector.
+ * @funcs: pointer to the vtable of HDCP helper funcs for this connector.
+ * @hdcp_content_type: True if the content_type property should be attached.
+ *
+ * This function initializes the HDCP helper for the given HDMI connector. This
+ * involves creating the Content Protection property as well as the Content Type
+ * property (if desired). Upon success, it will return a pointer to the HDCP
+ * helper data. Ownership of the underlying memory is transferred to the caller
+ * and should be freed using drm_hdcp_helper_destroy().
+ *
+ * Returns:
+ * Pointer to newly created HDCP helper data. PTR_ERR on failure.
+ */
+struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize_hdmi(struct drm_connector *connector,
+				const struct drm_hdcp_helper_funcs *funcs,
+				bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+
+	out = drm_hdcp_helper_initialize(connector, funcs, hdcp_content_type);
+	if (IS_ERR(out))
+		return out;
+
+	out->hdcp1_lut = &drm_hdcp_hdcp1_ddc_lut;
+
+	return out;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_initialize_hdmi);
+
+/**
+ * drm_hdcp_helper_destroy - Destroys the given HDCP helper data.
+ *
+ * @data: Pointer to the HDCP helper data.
+ *
+ * This function cleans up and destroys the HDCP helper data created by
+ * drm_hdcp_helper_initialize_dp() or drm_hdcp_helper_initialize_hdmi().
+ */
+void drm_hdcp_helper_destroy(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector;
+
+	if (!data)
+		return;
+
+	connector = data->connector;
+
+	/*
+	 * If the connector is registered, it's possible userspace could kick
+	 * off another HDCP enable, which would re-spawn the workers.
+	 */
+	drm_WARN_ON(connector->dev,
+		    connector->registration_state == DRM_CONNECTOR_REGISTERED);
+
+	/*
+	 * Now that the connector is not registered, check_work won't be run,
+	 * but cancel any outstanding instances of it
+	 */
+	cancel_delayed_work_sync(&data->check_work);
+
+	/*
+	 * We don't cancel prop_work in the same way as check_work since it
+	 * requires connection_mutex which could be held while calling this
+	 * function. Instead, we rely on the connector references grabbed before
+	 * scheduling prop_work to ensure the connector is alive when prop_work
+	 * is run. So if we're in the destroy path (which is where this
+	 * function should be called), we're "guaranteed" that prop_work is not
+	 * active (tl;dr This Should Never Happen).
+	 */
+	drm_WARN_ON(connector->dev, work_pending(&data->prop_work));
+
+	kfree(data);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_destroy);
include/drm/display/drm_hdcp.h +287 -0

311 lines not on this page · raw message

include/drm/display/drm_hdcp_helper.h +50 -1

61 lines not on this page · raw message

Signature · 1 line
2.40.0.348.gf938b09366-goog
#5 lore raw

Re: [PATCH v8 04/10] drm/hdcp: Expand HDCP helper library for enable/disable/check

I love your patch! Perhaps something to improve:

Hi Mark,

I love your patch! Perhaps something to improve:

[auto build test WARNING on drm-intel/for-linux-next-fixes]
[also build test WARNING on drm/drm-next linus/master v6.3-rc4 next-20230331] [cannot apply to drm-misc/drm-misc-next drm-intel/for-linux-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mark-Yacoub/drm-hdcp-Add-drm_hdcp_atomic_check/20230401-061425
base:   git://anongit.freedesktop.org/drm-intel for-linux-next-fixes
patch link:    https://lore.kernel.org/r/20230331221213.1691997-5-markyacoub%40google.com
patch subject: [PATCH v8 04/10] drm/hdcp: Expand HDCP helper library for enable/disable/check
config: riscv-allmodconfig (https://download.01.org/0day-ci/archive/20230401/[email protected]/config)
compiler: riscv64-linux-gcc (GCC) 12.1.0

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/82a092e7e090cdeb3ff18498e6ad671906268631
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Mark-Yacoub/drm-hdcp-Add-drm_hdcp_atomic_check/20230401-061425
git checkout 82a092e7e090cdeb3ff18498e6ad671906268631
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=riscv olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=riscv SHELL=/bin/bash drivers/gpu/drm/display/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/display/drm_hdcp_helper.c:719: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst

* Check if the sink is capable of HDCP 1.x. DisplayPort has a dedicated bit
drivers/gpu/drm/display/drm_hdcp_helper.c:742: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst
* Check if the sink is capable of HDCP 1.x. HDMI spec states that transmitters
drivers/gpu/drm/display/drm_hdcp_helper.c:1633: warning: Function parameter or member 'aux' not described in 'drm_hdcp_helper_initialize_dp'

vim +719 drivers/gpu/drm/display/drm_hdcp_helper.c

717
718 /**

 > 719	 * Check if the sink is capable of HDCP 1.x. DisplayPort has a dedicated bit

720 * for this in DPCD.
721 *
722 * @data: pointer to the HDCP helper data.
723 * @capable: pointer to a bool which will contain true if the sink is capable.
724 *
725 * Returns:
726 * -errno if the transacation between source and sink fails.
727 */
728 int drm_hdcp_helper_hdcp1_capable_dp(struct drm_hdcp_helper_data *data,
729 bool *capable)
730 {
731 int ret;
732 u8 bcaps;
733
734 ret = data->funcs->remote_read(data, data->hdcp1_lut->bcaps, &bcaps, 1);
735 *capable = !ret && (bcaps & DP_BCAPS_HDCP_CAPABLE);
736
737 return 0;
738 }
739 EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_capable_dp);
740

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
#6 lore raw

RE: [PATCH v8 04/10] drm/hdcp: Expand HDCP helper library for enable/disable/check

In reply to Mark Yacoub
Quoted 881 lines

-----Original Message-----
From: Mark Yacoub <[email protected]>
Sent: Saturday, April 1, 2023 3:42 AM
To: David Airlie <[email protected]>; Daniel Vetter <[email protected]>
Cc: [email protected]; Kandpal, Suraj <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; Nikula, Jani <[email protected]>; Mark Yacoub <[email protected]>; linux- [email protected]
Subject: [PATCH v8 04/10] drm/hdcp: Expand HDCP helper library for enable/disable/check

From: Sean Paul <[email protected]>

Expand upon the HDCP helper library to manage HDCP enable, disable, and check.

Previous to this patch, the majority of the state management and sink interaction is tucked inside the Intel driver with the understanding that once a new platform supported HDCP we could make good decisions about what should be centralized. With the addition of HDCP support for Qualcomm, it's time to migrate the protocol-specific bits of HDCP authentication, key exchange, and link checks to the HDCP helper.

In terms of functionality, this migration is 1:1 with the Intel driver, however things are laid out a bit differently than with intel_hdcp.c, which is why this is a separate patch from the i915 transition to the helper. On i915, the shim vtable is used to account for HDMI vs. DP vs. DP-MST differences whereas the helper library uses a LUT to account for the register offsets and a remote read function to route the messages. On i915, storing the sink information in the source is done inline whereas now we use the new drm_hdcp_helper_funcs vtable to store and fetch information to/from source hw. Finally, instead of calling enable/disable directly from the driver, we'll leave that decision to the helper and by calling drm_hdcp_helper_atomic_commit() from the driver. All told, this will centralize the protocol and state handling in the helper, ensuring we collect all of our bugs^Wlogic in one place.

---
Changes in v2:
-Fixed set-but-unused variable identified by 0-day Changes in v3:
-Fixed uninitialized variable warning identified by 0-day Changes in v4:
-None
Changes in v5:
-None
Changes in v6:

(quoted diff) +0 -2
-Fixed typo in function descriptions
-Rebased: Moved the new code between drm_hdcp.h and

drm_hdcp_helper.c/h
-Add missing headers. Reported-by: kernel test robot <[email protected]> Changes in v7:
- Add a |driver_data| field to some functions in drm_hdcp_helper_funcs that are called by the driver so drivers can pass anything they such as bridges
- Isolate all non-common code between HDMI and DP into separate functions instead of manually checking for datra->aux
Changes in v8 (suraj):

(quoted diff) +0 -2
-Try hdcp 1.x if hdcp2_capable returns an error instead of goto out.
-set the enabled type to either 1 or 0 depending on the prop as hdcp2

can support either.

 drivers/gpu/drm/display/drm_hdcp_helper.c | 1215
+++++++++++++++++++++
 include/drm/display/drm_hdcp.h            |  287 +++++
 include/drm/display/drm_hdcp_helper.h     |   51 +-
 3 files changed, 1552 insertions(+), 1 deletion(-)
drivers/gpu/drm/display/drm_hdcp_helper.c +19 -0
index 3ee1a6ae26c53..3bc0308cc95d8 100644
@@ -6,13 +6,18 @@
  * Ramalingam C <[email protected]>
  */
 
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/gfp.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/firmware.h>
+#include <linux/workqueue.h>
 
+#include <drm/display/drm_dp_helper.h>
 #include <drm/display/drm_hdcp_helper.h>
 #include <drm/drm_sysfs.h>
 #include <drm/drm_print.h>
@@ -507,3 +512,1213 @@ bool drm_hdcp_has_changed(struct
 drm_connector *connector,
 	return old_hdcp != new_hdcp;
 }
 EXPORT_SYMBOL(drm_hdcp_has_changed);
+
+struct drm_hdcp_hdcp1_receiver_reg_lut {
+	unsigned int bksv;
+	unsigned int ri;
+	unsigned int aksv;
+	unsigned int an;
+	unsigned int ainfo;
+	unsigned int v[5];
+	unsigned int bcaps;
+	unsigned int bcaps_mask_repeater_present;
+	unsigned int bstatus;
+};
+
+static const struct drm_hdcp_hdcp1_receiver_reg_lut

drm_hdcp_hdcp1_ddc_lut = {

drivers/gpu/drm/display/drm_hdcp_helper.c +10 -0
+	.bksv = DRM_HDCP_DDC_BKSV,
+	.ri = DRM_HDCP_DDC_RI_PRIME,
+	.aksv = DRM_HDCP_DDC_AKSV,
+	.an = DRM_HDCP_DDC_AN,
+	.ainfo = DRM_HDCP_DDC_AINFO,
+	.v = { DRM_HDCP_DDC_V_PRIME(0), DRM_HDCP_DDC_V_PRIME(1),
+	       DRM_HDCP_DDC_V_PRIME(2), DRM_HDCP_DDC_V_PRIME(3),
+	       DRM_HDCP_DDC_V_PRIME(4) },
+	.bcaps = DRM_HDCP_DDC_BCAPS,
+	.bcaps_mask_repeater_present =

DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT,

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+	.bstatus = DRM_HDCP_DDC_BSTATUS,
+};
+
+static const struct drm_hdcp_hdcp1_receiver_reg_lut

drm_hdcp_hdcp1_dpcd_lut = {

drivers/gpu/drm/display/drm_hdcp_helper.c +14 -0
+	.bksv = DP_AUX_HDCP_BKSV,
+	.ri = DP_AUX_HDCP_RI_PRIME,
+	.aksv = DP_AUX_HDCP_AKSV,
+	.an = DP_AUX_HDCP_AN,
+	.ainfo = DP_AUX_HDCP_AINFO,
+	.v = { DP_AUX_HDCP_V_PRIME(0), DP_AUX_HDCP_V_PRIME(1),
+	       DP_AUX_HDCP_V_PRIME(2), DP_AUX_HDCP_V_PRIME(3),
+	       DP_AUX_HDCP_V_PRIME(4) },
+	.bcaps = DP_AUX_HDCP_BCAPS,
+	.bcaps_mask_repeater_present = DP_BCAPS_REPEATER_PRESENT,
+
+	/*
+	 * For some reason the HDMI and DP HDCP specs call this register
+	 * definition by different names. In the HDMI spec, it's called

BSTATUS,

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+	 * but in DP it's called BINFO.
+	 */
+	.bstatus = DP_AUX_HDCP_BINFO,
+};
+
+/*
+ * Read a DPCD register.
+ *
+ * @data: drm_hdcp_helper_data containing the DisplayPort AUX channel

(SST or MST)
+ * @offset: address of the (first) register to read
+ * @value: buffer to store the register values
+ * @len: number of bytes in @value
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */

drivers/gpu/drm/display/drm_hdcp_helper.c +53 -0
+int drm_hdcp_remote_dpcd_read(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *value, size_t len)
+{
+	ssize_t ret;
+	struct drm_dp_aux *aux = data->aux;
+
+	ret = drm_dp_dpcd_read(aux, offset, value, len);
+	if (ret != len) {
+		if (ret >= 0)
+			return -EIO;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_dpcd_read);
+
+/*
+ * Read a value from a remote DDC bus.
+ *
+ * @data: drm_hdcp_helper_data containing the i2c_adapter.
+ * @offset: Offset of the value to read.
+ * @value: Pointer to the value to read.
+ * @len: Length of the value to read.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+
+int drm_hdcp_remote_ddc_read(struct drm_hdcp_helper_data *data,
+			     unsigned int offset, u8 *value, size_t len)
+{
+	int ret;
+	struct i2c_adapter *i2c = data->connector->ddc;
+	u8 start = offset & 0xff;
+	struct i2c_msg msgs[] = { { .addr = DRM_HDCP_DDC_ADDR,
+				    .flags = 0,
+				    .len = 1,
+				    .buf = &start },
+				  { .addr = DRM_HDCP_DDC_ADDR,
+				    .flags = I2C_M_RD,
+				    .len = len,
+				    .buf = value } };
+	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
+	if (ret == ARRAY_SIZE(msgs))
+		return 0;
+	return ret >= 0 ? -EIO : ret;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_ddc_read);
+
+/*
+ * Write to a DPCD register.
+ *
+ * @data: drm_hdcp_helper_data containing the DisplayPort AUX channel

(SST or MST)
+ * @offset: Offset of the DPCD register to read from
+ * @value: Pointer to store the DPCD register data
+ * @len: Number of bytes to read
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure
+ */

drivers/gpu/drm/display/drm_hdcp_helper.c +70 -0
+int drm_hdcp_remote_dpcd_write(struct drm_hdcp_helper_data *data,
+			       unsigned int offset, u8 *value, size_t len)
+{
+	ssize_t ret;
+	struct drm_dp_aux *aux = data->aux;
+
+	ret = drm_dp_dpcd_write(aux, offset, value, len);
+	if (ret != len) {
+		if (ret >= 0)
+			return -EIO;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_dpcd_write);
+
+/*
+ * Write data to the HDCP DDC slave device
+ *
+ * @data: drm_hdcp_helper_data containing the i2c_adapter.
+ * @offset: the offset to write to
+ * @buffer: the data to write
+ * @size: the size of the data
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_hdcp_remote_ddc_write(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *buffer, size_t len)
+{
+	int ret;
+	u8 *write_buf;
+	struct i2c_msg msg;
+	struct i2c_adapter *i2c = data->connector->ddc;
+
+	write_buf = kzalloc(len + 1, GFP_KERNEL);
+	if (!write_buf)
+		return -ENOMEM;
+
+	write_buf[0] = offset & 0xff;
+	memcpy(&write_buf[1], buffer, len);
+
+	msg.addr = DRM_HDCP_DDC_ADDR;
+	msg.flags = 0, msg.len = len + 1, msg.buf = write_buf;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+	if (ret == 1)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+
+	kfree(write_buf);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_ddc_write);
+
+static bool drm_hdcp_is_ksv_valid(struct drm_hdcp_ksv *ksv)
+{
+	/* Valid Ksv has 20 0's and 20 1's */
+	return hweight32(ksv->words[0]) + hweight32(ksv->words[1]) == 20;
+}
+
+static int drm_hdcp_read_valid_bksv(struct drm_hdcp_helper_data *data,
+				    struct drm_hdcp_ksv *bksv)
+{
+	int ret, i, tries = 2;
+
+	/* HDCP spec states that we must retry the bksv if it is invalid */
+	for (i = 0; i < tries; i++) {
+		ret = data->funcs->remote_read(data, data->hdcp1_lut-
Quoted 1 line

bksv,

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+					       bksv->bytes,

DRM_HDCP_KSV_LEN);

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+		if (ret)
+			return ret;
+
+		if (drm_hdcp_is_ksv_valid(bksv))
+			break;
+	}
+	if (i == tries) {
+		drm_dbg_kms(data->connector->dev, "Bksv is invalid

%*ph\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +18 -0
+			    DRM_HDCP_KSV_LEN, bksv->bytes);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * Check if the sink is capable of HDCP 1.x. DisplayPort has a dedicated bit
+ * for this in DPCD.
+ *
+ * @data: pointer to the HDCP helper data.
+ * @capable: pointer to a bool which will contain true if the sink is capable.
+ *
+ * Returns:
+ * -errno if the transacation between source and sink fails.
+ */
+int drm_hdcp_helper_hdcp1_capable_dp(struct drm_hdcp_helper_data

*data,

drivers/gpu/drm/display/drm_hdcp_helper.c +6 -0
+				     bool *capable)
+{
+	int ret;
+	u8 bcaps;
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->bcaps,

&bcaps, 1);

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+	*capable = !ret && (bcaps & DP_BCAPS_HDCP_CAPABLE);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_capable_dp);
+
+/**
+ * Check if the sink is capable of HDCP 1.x. HDMI spec states that

transmitters
+ * should use bksv to determine capability
+ *
+ * @data: pointer to the HDCP helper data.
+ * @capable: pointer to a bool which will contain true if the sink is capable.
+ *
+ * Returns:
+ * -errno if the transacation between source and sink fails.
+ */
+int drm_hdcp_helper_hdcp1_capable_hdmi(struct drm_hdcp_helper_data *data,

drivers/gpu/drm/display/drm_hdcp_helper.c +10 -0
+				       bool *capable)
+{
+	struct drm_hdcp_ksv bksv;
+	*capable = drm_hdcp_read_valid_bksv(data, &bksv) == 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_capable_hdmi);
+
+static void drm_hdcp_update_value(struct drm_hdcp_helper_data *data,

u64 value,

drivers/gpu/drm/display/drm_hdcp_helper.c +12 -0
+				  bool update_property)
+{
+	WARN_ON(!mutex_is_locked(&data->mutex));
+
+	data->value = value;
+	if (update_property) {
+		drm_connector_get(data->connector);
+		schedule_work(&data->prop_work);
+	}
+}
+
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp(struct

drm_hdcp_helper_data *data)

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+{
+	u8 val;
+	u8 mask = DP_BSTATUS_READY;
+	int ret = drm_hdcp_remote_dpcd_read(data,

DP_AUX_HDCP_BSTATUS, &val, 1);

drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+
+	if (ret)
+		return ret;
+	if (val & mask)
+		return 0;
+
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp);
+
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi(struct

drm_hdcp_helper_data *data)

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+{
+	u8 val;
+	u8 mask = DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
+	int ret = drm_hdcp_remote_ddc_read(data,

DRM_HDCP_DDC_BCAPS, &val, 1);

drivers/gpu/drm/display/drm_hdcp_helper.c +17 -0
+
+	if (ret)
+		return ret;
+	if (val & mask)
+		return 0;
+
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi);
+
+/* Read the KSV FIFO of the HDCP receive of DP.
+ * Read via 15 byte window (3 entries @ 5 bytes each)
+ *
+ * @fifo: buffer to store the KSV FIFO
+ * @num_downstream: number of devices in the KSV FIFO
+ */
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_dp(struct

drm_hdcp_helper_data *data,

drivers/gpu/drm/display/drm_hdcp_helper.c +7 -0
+					   u8 *fifo, u8 num_downstream)
+{
+	struct drm_device *dev = data->connector->dev;
+	int i;
+
+	for (i = 0; i < num_downstream; i += 3) {
+		size_t len = min(num_downstream - i, 3) *

DRM_HDCP_KSV_LEN;

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+		int ret = drm_hdcp_remote_dpcd_read(data,

DP_AUX_HDCP_KSV_FIFO,

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+						    fifo + i *

DRM_HDCP_KSV_LEN,

drivers/gpu/drm/display/drm_hdcp_helper.c +3 -0
+						    len);
+		if (ret) {
+			drm_err(dev, "Read ksv[%d] from DP/AUX failed

(%d)\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +16 -0
+				i, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_read_ksv_fifo_dp);
+
+/* Read the KSV FIFO of the HDCP receive of HDMI.
+ * Over HDMI, read the whole thing at once.
+ *
+ * @fifo: buffer to store the KSV FIFO
+ * @num_downstream: number of devices in the KSV FIFO
+ */
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi(struct

drm_hdcp_helper_data *data,

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+					     u8 *fifo, u8 num_downstream)
+{
+	struct drm_device *dev = data->connector->dev;
+
+	int ret = drm_hdcp_remote_ddc_read(data,

DRM_HDCP_DDC_KSV_FIFO, fifo,

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+					   num_downstream *

DRM_HDCP_KSV_LEN);

drivers/gpu/drm/display/drm_hdcp_helper.c +7 -0
+	if (ret)
+		drm_err(dev, "DDC ksv fifo read failed (%d)\n", ret);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi);
+
+static int drm_hdcp_helper_hdcp1_read_v_prime(struct

drm_hdcp_helper_data *data,

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+					      u32 *v_prime)
+{
+	struct drm_device *dev = data->connector->dev;
+	int ret, i;
+
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
+		ret = data->funcs->remote_read(data, data->hdcp1_lut->v[i],
+					       (u8 *)&v_prime[i],
+

DRM_HDCP_V_PRIME_PART_LEN);

drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+		if (ret) {
+			drm_dbg_kms(dev, "Read v'[%d] from failed (%d)\n",

i,

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+				    ret);
+			return ret >= 0 ? -EIO : ret;
+		}
+	}
+	return 0;
+}
+
+static int
+drm_hdcp_helper_hdcp1_authenticate_downstream(struct

drm_hdcp_helper_data *data)

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	u32 v_prime[DRM_HDCP_V_PRIME_NUM_PARTS];
+	u8 bstatus[DRM_HDCP_BSTATUS_LEN];
+	u8 num_downstream, *ksv_fifo;
+	int ret, i, tries = 3;
+
+	ret = read_poll_timeout(data->funcs->hdcp1_ksv_fifo_ready, ret,

!ret,

drivers/gpu/drm/display/drm_hdcp_helper.c +7 -0
+				10 * 1000, 5 * 1000 * 1000, false, data);
+	if (ret) {
+		drm_err(dev, "Failed to poll ksv ready, %d\n", ret);
+		return ret;
+	}
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->bstatus,

bstatus,

drivers/gpu/drm/display/drm_hdcp_helper.c +6 -0
+				       DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		return ret;
+
+	/*
+	 * When repeater reports 0 device count, HDCP1.4 spec allows

disabling

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+	 * the HDCP encryption. That implies that repeater can't have its own
+	 * display. As there is no consumption of encrypted content in the
+	 * repeater with 0 downstream devices, we are failing the
+	 * authentication.
+	 */
+	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
+	if (num_downstream == 0) {
+		drm_err(dev, "Repeater with zero downstream devices,

%*ph\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+			DRM_HDCP_BSTATUS_LEN, bstatus);
+		return -EINVAL;
+	}
+
+	ksv_fifo = kcalloc(DRM_HDCP_KSV_LEN, num_downstream,

GFP_KERNEL);

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+	if (!ksv_fifo)
+		return -ENOMEM;
+
+	ret = data->funcs->hdcp1_read_ksv_fifo(data, ksv_fifo,

num_downstream);

drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+	if (ret) {
+		drm_err(dev, "Failed to read ksv fifo, %d/%d\n",

num_downstream,

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+			ret);
+		goto out;
+	}
+
+	if (drm_hdcp_check_ksvs_revoked(dev, ksv_fifo, num_downstream))

{

drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+		drm_err(dev, "Revoked Ksv(s) in ksv_fifo\n");
+		ret = -EPERM;
+		goto out;
+	}
+
+	/*
+	 * When V prime mismatches, DP Spec mandates re-read of
+	 * V prime at least twice.
+	 */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_read_v_prime(data,

v_prime);

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+		if (ret)
+			continue;
+
+		ret = data->funcs->hdcp1_store_ksv_fifo(connector, ksv_fifo,
+							num_downstream,

bstatus,

drivers/gpu/drm/display/drm_hdcp_helper.c +7 -0
+							v_prime,
+							data->driver_data);
+		if (!ret)
+			break;
+	}
+	if (ret)
+		drm_err(dev, "Could not validate KSV FIFO with V' %d\n",

ret);

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+
+out:
+	if (!ret)
+		drm_dbg_kms(dev, "HDCP is enabled (%d downstream

devices)\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +7 -0
+			    num_downstream);
+
+	kfree(ksv_fifo);
+	return ret;
+}
+
+static int drm_hdcp_helper_hdcp1_validate_ri(struct

drm_hdcp_helper_data *data)

drivers/gpu/drm/display/drm_hdcp_helper.c +40 -0
+{
+	union {
+		u32 word;
+		u8 bytes[DRM_HDCP_RI_LEN];
+	} ri_prime = { .word = 0 };
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->ri,
+				       ri_prime.bytes, DRM_HDCP_RI_LEN);
+	if (ret) {
+		drm_err(dev, "Failed to read R0' %d\n", ret);
+		return ret;
+	}
+
+	return data->funcs->hdcp1_match_ri(connector, ri_prime.word,
+					   data->driver_data);
+}
+
+/*
+ * Wait for the DP receiver to set the R0' ready bit in the Bstatus register.
+ * This bit is set to 1 when the receiver has received and validated the
+ * receiver ID list and receiver capability list.
+ * The receiver must set this bit to 1 within 200 ms of the receiver ID
+ * list and receiver capability list being received.
+ *
+ * @data: DRM HDCP helper data structure
+ * @r0_prime_remaining_us: timeout for the R0' ready bit
+ *
+ * Return: 0 on success, error code on failure
+ */
+int drm_hdcp_helper_wait_r0_dp(struct drm_hdcp_helper_data *data,
+			       unsigned long r0_prime_remaining_us)
+{
+	u8 val;
+	int ret = 0;
+
+	ret = read_poll_timeout(drm_hdcp_remote_dpcd_read, ret,
+				!ret && (val &

DP_BSTATUS_R0_PRIME_READY), 1000,

drivers/gpu/drm/display/drm_hdcp_helper.c +12 -0
+				r0_prime_remaining_us, false, data,
+				DP_AUX_HDCP_BSTATUS, &val, 1);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_wait_r0_dp);
+
+/*
+ * drm_hdcp_helper_wait_r0_hdmi - Wait for R0 to be ready on HDMI
+ * @data: DRM HDCP helper data
+ * @r0_prime_remaining_us: Remaining time to wait for R0 in microseconds
+ *
+ * HDMI doesn't have a R0_READY bit, so we just have to wait the

remainder of the
+ * 300 ms timeout.
+ *
+ * Returns:
+ * 0 on success, negative error code on failure
+ */

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+
+int drm_hdcp_helper_wait_r0_hdmi(struct drm_hdcp_helper_data *data,
+				 unsigned long r0_prime_remaining_us)
+{
+	usleep_range(r0_prime_remaining_us, r0_prime_remaining_us +

1000);

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_wait_r0_hdmi);
+
+static int drm_hdcp_helper_hdcp1_authenticate(struct

drm_hdcp_helper_data *data)

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+{
+	union {
+		u32 word;
+		u8 bytes[DRM_HDCP_BSTATUS_LEN];
+	} bstatus;
+	const struct drm_hdcp_helper_funcs *funcs = data->funcs;
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	unsigned long r0_prime_timeout, r0_prime_remaining_us = 0,

tmp_jiffies;

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+	struct drm_hdcp_ksv aksv;
+	struct drm_hdcp_ksv bksv;
+	struct drm_hdcp_an an;
+	bool repeater_present;
+	int ret, i, tries = 3;
+	u8 bcaps;
+
+	if (funcs->hdcp1_read_an_aksv) {
+		ret = funcs->hdcp1_read_an_aksv(connector, an.words,

aksv.words,

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+						data->driver_data);
+		if (ret) {
+			drm_err(dev, "Failed to read An/Aksv values, %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = funcs->remote_write(data, data->hdcp1_lut->an,

an.bytes,

drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+					  DRM_HDCP_AN_LEN);
+		if (ret) {
+			drm_err(dev, "Failed to write An to receiver, %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = funcs->remote_write(data, data->hdcp1_lut->aksv,
+					  aksv.bytes, DRM_HDCP_KSV_LEN);
+		if (ret) {
+			drm_err(dev, "Failed to write Aksv to receiver,

%d\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+				ret);
+			return ret;
+		}
+	} else {
+		ret = funcs->hdcp1_send_an_aksv(connector, data-
Quoted 1 line

driver_data);

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+		if (ret) {
+			drm_err(dev, "Failed to read An/Aksv values, %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Timeout for R0' to become available. The spec says 100ms from

Aksv,

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+	 * but some monitors can take longer than this. We'll set the timeout

at

drivers/gpu/drm/display/drm_hdcp_helper.c +22 -0
+	 * 300ms just to be sure.
+	 */
+	r0_prime_timeout = jiffies + msecs_to_jiffies(300);
+
+	memset(&bksv, 0, sizeof(bksv));
+
+	ret = drm_hdcp_read_valid_bksv(data, &bksv);
+	if (ret < 0)
+		return ret;
+
+	if (drm_hdcp_check_ksvs_revoked(dev, bksv.bytes, 1)) {
+		drm_err(dev, "BKSV is revoked\n");
+		return -EPERM;
+	}
+
+	ret = funcs->remote_read(data, data->hdcp1_lut->bcaps, &bcaps, 1);
+	if (ret)
+		return ret;
+
+	memset(&bstatus, 0, sizeof(bstatus));
+
+	ret = funcs->remote_read(data, data->hdcp1_lut->bstatus,

bstatus.bytes,

drivers/gpu/drm/display/drm_hdcp_helper.c +7 -0
+				 DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		return ret;
+
+	if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus.bytes[0]) ||
+	    DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus.bytes[1])) {
+		drm_err(dev, "Max Topology Limit Exceeded,

bstatus=%*ph\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+			DRM_HDCP_BSTATUS_LEN, bstatus.bytes);
+		return -EPERM;
+	}
+
+	repeater_present = bcaps & data->hdcp1_lut-
Quoted 1 line

bcaps_mask_repeater_present;

drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+
+	ret = funcs->hdcp1_store_receiver_info(connector, bksv.words,
+					       bstatus.word, bcaps,
+					       repeater_present,
+					       data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to store bksv, %d\n", ret);
+		return ret;
+	}
+
+	ret = funcs->hdcp1_enable_encryption(connector, data-
Quoted 1 line

driver_data);

drivers/gpu/drm/display/drm_hdcp_helper.c +20 -0
+	if (ret)
+		return ret;
+
+	ret = funcs->hdcp1_wait_for_r0(connector, data->driver_data);
+	if (ret)
+		return ret;
+
+	tmp_jiffies = jiffies;
+	if (time_before(tmp_jiffies, r0_prime_timeout))
+		r0_prime_remaining_us =
+			jiffies_to_usecs(r0_prime_timeout - tmp_jiffies);
+
+	ret = funcs->wait_r0(data, r0_prime_remaining_us);
+	if (ret) {
+		drm_err(dev, "R0' did not become ready %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * DP HDCP Spec mandates the two more reattempt to read R0, in

case

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+	 * of R0 mismatch.
+	 */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_validate_ri(data);
+		if (!ret)
+			break;
+	}
+	if (ret) {
+		drm_err(dev, "Failed to match R0/R0', aborting HDCP %d\n",

ret);

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+		return ret;
+	}
+
+	if (repeater_present)
+		return

drm_hdcp_helper_hdcp1_authenticate_downstream(data);

drivers/gpu/drm/display/drm_hdcp_helper.c +6 -0
+
+	drm_dbg_kms(dev, "HDCP is enabled (no repeater present)\n");
+	return 0;
+}
+
+static int drm_hdcp_helper_hdcp1_enable(struct drm_hdcp_helper_data

*data)

drivers/gpu/drm/display/drm_hdcp_helper.c +6 -0
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int i, ret, tries = 3;
+
+	drm_dbg_kms(dev, "[%s:%d] HDCP is being enabled...\n", connector-
Quoted 1 line

name,

drivers/gpu/drm/display/drm_hdcp_helper.c +12 -0
+		    connector->base.id);
+
+	/* In case of authentication failures, HDCP spec expects reauth. */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_authenticate(data);
+		if (!ret)
+			return 0;
+
+		drm_dbg_kms(dev, "HDCP Auth failure (%d)\n", ret);
+
+		/* Ensuring HDCP encryption and signalling are stopped. */
+		data->funcs->hdcp1_disable(data->connector, data-
Quoted 1 line

driver_data);

drivers/gpu/drm/display/drm_hdcp_helper.c +3 -0
+	}
+
+	drm_err(dev, "HDCP authentication failed (%d tries/%d)\n", tries,

ret);

drivers/gpu/drm/display/drm_hdcp_helper.c +18 -0
+	return ret;
+}
+
+static inline void
+drm_hdcp_helper_driver_lock(struct drm_hdcp_helper_data *data)
+{
+	if (data->driver_mutex)
+		mutex_lock(data->driver_mutex);
+}
+
+static inline void
+drm_hdcp_helper_driver_unlock(struct drm_hdcp_helper_data *data)
+{
+	if (data->driver_mutex)
+		mutex_unlock(data->driver_mutex);
+}
+
+static int drm_hdcp_helper_enable_hdcp(struct drm_hdcp_helper_data

*data,

drivers/gpu/drm/display/drm_hdcp_helper.c +7 -0
+				       struct drm_atomic_state *state,
+				       struct mutex *driver_mutex)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_connector_state *conn_state;
+	struct drm_device *dev = connector->dev;
+	unsigned long check_link_interval =

DRM_HDCP2_CHECK_PERIOD_MS;

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+	bool capable;
+	int ret = 0;
+
+	conn_state = drm_atomic_get_new_connector_state(state,

connector);

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+
+	mutex_lock(&data->mutex);
+
+	if (data->value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+		drm_hdcp_update_value(data,

DRM_MODE_CONTENT_PROTECTION_ENABLED,

drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+				      true);
+		goto out_data_mutex;
+	}
+
+	drm_WARN_ON(dev, data->driver_mutex != NULL);
+	data->driver_mutex = driver_mutex;
+
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->funcs->setup) {
+		ret = data->funcs->setup(connector, state, data-
Quoted 1 line

driver_data);

drivers/gpu/drm/display/drm_hdcp_helper.c bool drm_hdcp_has_changed(struct drm_connector *connector, · line 1222

Quoted 2 lines
drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+		if (ret) {
+			drm_err(dev, "Failed to setup HDCP %d\n", ret);

Is drm_err the right print here since a monitor may just not be capable of it maybe keep it as A debug statement.

Quoted 36 lines
drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+			goto out;
+		}
+	}
+
+	if (!data->funcs->are_keys_valid ||
+	    !data->funcs->are_keys_valid(connector, data->driver_data)) {
+		if (data->funcs->load_keys) {
+			ret = data->funcs->load_keys(connector,
+						     data->driver_data);
+			if (ret) {
+				drm_err(dev, "Failed to load HDCP keys

%d\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+					ret);
+				goto out;
+			}
+		}
+	}
+
+	/*
+	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the

setup

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+	 * is capable of HDCP2.2, it is preferred to use HDCP2.2.
+	 */
+	ret = data->funcs->hdcp2_capable(connector, &capable,
+					 data->driver_data);
+	if (ret)
+		drm_err(dev, "HDCP 2.x capability check failed %d\n", ret);
+
+	if (!ret && capable) {
+		if (conn_state->content_type ==

DRM_MODE_HDCP_CONTENT_TYPE1) {

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+			data->enabled_type =

DRM_MODE_HDCP_CONTENT_TYPE1;

drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+		} else {
+			data->enabled_type =

DRM_MODE_HDCP_CONTENT_TYPE0;

drivers/gpu/drm/display/drm_hdcp_helper.c

Quoted 1 line
drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+		}

Why don't we just assign the enabled type directly with what's in conn_state->content_type, we do not require the if else statement here according to me.

Quoted 9 lines
drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+
+		ret = data->funcs->hdcp2_enable(connector, data-
Quoted 1 line

driver_data);

drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+		if (!ret) {
+			check_link_interval =

DRM_HDCP2_CHECK_PERIOD_MS;

drivers/gpu/drm/display/drm_hdcp_helper.c +3 -0
+			goto out;
+		}
+	}

drivers/gpu/drm/display/drm_hdcp_helper.c bool drm_hdcp_has_changed(struct drm_connector *connector, · line 1271

Quoted 10 lines
drivers/gpu/drm/display/drm_hdcp_helper.c +10 -0
+
+	/*
+	 * When HDCP2.2 fails and Content Type is not Type1, HDCP1.4 will
+	 * be attempted.
+	 */
+	ret = data->funcs->hdcp1_capable(data, &capable);
+	if (ret) {
+		drm_err(dev, "HDCP 1.x capability check failed %d\n", ret);
+		goto out;
+	}

Same here can we make this a debug print.

Regards,
Suraj Kandpal

Quoted 936 lines
drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+	if (capable &&
+	    conn_state->content_type !=

DRM_MODE_HDCP_CONTENT_TYPE1) {

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+		data->enabled_type = DRM_MODE_HDCP_CONTENT_TYPE0;
+		ret = drm_hdcp_helper_hdcp1_enable(data);
+		if (!ret)
+			check_link_interval =

DRM_HDCP_CHECK_PERIOD_MS;

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+	}
+
+out:
+	if (!ret) {
+		schedule_delayed_work(&data->check_work,

check_link_interval);

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+		drm_hdcp_update_value(data,

DRM_MODE_CONTENT_PROTECTION_ENABLED,

drivers/gpu/drm/display/drm_hdcp_helper.c +13 -0
+				      true);
+	}
+
+	drm_hdcp_helper_driver_unlock(data);
+	if (ret)
+		data->driver_mutex = NULL;
+
+out_data_mutex:
+	mutex_unlock(&data->mutex);
+	return ret;
+}
+
+static int drm_hdcp_helper_disable_hdcp(struct drm_hdcp_helper_data

*data)

drivers/gpu/drm/display/drm_hdcp_helper.c +10 -0
+{
+	int ret = 0;
+
+	mutex_lock(&data->mutex);
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		goto out;
+
+	drm_dbg_kms(data->connector->dev, "[%s:%d] HDCP is being

disabled...\n",

drivers/gpu/drm/display/drm_hdcp_helper.c +3 -0
+		    data->connector->name, data->connector->base.id);
+
+	drm_hdcp_update_value(data,

DRM_MODE_CONTENT_PROTECTION_UNDESIRED,

drivers/gpu/drm/display/drm_hdcp_helper.c +21 -0
+			      true);
+
+	if (data->enabled_type == DRM_MODE_HDCP_CONTENT_TYPE1)
+		ret = data->funcs->hdcp2_disable(data->connector,
+						 data->driver_data);
+	else
+		ret = data->funcs->hdcp1_disable(data->connector,
+						 data->driver_data);
+
+	drm_dbg_kms(data->connector->dev, "HDCP is disabled\n");
+
+out:
+	drm_hdcp_helper_driver_unlock(data);
+	data->driver_mutex = NULL;
+	mutex_unlock(&data->mutex);
+	cancel_delayed_work_sync(&data->check_work);
+	return ret;
+}
+
+/**
+ * drm_hdcp_helper_atomic_commit - Helper for drivers to call during

commit to
+ * enable/disable HDCP
+ *
+ * @data: pointer to the @drm_hdcp_helper_data for the connector
+ * @state: pointer to the atomic state being committed
+ * @driver_mutex: driver-provided lock to be used while interacting with the driver
+ *
+ * This function can be used by display drivers to determine when HDCP should be
+ * enabled or disabled based on the connector state. It should be called during
+ * steady-state commits as well as connector enable/disable. The function will
+ * handle the HDCP authentication/encryption logic, calling back into the driver
+ * when source operations are necessary.
+ *
+ * @driver_mutex will be retained and used for the duration of the HDCP session
+ * since it will be needed for link checks and retries. This mutex is useful if
+ * the driver has shared resources across connectors which must be serialized.
+ * For example, driver_mutex can be used for MST connectors sharing a common
+ * encoder which should not be accessed/changed concurrently. When the
+ * connector's session is torn down, the mutex will be forgotten by the helper
+ * for this connector until the next session.
+ */
+void drm_hdcp_helper_atomic_commit(struct drm_hdcp_helper_data *data,

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+				   struct drm_atomic_state *state,
+				   struct mutex *driver_mutex)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_connector_state *conn_state;
+	bool type_changed;
+
+	conn_state = drm_atomic_get_new_connector_state(state,

connector);

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+	if (!conn_state)
+		return;
+
+	type_changed = conn_state->hdcp_content_type != data-
Quoted 1 line

enabled_type;

drivers/gpu/drm/display/drm_hdcp_helper.c +16 -0
+
+	if (conn_state->content_protection ==
+	    DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+		drm_hdcp_helper_disable_hdcp(data);
+		return;
+	}
+
+	if (!conn_state->crtc) {
+		drm_hdcp_helper_disable_hdcp(data);
+
+		/* Restore property to DESIRED so it's retried later */
+		if (conn_state->content_protection ==
+		    DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+			mutex_lock(&data->mutex);
+			drm_hdcp_update_value(
+				data,

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/display/drm_hdcp_helper.c +23 -0
+				true);
+			mutex_unlock(&data->mutex);
+		}
+		return;
+	}
+
+	/* Already enabled */
+	if (conn_state->content_protection ==
+	    DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		return;
+
+	/* Disable and re-enable HDCP on content type change */
+	if (type_changed)
+		drm_hdcp_helper_disable_hdcp(data);
+
+	drm_hdcp_helper_enable_hdcp(data, state, driver_mutex);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_atomic_commit);
+
+static void drm_hdcp_helper_prop_work(struct work_struct *work)
+{
+	struct drm_hdcp_helper_data *data =
+		container_of(work, struct drm_hdcp_helper_data,

prop_work);

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	mutex_lock(&data->mutex);
+
+	/*
+	 * This worker is only used to flip between ENABLED/DESIRED. Either

of

drivers/gpu/drm/display/drm_hdcp_helper.c +5 -0
+	 * those to UNDESIRED is handled by core. If value == UNDESIRED,
+	 * we're running just after hdcp has been disabled, so just exit
+	 */
+	if (data->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		drm_hdcp_update_content_protection(connector, data-
Quoted 1 line

value);

drivers/gpu/drm/display/drm_hdcp_helper.c +10 -0
+
+	mutex_unlock(&data->mutex);
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+}
+
+/*
+ * Check the link registers for HDCP 1.x for DP.
+ */
+int drm_hdcp_hdcp1_check_link_registers_dp(struct drm_device *dev,
+					   struct drm_hdcp_helper_data

*data,

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+					   bool *shoud_retry)
+{
+	u8 bstatus;
+	int ret = drm_hdcp_remote_dpcd_read(data,

DP_AUX_HDCP_BSTATUS, &bstatus,

drivers/gpu/drm/display/drm_hdcp_helper.c +9 -0
+					    1);
+	*shoud_retry = true;
+
+	if (ret) {
+		*shoud_retry = false;
+		drm_err(dev, "Failed to read dpcd bstatus, %d\n", ret);
+		return ret;
+	}
+	if (bstatus & (DP_BSTATUS_LINK_FAILURE |

DP_BSTATUS_REAUTH_REQ))

drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_hdcp1_check_link_registers_dp);
+
+/*
+ * Check the link registers for HDCP 1.x for HDMI.
+ */
+int drm_hdcp_hdcp1_check_link_registers_hdmi(struct drm_device *dev,
+					     struct drm_hdcp_helper_data

*data,

drivers/gpu/drm/display/drm_hdcp_helper.c +41 -0
+					     bool *shoud_retry)
+{
+	int ret = drm_hdcp_helper_hdcp1_validate_ri(data);
+	*shoud_retry = true;
+
+	if (ret)
+		drm_err(dev, "Ri' mismatch, check failed (%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_hdcp1_check_link_registers_hdmi);
+
+static int drm_hdcp_hdcp1_check_link(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+	bool should_retry = false;
+
+	if (data->funcs->hdcp1_check_link) {
+		ret = data->funcs->hdcp1_check_link(connector,
+						    data->driver_data);
+		if (ret)
+			goto retry;
+	}
+
+	ret = data->funcs->hdcp1_check_link_registers(connector->dev, data,
+						      &should_retry);
+	if (!ret)
+		return 0;
+	else if (!should_retry)
+		return ret;
+
+retry:
+	drm_err(dev, "[%s:%d] HDCP link failed, retrying authentication\n",
+		connector->name, connector->base.id);
+
+	ret = data->funcs->hdcp1_disable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to disable hdcp (%d)\n", ret);
+		drm_hdcp_update_value(data,

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+				      true);
+		return ret;
+	}
+
+	ret = drm_hdcp_helper_hdcp1_enable(data);
+	if (ret) {
+		drm_err(dev, "Failed to enable hdcp (%d)\n", ret);
+		drm_hdcp_update_value(data,

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/display/drm_hdcp_helper.c +24 -0
+				      true);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int drm_hdcp_hdcp2_check_link(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+
+	ret = data->funcs->hdcp2_check_link(connector, data->driver_data);
+	if (!ret)
+		return 0;
+
+	drm_err(dev, "[%s:%d] HDCP2 link failed, retrying authentication\n",
+		connector->name, connector->base.id);
+
+	ret = data->funcs->hdcp2_disable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to disable hdcp2 (%d)\n", ret);
+		drm_hdcp_update_value(data,

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+				      true);
+		return ret;
+	}
+
+	ret = data->funcs->hdcp2_enable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to enable hdcp2 (%d)\n", ret);
+		drm_hdcp_update_value(data,

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/display/drm_hdcp_helper.c +11 -0
+				      true);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void drm_hdcp_helper_check_work(struct work_struct *work)
+{
+	struct drm_hdcp_helper_data *data = container_of(
+		to_delayed_work(work), struct drm_hdcp_helper_data,

check_work);

drivers/gpu/drm/display/drm_hdcp_helper.c +32 -0
+	unsigned long check_link_interval;
+
+	mutex_lock(&data->mutex);
+	if (data->value != DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		goto out_data_mutex;
+
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->enabled_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+		if (drm_hdcp_hdcp2_check_link(data))
+			goto out;
+		check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
+	} else {
+		if (drm_hdcp_hdcp1_check_link(data))
+			goto out;
+		check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
+	}
+	schedule_delayed_work(&data->check_work, check_link_interval);
+
+out:
+	drm_hdcp_helper_driver_unlock(data);
+out_data_mutex:
+	mutex_unlock(&data->mutex);
+}
+
+/**
+ * drm_hdcp_helper_schedule_hdcp_check - Schedule a check link cycle.
+ *
+ * @data: Pointer to the HDCP helper data.
+ *
+ * This function will kick off a check link cycle on behalf of the caller. This
+ * can be used by DP short hpd interrupt handlers, where the driver must

poke
+ * the helper to check the link is still valid.
+ */
+void drm_hdcp_helper_schedule_hdcp_check(struct
drm_hdcp_helper_data *data)

drivers/gpu/drm/display/drm_hdcp_helper.c +24 -0
+{
+	schedule_delayed_work(&data->check_work, 0);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_schedule_hdcp_check);
+
+static struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize(struct drm_connector *connector,
+			   const struct drm_hdcp_helper_funcs *funcs,
+			   bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+	int ret;
+
+	out = kzalloc(sizeof(*out), GFP_KERNEL);
+	if (!out)
+		return ERR_PTR(-ENOMEM);
+
+	out->connector = connector;
+	out->funcs = funcs;
+
+	mutex_init(&out->mutex);
+	out->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+
+	INIT_DELAYED_WORK(&out->check_work,

drm_hdcp_helper_check_work);

drivers/gpu/drm/display/drm_hdcp_helper.c +19 -0
+	INIT_WORK(&out->prop_work, drm_hdcp_helper_prop_work);
+
+	ret = drm_connector_attach_content_protection_property(
+		connector, hdcp_content_type);
+	if (ret) {
+		drm_hdcp_helper_destroy(out);
+		return ERR_PTR(ret);
+	}
+
+	return out;
+}
+
+/**
+ * drm_hdcp_helper_initialize_dp - Initializes the HDCP helpers for a
+ * DisplayPort connector
+ *
+ * @connector: pointer to the DisplayPort connector.
+ * @funcs: pointer to the vtable of HDCP helper funcs for this connector.
+ * @hdcp_content_type: True if the content_type property should be

attached.
+ *
+ * This function initializes the HDCP helper for the given DisplayPort connector.
+ * This involves creating the Content Protection property as well as the Content
+ * Type property (if desired). Upon success, it will return a pointer to the
+ * HDCP helper data. Ownership of the underlying memory is ttransferredto the
+ * caller and should be freed using drm_hdcp_helper_destroy().
+ *
+ * Returns:
+ * Pointer to newly created HDCP helper data. PTR_ERR on failure.
+ */

drivers/gpu/drm/display/drm_hdcp_helper.c +3 -0
+struct drm_hdcp_helper_data *drm_hdcp_helper_initialize_dp(
+	struct drm_connector *connector, struct drm_dp_aux *aux,
+	const struct drm_hdcp_helper_funcs *funcs, bool

hdcp_content_type)

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+{
+	struct drm_hdcp_helper_data *out;
+
+	out = drm_hdcp_helper_initialize(connector, funcs,

hdcp_content_type);

drivers/gpu/drm/display/drm_hdcp_helper.c +12 -0
+	if (IS_ERR(out))
+		return out;
+
+	out->aux = aux;
+	out->hdcp1_lut = &drm_hdcp_hdcp1_dpcd_lut;
+
+	return out;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_initialize_dp);
+
+/**
+ * drm_hdcp_helper_initialize_hdmi - Initializes the HDCP helpers for an

HDMI
+ * connector
+ *
+ * @connector: pointer to the HDMI connector.
+ * @funcs: pointer to the vtable of HDCP helper funcs for this connector.
+ * @hdcp_content_type: True if the content_type property should be attached.
+ *
+ * This function initializes the HDCP helper for the given HDMI connector. This
+ * involves creating the Content Protection property as well as the Content Type
+ * property (if desired). Upon success, it will return a pointer to the HDCP
+ * helper data. Ownership of the underlying memory is transferred to the caller
+ * and should be freed using drm_hdcp_helper_destroy().
+ *
+ * Returns:
+ * Pointer to newly created HDCP helper data. PTR_ERR on failure.
+ */

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize_hdmi(struct drm_connector *connector,
+				const struct drm_hdcp_helper_funcs *funcs,
+				bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+
+	out = drm_hdcp_helper_initialize(connector, funcs,

hdcp_content_type);

drivers/gpu/drm/display/drm_hdcp_helper.c +32 -0
+	if (IS_ERR(out))
+		return out;
+
+	out->hdcp1_lut = &drm_hdcp_hdcp1_ddc_lut;
+
+	return out;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_initialize_hdmi);
+
+/**
+ * drm_hdcp_helper_destroy - Destroys the given HDCP helper data.
+ *
+ * @data: Pointer to the HDCP helper data.
+ *
+ * This function cleans up and destroys the HDCP helper data created by
+ * drm_hdcp_helper_initialize_dp() or drm_hdcp_helper_initialize_hdmi().
+ */
+void drm_hdcp_helper_destroy(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector;
+
+	if (!data)
+		return;
+
+	connector = data->connector;
+
+	/*
+	 * If the connector is registered, it's possible userspace could kick
+	 * off another HDCP enable, which would re-spawn the workers.
+	 */
+	drm_WARN_ON(connector->dev,
+		    connector->registration_state ==

DRM_CONNECTOR_REGISTERED);

drivers/gpu/drm/display/drm_hdcp_helper.c +3 -0
+
+	/*
+	 * Now that the connector is not registered, check_work won't be

run,

drivers/gpu/drm/display/drm_hdcp_helper.c +8 -0
+	 * but cancel any outstanding instances of it
+	 */
+	cancel_delayed_work_sync(&data->check_work);
+
+	/*
+	 * We don't cancel prop_work in the same way as check_work since it
+	 * requires connection_mutex which could be held while calling this
+	 * function. Instead, we rely on the connector references grabbed

before

drivers/gpu/drm/display/drm_hdcp_helper.c +1 -0
+	 * scheduling prop_work to ensure the connector is alive when

prop_work

drivers/gpu/drm/display/drm_hdcp_helper.c +2 -0
+	 * is run. So if we're in the destroy path (which is where this
+	 * function should be called), we're "guaranteed" that prop_work is

not

drivers/gpu/drm/display/drm_hdcp_helper.c +3 -0
+	 * active (tl;dr This Should Never Happen).
+	 */
+	drm_WARN_ON(connector->dev, work_pending(&data-
Quoted 1 line

prop_work));

drivers/gpu/drm/display/drm_hdcp_helper.c +4 -0
+
+	kfree(data);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_destroy);
include/drm/display/drm_hdcp.h +42 -0
index 96a99b1377c0e..6ff28db964f13 100644
@@ -9,8 +9,12 @@
 #ifndef _DRM_HDCP_H_
 #define _DRM_HDCP_H_
 
+#include "linux/mutex.h"
+#include "linux/workqueue.h"
 #include <linux/types.h>
 
+#include <drm/drm_device.h>
+
 /* Period of hdcp checks (to ensure we're still authenticated) */
 #define DRM_HDCP_CHECK_PERIOD_MS		(128 * 16)
 #define DRM_HDCP2_CHECK_PERIOD_MS		500
@@ -36,6 +40,7 @@
 #define DRM_HDCP_DDC_BKSV			0x00
 #define DRM_HDCP_DDC_RI_PRIME			0x08
 #define DRM_HDCP_DDC_AKSV			0x10
+#define DRM_HDCP_DDC_AINFO			0x15
 #define DRM_HDCP_DDC_AN				0x18
 #define DRM_HDCP_DDC_V_PRIME(h)			(0x20 + h * 4)
 #define DRM_HDCP_DDC_BCAPS			0x40
@@ -123,6 +128,19 @@
 #define HDCP_2_2_DEV_COUNT_HI(x)		((x) & BIT(0))
 #define HDCP_2_2_DEPTH(x)			(((x) & (0x7 << 1)) >> 1)
 
+struct drm_hdcp_ksv {
+	union {
+		u32 words[2];
+		u8 bytes[DRM_HDCP_KSV_LEN];
+	};
+};
+struct drm_hdcp_an {
+	union {
+		u32 words[2];
+		u8 bytes[DRM_HDCP_AN_LEN];
+	};
+};
+
 struct hdcp2_cert_rx {
 	u8	receiver_id[HDCP_2_2_RECEIVER_ID_LEN];
 	u8	kpub_rx[HDCP_2_2_K_PUB_RX_LEN];
@@ -295,4 +313,273 @@ struct hdcp_srm_header {
 #define DRM_MODE_HDCP_CONTENT_TYPE0		0
 #define DRM_MODE_HDCP_CONTENT_TYPE1		1
 
+struct drm_connector;
+struct drm_atomic_state;
+
+struct drm_hdcp_helper_data {
+	struct mutex mutex;
+	struct mutex *driver_mutex;
+
+	struct drm_connector *connector;
+	const struct drm_hdcp_helper_funcs *funcs;
+
+	u64 value;
+	unsigned int enabled_type;
+
+	struct delayed_work check_work;
+	struct work_struct prop_work;
+
+	struct drm_dp_aux *aux;
+	const struct drm_hdcp_hdcp1_receiver_reg_lut *hdcp1_lut;
+
+	void *driver_data;
+};
+
+/**
+ * struct drm_hdcp_helper_funcs - A vtable of function hooks for the hdcp

helper
+ *
+ * These hooks are used by the hdcp helper to call into the driver/connector
+ * code to read/write to hw.
+ */

include/drm/display/drm_hdcp.h +23 -0
+struct drm_hdcp_helper_funcs {
+	/**
+	 * @setup - Performs driver-specific setup before hdcp is enabled
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*setup)(struct drm_connector *connector,
+		     struct drm_atomic_state *state, void *driver_data);
+
+	/**
+	 * @are_keys_valid - Checks if the HDCP transmitter keys are valid
+	 *
+	 * Returns: true if the display controller has valid keys loaded
+	 */
+	bool (*are_keys_valid)(struct drm_connector *connector,
+			       void *driver_data);
+
+	/**
+	 * @load_keys - Instructs the driver to load its HDCP transmitter keys
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*load_keys)(struct drm_connector *connector, void

*driver_data);

include/drm/display/drm_hdcp.h +3 -0
+
+	/**
+	 * @hdcp2_capable - Checks if both source and sink support HDCP

2.x

include/drm/display/drm_hdcp.h +4 -0
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_capable)(struct drm_connector *connector, bool

*capable,

include/drm/display/drm_hdcp.h +6 -0
+			     void *driver_data);
+
+	/**
+	 * @hdcp2_enable - Enables HDCP 2.x on the specified connector
+	 *
+	 * Since we don't have multiple examples of HDCP 2.x enablement,

we

include/drm/display/drm_hdcp.h +1 -0
+	 * provide the bare minimum support for HDCP 2.x help. Once we

have

include/drm/display/drm_hdcp.h +5 -0
+	 * more examples, perhaps we can be more helpful.
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_enable)(struct drm_connector *connector, void

*driver_data);

include/drm/display/drm_hdcp.h +3 -0
+
+	/**
+	 * @hdcp2_check_link - Checks the HDCP 2.x link on a specified

connector

include/drm/display/drm_hdcp.h +16 -0
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_check_link)(struct drm_connector *connector,
+				void *driver_data);
+
+	/**
+	 * @hdcp2_disable - Disables HDCP 2.x on the specified connector
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_disable)(struct drm_connector *connector,
+			     void *driver_data);
+
+	/**
+	 * @hdcp1_read_an_aksv - Reads transmitter's An & Aksv from

hardware

include/drm/display/drm_hdcp.h +2 -0
+	 *
+	 * Use this function if hardware allows reading the transmitter's An

and

include/drm/display/drm_hdcp.h +7 -0
+	 * Aksv values from the kernel. If your hardware will not allow this,
+	 * use hdcp1_send_an_aksv() and implement the transmission in the
+	 * driver.
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_read_an_aksv)(struct drm_connector *connector, u32

*an,

include/drm/display/drm_hdcp.h +4 -0
+				  u32 *aksv, void *driver_data);
+
+	/**
+	 * @hdcp1_send_an_aksv - Sends transmitter's An & Aksv to the

receiver

include/drm/display/drm_hdcp.h +2 -0
+	 *
+	 * Only implement this on hardware where An or Aksv are not

accessible

include/drm/display/drm_hdcp.h +10 -0
+	 * from the kernel. If these values can be read, use
+	 * hdcp1_read_an_aksv() instead.
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_send_an_aksv)(struct drm_connector *connector,
+				  void *driver_data);
+
+	/**
+	 * @hdcp1_store_receiver_info - Stores the receiver's info in the

transmitter

include/drm/display/drm_hdcp.h +10 -0
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_store_receiver_info)(struct drm_connector *connector,
+					 u32 *ksv, u32 status, u8 caps,
+					 bool repeater_present,
+					 void *driver_data);
+
+	/**
+	 * @hdcp1_enable_encryption - Enables encryption of the outgoing

signal

include/drm/display/drm_hdcp.h +16 -0
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_enable_encryption)(struct drm_connector *connector,
+				       void *driver_data);
+
+	/**
+	 * @hdcp1_wait_for_r0 - Wait for transmitter to calculate R0
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_wait_for_r0)(struct drm_connector *connector,
+				 void *driver_data);
+
+	/**
+	 * @hdcp1_match_ri - Matches the given Ri from the receiver with Ri

in

include/drm/display/drm_hdcp.h +5 -0
+	 * the transmitter
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_match_ri)(struct drm_connector *connector, u32

ri_prime,

include/drm/display/drm_hdcp.h +4 -0
+			      void *driver_data);
+
+	/**
+	 * @hdcp1_post_encryption - Allows the driver to confirm encryption

and

include/drm/display/drm_hdcp.h +9 -0
+	 * perform any post-processing
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_post_encryption)(struct drm_connector *connector,
+				     void *driver_data);
+
+	/**
+	 * @hdcp1_store_ksv_fifo - Write the receiver's KSV list to

transmitter

include/drm/display/drm_hdcp.h +10 -0
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_store_ksv_fifo)(struct drm_connector *connector,
+				    u8 *ksv_fifo, u8 num_downstream,
+				    u8 *bstatus, u32 *vprime,
+				    void *driver_data);
+
+	/**
+	 * @hdcp1_check_link - Allows the driver to check the HDCP 1.x

status

include/drm/display/drm_hdcp.h +46 -0
+	 * on a specified connector
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_check_link)(struct drm_connector *connector,
+				void *driver_data);
+
+	/**
+	 * @hdcp1_disable - Disables HDCP 1.x on the specified connector
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_disable)(struct drm_connector *connector,
+			     void *driver_data);
+
+	/**
+	 * @remote_read - Read HDCP keys from the remote HDCP engine
+
+	 * @data: drm_hdcp_helper_data containing the adapter
+	 * @offset: address of the register to read
+	 * @value: Pointer to the value to be filled
+	 * @len: The length of the data to be read
+	 *
+	 * Return 0 on success
+	 */
+	int (*remote_read)(struct drm_hdcp_helper_data *data,
+			   unsigned int offset, u8 *value, size_t len);
+
+	/**
+	 * @remote_write - Write HDCP keys to the remote HDCP engine
+
+	 * @data: drm_hdcp_helper_data containing the adapter
+	 * @offset: address of the register to read
+	 * @value: Pointer to the value to be filled
+	 * @len: The length of the data to be read
+	 *
+	 * Return 0 on success
+	 */
+	int (*remote_write)(struct drm_hdcp_helper_data *data,
+			    unsigned int offset, u8 *value, size_t len);
+
+	/**
+	 * @hdcp1_capable - Checks if the sink is capable of HDCP 1.x.
+	 *
+	 * @data: pointer to the HDCP helper data.
+	 * @capable: pointer to a bool which will contain true if the sink is

capable.

include/drm/display/drm_hdcp.h +5 -0
+	 *
+	 * Returns:
+	 * -errno if the transacation between source and sink fails.
+	 */
+	int (*hdcp1_capable)(struct drm_hdcp_helper_data *data, bool

*capable);

include/drm/display/drm_hdcp.h +18 -0
+
+	/**
+	 * @hdcp1_ksv_fifo_ready - Read KSV FIFO ready bit.
+	 *
+	 * @data: pointer to the HDCP helper data.
+	 *
+	 * Returns:
+	 * -errno if the transacation between source and sink fails.
+	 * 0 on success.
+	 * -EAGAIN otherwise.
+	 */
+	int (*hdcp1_ksv_fifo_ready)(struct drm_hdcp_helper_data *data);
+
+	/**
+	 * @wait_r0 - Wait for R0' to become available.
+	 *
+	 * @data: pointer to the HDCP helper data.
+	 * @r0_prime_remaining_us: time remaining for R0' to become

available.

include/drm/display/drm_hdcp.h +9 -0
+	 *
+	 * Returns 0 on success.
+	 */
+	int (*wait_r0)(struct drm_hdcp_helper_data *data,
+		       unsigned long r0_prime_remaining_us);
+
+	/*
+	 * @hdcp1_check_link_registers - This function checks the HDCP1
+	 * link registers and returns true if the link is valid and false

otherwise.

include/drm/display/drm_hdcp.h +9 -0
+	 *
+	 * @dev: DRM device for printing error messages
+	 * @data: HDCP helper data
+	 * @should_retry: if true, the caller will retry checking the registers
+	 *
+	 * Returns 0 on success.
+	 */
+	int (*hdcp1_check_link_registers)(struct drm_device *dev,
+					  struct drm_hdcp_helper_data

*data,

include/drm/display/drm_hdcp.h +11 -0
+					  bool *shoud_retry);
+
+	/**
+	 * @hdcp1_read_ksv_fifo - Read HDCP1.x KSV FIFO
+	 * @data: Helper data
+	 * @fifo: Buffer to store values
+	 * @num_downstream: Number of downstream devices
+	 *
+	 * Return: Zero on success or negative error code on failure.
+	 */
+	int (*hdcp1_read_ksv_fifo)(struct drm_hdcp_helper_data *data, u8

*fifo,

include/drm/display/drm_hdcp.h +3 -0
+				   u8 num_downstream);
+};
+
 #endif
include/drm/display/drm_hdcp_helper.h +17 -1
index 703421fcdf96c..a2e25bad78dbd 100644
@@ -15,11 +15,60 @@ struct drm_atomic_state;
 struct drm_device;
 struct drm_connector;
 
+struct drm_hdcp_helper_data;
+struct drm_dp_aux;
+struct i2c_adapter;
+struct mutex;
+
 int drm_hdcp_check_ksvs_revoked(struct drm_device *dev, u8 *ksvs, u32
 ksv_count);
 int drm_connector_attach_content_protection_property(struct
 drm_connector *connector,
 						     bool hdcp_content_type);
 void drm_hdcp_update_content_protection(struct drm_connector
 *connector, u64 val);
 bool drm_hdcp_has_changed(struct drm_connector *connector,
-			   struct drm_atomic_state *state);
+			  struct drm_atomic_state *state);
+
+int drm_hdcp_remote_dpcd_read(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *value, size_t len);
+int drm_hdcp_remote_ddc_read(struct drm_hdcp_helper_data *data,
+			     unsigned int offset, u8 *value, size_t len);
+int drm_hdcp_remote_dpcd_write(struct drm_hdcp_helper_data *data,
+			       unsigned int offset, u8 *value, size_t len);
+int drm_hdcp_remote_ddc_write(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *buffer, size_t len);
+
+int drm_hdcp_helper_hdcp1_capable_dp(struct drm_hdcp_helper_data

*data,

include/drm/display/drm_hdcp_helper.h +2 -0
+				     bool *capable);
+int drm_hdcp_helper_hdcp1_capable_hdmi(struct drm_hdcp_helper_data

*data,

include/drm/display/drm_hdcp_helper.h +2 -0
+				       bool *capable);
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp(struct

drm_hdcp_helper_data *data);
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi(struct drm_hdcp_helper_data *data);
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_dp(struct drm_hdcp_helper_data *data,

include/drm/display/drm_hdcp_helper.h +2 -0
+					   u8 *fifo, u8 num_downstream);
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi(struct

drm_hdcp_helper_data *data,

include/drm/display/drm_hdcp_helper.h +6 -0
+					     u8 *fifo, u8 num_downstream);
+int drm_hdcp_helper_wait_r0_dp(struct drm_hdcp_helper_data *data,
+			       unsigned long r0_prime_remaining_us);
+int drm_hdcp_helper_wait_r0_hdmi(struct drm_hdcp_helper_data *data,
+				 unsigned long r0_prime_remaining_us);
+void drm_hdcp_helper_atomic_commit(struct drm_hdcp_helper_data

*data,

include/drm/display/drm_hdcp_helper.h +5 -0
+				   struct drm_atomic_state *state,
+				   struct mutex *driver_mutex);
+
+int drm_hdcp_hdcp1_check_link_registers_dp(struct drm_device *dev,
+					   struct drm_hdcp_helper_data

*data,

include/drm/display/drm_hdcp_helper.h +3 -0
+					   bool *shoud_retry);
+int drm_hdcp_hdcp1_check_link_registers_hdmi(struct drm_device *dev,
+					     struct drm_hdcp_helper_data

*data,

include/drm/display/drm_hdcp_helper.h +2 -0
+					     bool *shoud_retry);
+void drm_hdcp_helper_schedule_hdcp_check(struct

drm_hdcp_helper_data *data);

include/drm/display/drm_hdcp_helper.h +4 -0
+
+struct drm_hdcp_helper_data *drm_hdcp_helper_initialize_dp(
+	struct drm_connector *connector, struct drm_dp_aux *aux,
+	const struct drm_hdcp_helper_funcs *funcs, bool

hdcp_content_type);

include/drm/display/drm_hdcp_helper.h +5 -1
+struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize_hdmi(struct drm_connector *connector,
+				const struct drm_hdcp_helper_funcs *funcs,
+				bool hdcp_content_type);
+void drm_hdcp_helper_destroy(struct drm_hdcp_helper_data *data);
 
 #endif
--

2.40.0.348.gf938b09366-goog

#7 lore raw

Re: [PATCH v8 04/10] drm/hdcp: Expand HDCP helper library for enable/disable/check

In reply to Mark Yacoub
Quoted 11 lines · Mark Yacoub

From: Sean Paul <[email protected]>

Expand upon the HDCP helper library to manage HDCP enable, disable, and check.

Previous to this patch, the majority of the state management and sink interaction is tucked inside the Intel driver with the understanding that once a new platform supported HDCP we could make good decisions about what should be centralized. With the addition of HDCP support for Qualcomm, it's time to migrate the protocol-specific bits of HDCP authentication, key exchange, and link checks to the HDCP helper.

Quoted 14 lines

In terms of functionality, this migration is 1:1 with the Intel driver, however things are laid out a bit differently than with intel_hdcp.c, which is why this is a separate patch from the i915 transition to the helper. On i915, the shim vtable is used to account for HDMI vs. DP vs. DP-MST differences whereas the helper library uses a LUT to account for the register offsets and a remote read function to route the messages. On i915, storing the sink information in the source is done inline whereas now we use the new drm_hdcp_helper_funcs vtable to store and fetch information to/from source hw. Finally, instead of calling enable/disable directly from the driver, we'll leave that decision to the helper and by calling drm_hdcp_helper_atomic_commit() from the driver. All told, this will centralize the protocol and state handling in the helper, ensuring we collect all of our bugs^Wlogic in one place.

I have a generic question regarding vtable design. Currently low level accessors are a part of main helper vtable. They are set by the driver. But when the driver calls drm_hdcp_helper_initialize_hdmi() or drm_hdcp_helper_initialize_dp(), we also know, which low level backend should be used. Can be have a struct describing hdmi vs dp functions and move that to the helper too?

For the reference: earlier revisions had it in form of if (aux) spread all over the low level functions. I asked to split those accessors (thank you!) but I think we can still have them as a set of functions internal to the helper.

Quoted 1,656 lines

---
Changes in v2:
-Fixed set-but-unused variable identified by 0-day Changes in v3:
-Fixed uninitialized variable warning identified by 0-day Changes in v4:
-None
Changes in v5:
-None
Changes in v6:

(quoted diff) +0 -3
-Fixed typo in function descriptions
-Rebased: Moved the new code between drm_hdcp.h and drm_hdcp_helper.c/h
-Add missing headers. Reported-by: kernel test robot <[email protected]>

Changes in v7:
- Add a |driver_data| field to some functions in drm_hdcp_helper_funcs that are called by the driver so drivers can pass anything they such as bridges
- Isolate all non-common code between HDMI and DP into separate functions instead of manually checking for datra->aux
Changes in v8 (suraj):

(quoted diff) +0 -2
-Try hdcp 1.x if hdcp2_capable returns an error instead of goto out.
-set the enabled type to either 1 or 0 depending on the prop as hdcp2

can support either.

  drivers/gpu/drm/display/drm_hdcp_helper.c | 1215 +++++++++++++++++++++
  include/drm/display/drm_hdcp.h            |  287 +++++
  include/drm/display/drm_hdcp_helper.h     |   51 +-
  3 files changed, 1552 insertions(+), 1 deletion(-)
drivers/gpu/drm/display/drm_hdcp_helper.c +1215 -0
index 3ee1a6ae26c53..3bc0308cc95d8 100644
@@ -6,13 +6,18 @@
   * Ramalingam C <[email protected]>
   */
 
+#include <linux/delay.h>
  #include <linux/device.h>
  #include <linux/err.h>
  #include <linux/gfp.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
  #include <linux/export.h>
  #include <linux/slab.h>
  #include <linux/firmware.h>
+#include <linux/workqueue.h>
 
+#include <drm/display/drm_dp_helper.h>
  #include <drm/display/drm_hdcp_helper.h>
  #include <drm/drm_sysfs.h>
  #include <drm/drm_print.h>
@@ -507,3 +512,1213 @@ bool drm_hdcp_has_changed(struct drm_connector *connector,
  	return old_hdcp != new_hdcp;
  }
  EXPORT_SYMBOL(drm_hdcp_has_changed);
+
+struct drm_hdcp_hdcp1_receiver_reg_lut {
+	unsigned int bksv;
+	unsigned int ri;
+	unsigned int aksv;
+	unsigned int an;
+	unsigned int ainfo;
+	unsigned int v[5];
+	unsigned int bcaps;
+	unsigned int bcaps_mask_repeater_present;
+	unsigned int bstatus;
+};
+
+static const struct drm_hdcp_hdcp1_receiver_reg_lut drm_hdcp_hdcp1_ddc_lut = {
+	.bksv = DRM_HDCP_DDC_BKSV,
+	.ri = DRM_HDCP_DDC_RI_PRIME,
+	.aksv = DRM_HDCP_DDC_AKSV,
+	.an = DRM_HDCP_DDC_AN,
+	.ainfo = DRM_HDCP_DDC_AINFO,
+	.v = { DRM_HDCP_DDC_V_PRIME(0), DRM_HDCP_DDC_V_PRIME(1),
+	       DRM_HDCP_DDC_V_PRIME(2), DRM_HDCP_DDC_V_PRIME(3),
+	       DRM_HDCP_DDC_V_PRIME(4) },
+	.bcaps = DRM_HDCP_DDC_BCAPS,
+	.bcaps_mask_repeater_present = DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT,
+	.bstatus = DRM_HDCP_DDC_BSTATUS,
+};
+
+static const struct drm_hdcp_hdcp1_receiver_reg_lut drm_hdcp_hdcp1_dpcd_lut = {
+	.bksv = DP_AUX_HDCP_BKSV,
+	.ri = DP_AUX_HDCP_RI_PRIME,
+	.aksv = DP_AUX_HDCP_AKSV,
+	.an = DP_AUX_HDCP_AN,
+	.ainfo = DP_AUX_HDCP_AINFO,
+	.v = { DP_AUX_HDCP_V_PRIME(0), DP_AUX_HDCP_V_PRIME(1),
+	       DP_AUX_HDCP_V_PRIME(2), DP_AUX_HDCP_V_PRIME(3),
+	       DP_AUX_HDCP_V_PRIME(4) },
+	.bcaps = DP_AUX_HDCP_BCAPS,
+	.bcaps_mask_repeater_present = DP_BCAPS_REPEATER_PRESENT,
+
+	/*
+	 * For some reason the HDMI and DP HDCP specs call this register
+	 * definition by different names. In the HDMI spec, it's called BSTATUS,
+	 * but in DP it's called BINFO.
+	 */
+	.bstatus = DP_AUX_HDCP_BINFO,
+};
+
+/*
+ * Read a DPCD register.
+ *
+ * @data: drm_hdcp_helper_data containing the DisplayPort AUX channel (SST or MST)
+ * @offset: address of the (first) register to read
+ * @value: buffer to store the register values
+ * @len: number of bytes in @value
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_hdcp_remote_dpcd_read(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *value, size_t len)
+{
+	ssize_t ret;
+	struct drm_dp_aux *aux = data->aux;
+
+	ret = drm_dp_dpcd_read(aux, offset, value, len);
+	if (ret != len) {
+		if (ret >= 0)
+			return -EIO;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_dpcd_read);
+
+/*
+ * Read a value from a remote DDC bus.
+ *
+ * @data: drm_hdcp_helper_data containing the i2c_adapter.
+ * @offset: Offset of the value to read.
+ * @value: Pointer to the value to read.
+ * @len: Length of the value to read.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+
+int drm_hdcp_remote_ddc_read(struct drm_hdcp_helper_data *data,
+			     unsigned int offset, u8 *value, size_t len)
+{
+	int ret;
+	struct i2c_adapter *i2c = data->connector->ddc;
+	u8 start = offset & 0xff;
+	struct i2c_msg msgs[] = { { .addr = DRM_HDCP_DDC_ADDR,
+				    .flags = 0,
+				    .len = 1,
+				    .buf = &start },
+				  { .addr = DRM_HDCP_DDC_ADDR,
+				    .flags = I2C_M_RD,
+				    .len = len,
+				    .buf = value } };
+	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
+	if (ret == ARRAY_SIZE(msgs))
+		return 0;
+	return ret >= 0 ? -EIO : ret;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_ddc_read);
+
+/*
+ * Write to a DPCD register.
+ *
+ * @data: drm_hdcp_helper_data containing the DisplayPort AUX channel (SST or MST)
+ * @offset: Offset of the DPCD register to read from
+ * @value: Pointer to store the DPCD register data
+ * @len: Number of bytes to read
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure
+ */
+int drm_hdcp_remote_dpcd_write(struct drm_hdcp_helper_data *data,
+			       unsigned int offset, u8 *value, size_t len)
+{
+	ssize_t ret;
+	struct drm_dp_aux *aux = data->aux;
+
+	ret = drm_dp_dpcd_write(aux, offset, value, len);
+	if (ret != len) {
+		if (ret >= 0)
+			return -EIO;
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_dpcd_write);
+
+/*
+ * Write data to the HDCP DDC slave device
+ *
+ * @data: drm_hdcp_helper_data containing the i2c_adapter.
+ * @offset: the offset to write to
+ * @buffer: the data to write
+ * @size: the size of the data
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_hdcp_remote_ddc_write(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *buffer, size_t len)
+{
+	int ret;
+	u8 *write_buf;
+	struct i2c_msg msg;
+	struct i2c_adapter *i2c = data->connector->ddc;
+
+	write_buf = kzalloc(len + 1, GFP_KERNEL);
+	if (!write_buf)
+		return -ENOMEM;
+
+	write_buf[0] = offset & 0xff;
+	memcpy(&write_buf[1], buffer, len);
+
+	msg.addr = DRM_HDCP_DDC_ADDR;
+	msg.flags = 0, msg.len = len + 1, msg.buf = write_buf;
+
+	ret = i2c_transfer(i2c, &msg, 1);
+	if (ret == 1)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+
+	kfree(write_buf);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_remote_ddc_write);
+
+static bool drm_hdcp_is_ksv_valid(struct drm_hdcp_ksv *ksv)
+{
+	/* Valid Ksv has 20 0's and 20 1's */
+	return hweight32(ksv->words[0]) + hweight32(ksv->words[1]) == 20;
+}
+
+static int drm_hdcp_read_valid_bksv(struct drm_hdcp_helper_data *data,
+				    struct drm_hdcp_ksv *bksv)
+{
+	int ret, i, tries = 2;
+
+	/* HDCP spec states that we must retry the bksv if it is invalid */
+	for (i = 0; i < tries; i++) {
+		ret = data->funcs->remote_read(data, data->hdcp1_lut->bksv,
+					       bksv->bytes, DRM_HDCP_KSV_LEN);
+		if (ret)
+			return ret;
+
+		if (drm_hdcp_is_ksv_valid(bksv))
+			break;
+	}
+	if (i == tries) {
+		drm_dbg_kms(data->connector->dev, "Bksv is invalid %*ph\n",
+			    DRM_HDCP_KSV_LEN, bksv->bytes);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * Check if the sink is capable of HDCP 1.x. DisplayPort has a dedicated bit
+ * for this in DPCD.
+ *
+ * @data: pointer to the HDCP helper data.
+ * @capable: pointer to a bool which will contain true if the sink is capable.
+ *
+ * Returns:
+ * -errno if the transacation between source and sink fails.
+ */
+int drm_hdcp_helper_hdcp1_capable_dp(struct drm_hdcp_helper_data *data,
+				     bool *capable)
+{
+	int ret;
+	u8 bcaps;
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->bcaps, &bcaps, 1);
+	*capable = !ret && (bcaps & DP_BCAPS_HDCP_CAPABLE);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_capable_dp);
+
+/**
+ * Check if the sink is capable of HDCP 1.x. HDMI spec states that transmitters
+ * should use bksv to determine capability
+ *
+ * @data: pointer to the HDCP helper data.
+ * @capable: pointer to a bool which will contain true if the sink is capable.
+ *
+ * Returns:
+ * -errno if the transacation between source and sink fails.
+ */
+int drm_hdcp_helper_hdcp1_capable_hdmi(struct drm_hdcp_helper_data *data,
+				       bool *capable)
+{
+	struct drm_hdcp_ksv bksv;
+	*capable = drm_hdcp_read_valid_bksv(data, &bksv) == 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_capable_hdmi);
+
+static void drm_hdcp_update_value(struct drm_hdcp_helper_data *data, u64 value,
+				  bool update_property)
+{
+	WARN_ON(!mutex_is_locked(&data->mutex));
+
+	data->value = value;
+	if (update_property) {
+		drm_connector_get(data->connector);
+		schedule_work(&data->prop_work);
+	}
+}
+
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp(struct drm_hdcp_helper_data *data)
+{
+	u8 val;
+	u8 mask = DP_BSTATUS_READY;
+	int ret = drm_hdcp_remote_dpcd_read(data, DP_AUX_HDCP_BSTATUS, &val, 1);
+
+	if (ret)
+		return ret;
+	if (val & mask)
+		return 0;
+
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp);
+
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi(struct drm_hdcp_helper_data *data)
+{
+	u8 val;
+	u8 mask = DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
+	int ret = drm_hdcp_remote_ddc_read(data, DRM_HDCP_DDC_BCAPS, &val, 1);
+
+	if (ret)
+		return ret;
+	if (val & mask)
+		return 0;
+
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi);
+
+/* Read the KSV FIFO of the HDCP receive of DP.
+ * Read via 15 byte window (3 entries @ 5 bytes each)
+ *
+ * @fifo: buffer to store the KSV FIFO
+ * @num_downstream: number of devices in the KSV FIFO
+ */
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_dp(struct drm_hdcp_helper_data *data,
+					   u8 *fifo, u8 num_downstream)
+{
+	struct drm_device *dev = data->connector->dev;
+	int i;
+
+	for (i = 0; i < num_downstream; i += 3) {
+		size_t len = min(num_downstream - i, 3) * DRM_HDCP_KSV_LEN;
+		int ret = drm_hdcp_remote_dpcd_read(data, DP_AUX_HDCP_KSV_FIFO,
+						    fifo + i * DRM_HDCP_KSV_LEN,
+						    len);
+		if (ret) {
+			drm_err(dev, "Read ksv[%d] from DP/AUX failed (%d)\n",
+				i, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_read_ksv_fifo_dp);
+
+/* Read the KSV FIFO of the HDCP receive of HDMI.
+ * Over HDMI, read the whole thing at once.
+ *
+ * @fifo: buffer to store the KSV FIFO
+ * @num_downstream: number of devices in the KSV FIFO
+ */
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi(struct drm_hdcp_helper_data *data,
+					     u8 *fifo, u8 num_downstream)
+{
+	struct drm_device *dev = data->connector->dev;
+
+	int ret = drm_hdcp_remote_ddc_read(data, DRM_HDCP_DDC_KSV_FIFO, fifo,
+					   num_downstream * DRM_HDCP_KSV_LEN);
+	if (ret)
+		drm_err(dev, "DDC ksv fifo read failed (%d)\n", ret);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi);
+
+static int drm_hdcp_helper_hdcp1_read_v_prime(struct drm_hdcp_helper_data *data,
+					      u32 *v_prime)
+{
+	struct drm_device *dev = data->connector->dev;
+	int ret, i;
+
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
+		ret = data->funcs->remote_read(data, data->hdcp1_lut->v[i],
+					       (u8 *)&v_prime[i],
+					       DRM_HDCP_V_PRIME_PART_LEN);
+		if (ret) {
+			drm_dbg_kms(dev, "Read v'[%d] from failed (%d)\n", i,
+				    ret);
+			return ret >= 0 ? -EIO : ret;
+		}
+	}
+	return 0;
+}
+
+static int
+drm_hdcp_helper_hdcp1_authenticate_downstream(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	u32 v_prime[DRM_HDCP_V_PRIME_NUM_PARTS];
+	u8 bstatus[DRM_HDCP_BSTATUS_LEN];
+	u8 num_downstream, *ksv_fifo;
+	int ret, i, tries = 3;
+
+	ret = read_poll_timeout(data->funcs->hdcp1_ksv_fifo_ready, ret, !ret,
+				10 * 1000, 5 * 1000 * 1000, false, data);
+	if (ret) {
+		drm_err(dev, "Failed to poll ksv ready, %d\n", ret);
+		return ret;
+	}
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->bstatus, bstatus,
+				       DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		return ret;
+
+	/*
+	 * When repeater reports 0 device count, HDCP1.4 spec allows disabling
+	 * the HDCP encryption. That implies that repeater can't have its own
+	 * display. As there is no consumption of encrypted content in the
+	 * repeater with 0 downstream devices, we are failing the
+	 * authentication.
+	 */
+	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
+	if (num_downstream == 0) {
+		drm_err(dev, "Repeater with zero downstream devices, %*ph\n",
+			DRM_HDCP_BSTATUS_LEN, bstatus);
+		return -EINVAL;
+	}
+
+	ksv_fifo = kcalloc(DRM_HDCP_KSV_LEN, num_downstream, GFP_KERNEL);
+	if (!ksv_fifo)
+		return -ENOMEM;
+
+	ret = data->funcs->hdcp1_read_ksv_fifo(data, ksv_fifo, num_downstream);
+	if (ret) {
+		drm_err(dev, "Failed to read ksv fifo, %d/%d\n", num_downstream,
+			ret);
+		goto out;
+	}
+
+	if (drm_hdcp_check_ksvs_revoked(dev, ksv_fifo, num_downstream)) {
+		drm_err(dev, "Revoked Ksv(s) in ksv_fifo\n");
+		ret = -EPERM;
+		goto out;
+	}
+
+	/*
+	 * When V prime mismatches, DP Spec mandates re-read of
+	 * V prime at least twice.
+	 */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_read_v_prime(data, v_prime);
+		if (ret)
+			continue;
+
+		ret = data->funcs->hdcp1_store_ksv_fifo(connector, ksv_fifo,
+							num_downstream, bstatus,
+							v_prime,
+							data->driver_data);
+		if (!ret)
+			break;
+	}
+	if (ret)
+		drm_err(dev, "Could not validate KSV FIFO with V' %d\n", ret);
+
+out:
+	if (!ret)
+		drm_dbg_kms(dev, "HDCP is enabled (%d downstream devices)\n",
+			    num_downstream);
+
+	kfree(ksv_fifo);
+	return ret;
+}
+
+static int drm_hdcp_helper_hdcp1_validate_ri(struct drm_hdcp_helper_data *data)
+{
+	union {
+		u32 word;
+		u8 bytes[DRM_HDCP_RI_LEN];
+	} ri_prime = { .word = 0 };
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+
+	ret = data->funcs->remote_read(data, data->hdcp1_lut->ri,
+				       ri_prime.bytes, DRM_HDCP_RI_LEN);
+	if (ret) {
+		drm_err(dev, "Failed to read R0' %d\n", ret);
+		return ret;
+	}
+
+	return data->funcs->hdcp1_match_ri(connector, ri_prime.word,
+					   data->driver_data);
+}
+
+/*
+ * Wait for the DP receiver to set the R0' ready bit in the Bstatus register.
+ * This bit is set to 1 when the receiver has received and validated the
+ * receiver ID list and receiver capability list.
+ * The receiver must set this bit to 1 within 200 ms of the receiver ID
+ * list and receiver capability list being received.
+ *
+ * @data: DRM HDCP helper data structure
+ * @r0_prime_remaining_us: timeout for the R0' ready bit
+ *
+ * Return: 0 on success, error code on failure
+ */
+int drm_hdcp_helper_wait_r0_dp(struct drm_hdcp_helper_data *data,
+			       unsigned long r0_prime_remaining_us)
+{
+	u8 val;
+	int ret = 0;
+
+	ret = read_poll_timeout(drm_hdcp_remote_dpcd_read, ret,
+				!ret && (val & DP_BSTATUS_R0_PRIME_READY), 1000,
+				r0_prime_remaining_us, false, data,
+				DP_AUX_HDCP_BSTATUS, &val, 1);
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_wait_r0_dp);
+
+/*
+ * drm_hdcp_helper_wait_r0_hdmi - Wait for R0 to be ready on HDMI
+ * @data: DRM HDCP helper data
+ * @r0_prime_remaining_us: Remaining time to wait for R0 in microseconds
+ *
+ * HDMI doesn't have a R0_READY bit, so we just have to wait the remainder of the
+ * 300 ms timeout.
+ *
+ * Returns:
+ * 0 on success, negative error code on failure
+ */
+
+int drm_hdcp_helper_wait_r0_hdmi(struct drm_hdcp_helper_data *data,
+				 unsigned long r0_prime_remaining_us)
+{
+	usleep_range(r0_prime_remaining_us, r0_prime_remaining_us + 1000);
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_wait_r0_hdmi);
+
+static int drm_hdcp_helper_hdcp1_authenticate(struct drm_hdcp_helper_data *data)
+{
+	union {
+		u32 word;
+		u8 bytes[DRM_HDCP_BSTATUS_LEN];
+	} bstatus;
+	const struct drm_hdcp_helper_funcs *funcs = data->funcs;
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	unsigned long r0_prime_timeout, r0_prime_remaining_us = 0, tmp_jiffies;
+	struct drm_hdcp_ksv aksv;
+	struct drm_hdcp_ksv bksv;
+	struct drm_hdcp_an an;
+	bool repeater_present;
+	int ret, i, tries = 3;
+	u8 bcaps;
+
+	if (funcs->hdcp1_read_an_aksv) {
+		ret = funcs->hdcp1_read_an_aksv(connector, an.words, aksv.words,
+						data->driver_data);
+		if (ret) {
+			drm_err(dev, "Failed to read An/Aksv values, %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = funcs->remote_write(data, data->hdcp1_lut->an, an.bytes,
+					  DRM_HDCP_AN_LEN);
+		if (ret) {
+			drm_err(dev, "Failed to write An to receiver, %d\n",
+				ret);
+			return ret;
+		}
+
+		ret = funcs->remote_write(data, data->hdcp1_lut->aksv,
+					  aksv.bytes, DRM_HDCP_KSV_LEN);
+		if (ret) {
+			drm_err(dev, "Failed to write Aksv to receiver, %d\n",
+				ret);
+			return ret;
+		}
+	} else {
+		ret = funcs->hdcp1_send_an_aksv(connector, data->driver_data);
+		if (ret) {
+			drm_err(dev, "Failed to read An/Aksv values, %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Timeout for R0' to become available. The spec says 100ms from Aksv,
+	 * but some monitors can take longer than this. We'll set the timeout at
+	 * 300ms just to be sure.
+	 */
+	r0_prime_timeout = jiffies + msecs_to_jiffies(300);
+
+	memset(&bksv, 0, sizeof(bksv));
+
+	ret = drm_hdcp_read_valid_bksv(data, &bksv);
+	if (ret < 0)
+		return ret;
+
+	if (drm_hdcp_check_ksvs_revoked(dev, bksv.bytes, 1)) {
+		drm_err(dev, "BKSV is revoked\n");
+		return -EPERM;
+	}
+
+	ret = funcs->remote_read(data, data->hdcp1_lut->bcaps, &bcaps, 1);
+	if (ret)
+		return ret;
+
+	memset(&bstatus, 0, sizeof(bstatus));
+
+	ret = funcs->remote_read(data, data->hdcp1_lut->bstatus, bstatus.bytes,
+				 DRM_HDCP_BSTATUS_LEN);
+	if (ret)
+		return ret;
+
+	if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus.bytes[0]) ||
+	    DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus.bytes[1])) {
+		drm_err(dev, "Max Topology Limit Exceeded, bstatus=%*ph\n",
+			DRM_HDCP_BSTATUS_LEN, bstatus.bytes);
+		return -EPERM;
+	}
+
+	repeater_present = bcaps & data->hdcp1_lut->bcaps_mask_repeater_present;
+
+	ret = funcs->hdcp1_store_receiver_info(connector, bksv.words,
+					       bstatus.word, bcaps,
+					       repeater_present,
+					       data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to store bksv, %d\n", ret);
+		return ret;
+	}
+
+	ret = funcs->hdcp1_enable_encryption(connector, data->driver_data);
+	if (ret)
+		return ret;
+
+	ret = funcs->hdcp1_wait_for_r0(connector, data->driver_data);
+	if (ret)
+		return ret;
+
+	tmp_jiffies = jiffies;
+	if (time_before(tmp_jiffies, r0_prime_timeout))
+		r0_prime_remaining_us =
+			jiffies_to_usecs(r0_prime_timeout - tmp_jiffies);
+
+	ret = funcs->wait_r0(data, r0_prime_remaining_us);
+	if (ret) {
+		drm_err(dev, "R0' did not become ready %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * DP HDCP Spec mandates the two more reattempt to read R0, in case
+	 * of R0 mismatch.
+	 */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_validate_ri(data);
+		if (!ret)
+			break;
+	}
+	if (ret) {
+		drm_err(dev, "Failed to match R0/R0', aborting HDCP %d\n", ret);
+		return ret;
+	}
+
+	if (repeater_present)
+		return drm_hdcp_helper_hdcp1_authenticate_downstream(data);
+
+	drm_dbg_kms(dev, "HDCP is enabled (no repeater present)\n");
+	return 0;
+}
+
+static int drm_hdcp_helper_hdcp1_enable(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int i, ret, tries = 3;
+
+	drm_dbg_kms(dev, "[%s:%d] HDCP is being enabled...\n", connector->name,
+		    connector->base.id);
+
+	/* In case of authentication failures, HDCP spec expects reauth. */
+	for (i = 0; i < tries; i++) {
+		ret = drm_hdcp_helper_hdcp1_authenticate(data);
+		if (!ret)
+			return 0;
+
+		drm_dbg_kms(dev, "HDCP Auth failure (%d)\n", ret);
+
+		/* Ensuring HDCP encryption and signalling are stopped. */
+		data->funcs->hdcp1_disable(data->connector, data->driver_data);
+	}
+
+	drm_err(dev, "HDCP authentication failed (%d tries/%d)\n", tries, ret);
+	return ret;
+}
+
+static inline void
+drm_hdcp_helper_driver_lock(struct drm_hdcp_helper_data *data)
+{
+	if (data->driver_mutex)
+		mutex_lock(data->driver_mutex);
+}
+
+static inline void
+drm_hdcp_helper_driver_unlock(struct drm_hdcp_helper_data *data)
+{
+	if (data->driver_mutex)
+		mutex_unlock(data->driver_mutex);
+}
+
+static int drm_hdcp_helper_enable_hdcp(struct drm_hdcp_helper_data *data,
+				       struct drm_atomic_state *state,
+				       struct mutex *driver_mutex)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_connector_state *conn_state;
+	struct drm_device *dev = connector->dev;
+	unsigned long check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
+	bool capable;
+	int ret = 0;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+
+	mutex_lock(&data->mutex);
+
+	if (data->value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_ENABLED,
+				      true);
+		goto out_data_mutex;
+	}
+
+	drm_WARN_ON(dev, data->driver_mutex != NULL);
+	data->driver_mutex = driver_mutex;
+
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->funcs->setup) {
+		ret = data->funcs->setup(connector, state, data->driver_data);
+		if (ret) {
+			drm_err(dev, "Failed to setup HDCP %d\n", ret);
+			goto out;
+		}
+	}
+
+	if (!data->funcs->are_keys_valid ||
+	    !data->funcs->are_keys_valid(connector, data->driver_data)) {
+		if (data->funcs->load_keys) {
+			ret = data->funcs->load_keys(connector,
+						     data->driver_data);
+			if (ret) {
+				drm_err(dev, "Failed to load HDCP keys %d\n",
+					ret);
+				goto out;
+			}
+		}
+	}
+
+	/*
+	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
+	 * is capable of HDCP2.2, it is preferred to use HDCP2.2.
+	 */
+	ret = data->funcs->hdcp2_capable(connector, &capable,
+					 data->driver_data);
+	if (ret)
+		drm_err(dev, "HDCP 2.x capability check failed %d\n", ret);
+
+	if (!ret && capable) {
+		if (conn_state->content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+			data->enabled_type = DRM_MODE_HDCP_CONTENT_TYPE1;
+		} else {
+			data->enabled_type = DRM_MODE_HDCP_CONTENT_TYPE0;
+		}
+
+		ret = data->funcs->hdcp2_enable(connector, data->driver_data);
+		if (!ret) {
+			check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
+			goto out;
+		}
+	}
+
+	/*
+	 * When HDCP2.2 fails and Content Type is not Type1, HDCP1.4 will
+	 * be attempted.
+	 */
+	ret = data->funcs->hdcp1_capable(data, &capable);
+	if (ret) {
+		drm_err(dev, "HDCP 1.x capability check failed %d\n", ret);
+		goto out;
+	}
+	if (capable &&
+	    conn_state->content_type != DRM_MODE_HDCP_CONTENT_TYPE1) {
+		data->enabled_type = DRM_MODE_HDCP_CONTENT_TYPE0;
+		ret = drm_hdcp_helper_hdcp1_enable(data);
+		if (!ret)
+			check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
+	}
+
+out:
+	if (!ret) {
+		schedule_delayed_work(&data->check_work, check_link_interval);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_ENABLED,
+				      true);
+	}
+
+	drm_hdcp_helper_driver_unlock(data);
+	if (ret)
+		data->driver_mutex = NULL;
+
+out_data_mutex:
+	mutex_unlock(&data->mutex);
+	return ret;
+}
+
+static int drm_hdcp_helper_disable_hdcp(struct drm_hdcp_helper_data *data)
+{
+	int ret = 0;
+
+	mutex_lock(&data->mutex);
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		goto out;
+
+	drm_dbg_kms(data->connector->dev, "[%s:%d] HDCP is being disabled...\n",
+		    data->connector->name, data->connector->base.id);
+
+	drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_UNDESIRED,
+			      true);
+
+	if (data->enabled_type == DRM_MODE_HDCP_CONTENT_TYPE1)
+		ret = data->funcs->hdcp2_disable(data->connector,
+						 data->driver_data);
+	else
+		ret = data->funcs->hdcp1_disable(data->connector,
+						 data->driver_data);
+
+	drm_dbg_kms(data->connector->dev, "HDCP is disabled\n");
+
+out:
+	drm_hdcp_helper_driver_unlock(data);
+	data->driver_mutex = NULL;
+	mutex_unlock(&data->mutex);
+	cancel_delayed_work_sync(&data->check_work);
+	return ret;
+}
+
+/**
+ * drm_hdcp_helper_atomic_commit - Helper for drivers to call during commit to
+ * enable/disable HDCP
+ *
+ * @data: pointer to the @drm_hdcp_helper_data for the connector
+ * @state: pointer to the atomic state being committed
+ * @driver_mutex: driver-provided lock to be used while interacting with the driver
+ *
+ * This function can be used by display drivers to determine when HDCP should be
+ * enabled or disabled based on the connector state. It should be called during
+ * steady-state commits as well as connector enable/disable. The function will
+ * handle the HDCP authentication/encryption logic, calling back into the driver
+ * when source operations are necessary.
+ *
+ * @driver_mutex will be retained and used for the duration of the HDCP session
+ * since it will be needed for link checks and retries. This mutex is useful if
+ * the driver has shared resources across connectors which must be serialized.
+ * For example, driver_mutex can be used for MST connectors sharing a common
+ * encoder which should not be accessed/changed concurrently. When the
+ * connector's session is torn down, the mutex will be forgotten by the helper
+ * for this connector until the next session.
+ */
+void drm_hdcp_helper_atomic_commit(struct drm_hdcp_helper_data *data,
+				   struct drm_atomic_state *state,
+				   struct mutex *driver_mutex)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_connector_state *conn_state;
+	bool type_changed;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!conn_state)
+		return;
+
+	type_changed = conn_state->hdcp_content_type != data->enabled_type;
+
+	if (conn_state->content_protection ==
+	    DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+		drm_hdcp_helper_disable_hdcp(data);
+		return;
+	}
+
+	if (!conn_state->crtc) {
+		drm_hdcp_helper_disable_hdcp(data);
+
+		/* Restore property to DESIRED so it's retried later */
+		if (conn_state->content_protection ==
+		    DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+			mutex_lock(&data->mutex);
+			drm_hdcp_update_value(
+				data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				true);
+			mutex_unlock(&data->mutex);
+		}
+		return;
+	}
+
+	/* Already enabled */
+	if (conn_state->content_protection ==
+	    DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		return;
+
+	/* Disable and re-enable HDCP on content type change */
+	if (type_changed)
+		drm_hdcp_helper_disable_hdcp(data);
+
+	drm_hdcp_helper_enable_hdcp(data, state, driver_mutex);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_atomic_commit);
+
+static void drm_hdcp_helper_prop_work(struct work_struct *work)
+{
+	struct drm_hdcp_helper_data *data =
+		container_of(work, struct drm_hdcp_helper_data, prop_work);
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+
+	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+	mutex_lock(&data->mutex);
+
+	/*
+	 * This worker is only used to flip between ENABLED/DESIRED. Either of
+	 * those to UNDESIRED is handled by core. If value == UNDESIRED,
+	 * we're running just after hdcp has been disabled, so just exit
+	 */
+	if (data->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		drm_hdcp_update_content_protection(connector, data->value);
+
+	mutex_unlock(&data->mutex);
+	drm_modeset_unlock(&dev->mode_config.connection_mutex);
+}
+
+/*
+ * Check the link registers for HDCP 1.x for DP.
+ */
+int drm_hdcp_hdcp1_check_link_registers_dp(struct drm_device *dev,
+					   struct drm_hdcp_helper_data *data,
+					   bool *shoud_retry)
+{
+	u8 bstatus;
+	int ret = drm_hdcp_remote_dpcd_read(data, DP_AUX_HDCP_BSTATUS, &bstatus,
+					    1);
+	*shoud_retry = true;
+
+	if (ret) {
+		*shoud_retry = false;
+		drm_err(dev, "Failed to read dpcd bstatus, %d\n", ret);
+		return ret;
+	}
+	if (bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ))
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdcp_hdcp1_check_link_registers_dp);
+
+/*
+ * Check the link registers for HDCP 1.x for HDMI.
+ */
+int drm_hdcp_hdcp1_check_link_registers_hdmi(struct drm_device *dev,
+					     struct drm_hdcp_helper_data *data,
+					     bool *shoud_retry)
+{
+	int ret = drm_hdcp_helper_hdcp1_validate_ri(data);
+	*shoud_retry = true;
+
+	if (ret)
+		drm_err(dev, "Ri' mismatch, check failed (%d)\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_hdcp_hdcp1_check_link_registers_hdmi);
+
+static int drm_hdcp_hdcp1_check_link(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+	bool should_retry = false;
+
+	if (data->funcs->hdcp1_check_link) {
+		ret = data->funcs->hdcp1_check_link(connector,
+						    data->driver_data);
+		if (ret)
+			goto retry;
+	}
+
+	ret = data->funcs->hdcp1_check_link_registers(connector->dev, data,
+						      &should_retry);
+	if (!ret)
+		return 0;
+	else if (!should_retry)
+		return ret;
+
+retry:
+	drm_err(dev, "[%s:%d] HDCP link failed, retrying authentication\n",
+		connector->name, connector->base.id);
+
+	ret = data->funcs->hdcp1_disable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to disable hdcp (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	ret = drm_hdcp_helper_hdcp1_enable(data);
+	if (ret) {
+		drm_err(dev, "Failed to enable hdcp (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int drm_hdcp_hdcp2_check_link(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector = data->connector;
+	struct drm_device *dev = connector->dev;
+	int ret;
+
+	ret = data->funcs->hdcp2_check_link(connector, data->driver_data);
+	if (!ret)
+		return 0;
+
+	drm_err(dev, "[%s:%d] HDCP2 link failed, retrying authentication\n",
+		connector->name, connector->base.id);
+
+	ret = data->funcs->hdcp2_disable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to disable hdcp2 (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	ret = data->funcs->hdcp2_enable(connector, data->driver_data);
+	if (ret) {
+		drm_err(dev, "Failed to enable hdcp2 (%d)\n", ret);
+		drm_hdcp_update_value(data, DRM_MODE_CONTENT_PROTECTION_DESIRED,
+				      true);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void drm_hdcp_helper_check_work(struct work_struct *work)
+{
+	struct drm_hdcp_helper_data *data = container_of(
+		to_delayed_work(work), struct drm_hdcp_helper_data, check_work);
+	unsigned long check_link_interval;
+
+	mutex_lock(&data->mutex);
+	if (data->value != DRM_MODE_CONTENT_PROTECTION_ENABLED)
+		goto out_data_mutex;
+
+	drm_hdcp_helper_driver_lock(data);
+
+	if (data->enabled_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+		if (drm_hdcp_hdcp2_check_link(data))
+			goto out;
+		check_link_interval = DRM_HDCP2_CHECK_PERIOD_MS;
+	} else {
+		if (drm_hdcp_hdcp1_check_link(data))
+			goto out;
+		check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
+	}
+	schedule_delayed_work(&data->check_work, check_link_interval);
+
+out:
+	drm_hdcp_helper_driver_unlock(data);
+out_data_mutex:
+	mutex_unlock(&data->mutex);
+}
+
+/**
+ * drm_hdcp_helper_schedule_hdcp_check - Schedule a check link cycle.
+ *
+ * @data: Pointer to the HDCP helper data.
+ *
+ * This function will kick off a check link cycle on behalf of the caller. This
+ * can be used by DP short hpd interrupt handlers, where the driver must poke
+ * the helper to check the link is still valid.
+ */
+void drm_hdcp_helper_schedule_hdcp_check(struct drm_hdcp_helper_data *data)
+{
+	schedule_delayed_work(&data->check_work, 0);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_schedule_hdcp_check);
+
+static struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize(struct drm_connector *connector,
+			   const struct drm_hdcp_helper_funcs *funcs,
+			   bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+	int ret;
+
+	out = kzalloc(sizeof(*out), GFP_KERNEL);
+	if (!out)
+		return ERR_PTR(-ENOMEM);
+
+	out->connector = connector;
+	out->funcs = funcs;
+
+	mutex_init(&out->mutex);
+	out->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+
+	INIT_DELAYED_WORK(&out->check_work, drm_hdcp_helper_check_work);
+	INIT_WORK(&out->prop_work, drm_hdcp_helper_prop_work);
+
+	ret = drm_connector_attach_content_protection_property(
+		connector, hdcp_content_type);
+	if (ret) {
+		drm_hdcp_helper_destroy(out);
+		return ERR_PTR(ret);
+	}
+
+	return out;
+}
+
+/**
+ * drm_hdcp_helper_initialize_dp - Initializes the HDCP helpers for a
+ * DisplayPort connector
+ *
+ * @connector: pointer to the DisplayPort connector.
+ * @funcs: pointer to the vtable of HDCP helper funcs for this connector.
+ * @hdcp_content_type: True if the content_type property should be attached.
+ *
+ * This function initializes the HDCP helper for the given DisplayPort connector.
+ * This involves creating the Content Protection property as well as the Content
+ * Type property (if desired). Upon success, it will return a pointer to the
+ * HDCP helper data. Ownership of the underlying memory is ttransferredto the
+ * caller and should be freed using drm_hdcp_helper_destroy().
+ *
+ * Returns:
+ * Pointer to newly created HDCP helper data. PTR_ERR on failure.
+ */
+struct drm_hdcp_helper_data *drm_hdcp_helper_initialize_dp(
+	struct drm_connector *connector, struct drm_dp_aux *aux,
+	const struct drm_hdcp_helper_funcs *funcs, bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+
+	out = drm_hdcp_helper_initialize(connector, funcs, hdcp_content_type);
+	if (IS_ERR(out))
+		return out;
+
+	out->aux = aux;
+	out->hdcp1_lut = &drm_hdcp_hdcp1_dpcd_lut;
+
+	return out;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_initialize_dp);
+
+/**
+ * drm_hdcp_helper_initialize_hdmi - Initializes the HDCP helpers for an HDMI
+ * connector
+ *
+ * @connector: pointer to the HDMI connector.
+ * @funcs: pointer to the vtable of HDCP helper funcs for this connector.
+ * @hdcp_content_type: True if the content_type property should be attached.
+ *
+ * This function initializes the HDCP helper for the given HDMI connector. This
+ * involves creating the Content Protection property as well as the Content Type
+ * property (if desired). Upon success, it will return a pointer to the HDCP
+ * helper data. Ownership of the underlying memory is transferred to the caller
+ * and should be freed using drm_hdcp_helper_destroy().
+ *
+ * Returns:
+ * Pointer to newly created HDCP helper data. PTR_ERR on failure.
+ */
+struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize_hdmi(struct drm_connector *connector,
+				const struct drm_hdcp_helper_funcs *funcs,
+				bool hdcp_content_type)
+{
+	struct drm_hdcp_helper_data *out;
+
+	out = drm_hdcp_helper_initialize(connector, funcs, hdcp_content_type);
+	if (IS_ERR(out))
+		return out;
+
+	out->hdcp1_lut = &drm_hdcp_hdcp1_ddc_lut;
+
+	return out;
+}
+EXPORT_SYMBOL(drm_hdcp_helper_initialize_hdmi);
+
+/**
+ * drm_hdcp_helper_destroy - Destroys the given HDCP helper data.
+ *
+ * @data: Pointer to the HDCP helper data.
+ *
+ * This function cleans up and destroys the HDCP helper data created by
+ * drm_hdcp_helper_initialize_dp() or drm_hdcp_helper_initialize_hdmi().
+ */
+void drm_hdcp_helper_destroy(struct drm_hdcp_helper_data *data)
+{
+	struct drm_connector *connector;
+
+	if (!data)
+		return;
+
+	connector = data->connector;
+
+	/*
+	 * If the connector is registered, it's possible userspace could kick
+	 * off another HDCP enable, which would re-spawn the workers.
+	 */
+	drm_WARN_ON(connector->dev,
+		    connector->registration_state == DRM_CONNECTOR_REGISTERED);
+
+	/*
+	 * Now that the connector is not registered, check_work won't be run,
+	 * but cancel any outstanding instances of it
+	 */
+	cancel_delayed_work_sync(&data->check_work);
+
+	/*
+	 * We don't cancel prop_work in the same way as check_work since it
+	 * requires connection_mutex which could be held while calling this
+	 * function. Instead, we rely on the connector references grabbed before
+	 * scheduling prop_work to ensure the connector is alive when prop_work
+	 * is run. So if we're in the destroy path (which is where this
+	 * function should be called), we're "guaranteed" that prop_work is not
+	 * active (tl;dr This Should Never Happen).
+	 */
+	drm_WARN_ON(connector->dev, work_pending(&data->prop_work));
+
+	kfree(data);
+}
+EXPORT_SYMBOL(drm_hdcp_helper_destroy);
include/drm/display/drm_hdcp.h +287 -0
index 96a99b1377c0e..6ff28db964f13 100644
@@ -9,8 +9,12 @@
  #ifndef _DRM_HDCP_H_
  #define _DRM_HDCP_H_
 
+#include "linux/mutex.h"
+#include "linux/workqueue.h"
  #include <linux/types.h>
 
+#include <drm/drm_device.h>
+
  /* Period of hdcp checks (to ensure we're still authenticated) */
  #define DRM_HDCP_CHECK_PERIOD_MS		(128 * 16)
  #define DRM_HDCP2_CHECK_PERIOD_MS		500
@@ -36,6 +40,7 @@
  #define DRM_HDCP_DDC_BKSV			0x00
  #define DRM_HDCP_DDC_RI_PRIME			0x08
  #define DRM_HDCP_DDC_AKSV			0x10
+#define DRM_HDCP_DDC_AINFO			0x15
  #define DRM_HDCP_DDC_AN				0x18
  #define DRM_HDCP_DDC_V_PRIME(h)			(0x20 + h * 4)
  #define DRM_HDCP_DDC_BCAPS			0x40
@@ -123,6 +128,19 @@
  #define HDCP_2_2_DEV_COUNT_HI(x)		((x) & BIT(0))
  #define HDCP_2_2_DEPTH(x)			(((x) & (0x7 << 1)) >> 1)
 
+struct drm_hdcp_ksv {
+	union {
+		u32 words[2];
+		u8 bytes[DRM_HDCP_KSV_LEN];
+	};
+};
+struct drm_hdcp_an {
+	union {
+		u32 words[2];
+		u8 bytes[DRM_HDCP_AN_LEN];
+	};
+};
+
  struct hdcp2_cert_rx {
  	u8	receiver_id[HDCP_2_2_RECEIVER_ID_LEN];
  	u8	kpub_rx[HDCP_2_2_K_PUB_RX_LEN];
@@ -295,4 +313,273 @@ struct hdcp_srm_header {
  #define DRM_MODE_HDCP_CONTENT_TYPE0		0
  #define DRM_MODE_HDCP_CONTENT_TYPE1		1
 
+struct drm_connector;
+struct drm_atomic_state;
+
+struct drm_hdcp_helper_data {
+	struct mutex mutex;
+	struct mutex *driver_mutex;
+
+	struct drm_connector *connector;
+	const struct drm_hdcp_helper_funcs *funcs;
+
+	u64 value;
+	unsigned int enabled_type;
+
+	struct delayed_work check_work;
+	struct work_struct prop_work;
+
+	struct drm_dp_aux *aux;
+	const struct drm_hdcp_hdcp1_receiver_reg_lut *hdcp1_lut;
+
+	void *driver_data;
+};
+
+/**
+ * struct drm_hdcp_helper_funcs - A vtable of function hooks for the hdcp helper
+ *
+ * These hooks are used by the hdcp helper to call into the driver/connector
+ * code to read/write to hw.
+ */
+struct drm_hdcp_helper_funcs {
+	/**
+	 * @setup - Performs driver-specific setup before hdcp is enabled
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*setup)(struct drm_connector *connector,
+		     struct drm_atomic_state *state, void *driver_data);
+
+	/**
+	 * @are_keys_valid - Checks if the HDCP transmitter keys are valid
+	 *
+	 * Returns: true if the display controller has valid keys loaded
+	 */
+	bool (*are_keys_valid)(struct drm_connector *connector,
+			       void *driver_data);
+
+	/**
+	 * @load_keys - Instructs the driver to load its HDCP transmitter keys
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*load_keys)(struct drm_connector *connector, void *driver_data);
+
+	/**
+	 * @hdcp2_capable - Checks if both source and sink support HDCP 2.x
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_capable)(struct drm_connector *connector, bool *capable,
+			     void *driver_data);
+
+	/**
+	 * @hdcp2_enable - Enables HDCP 2.x on the specified connector
+	 *
+	 * Since we don't have multiple examples of HDCP 2.x enablement, we
+	 * provide the bare minimum support for HDCP 2.x help. Once we have
+	 * more examples, perhaps we can be more helpful.
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_enable)(struct drm_connector *connector, void *driver_data);
+
+	/**
+	 * @hdcp2_check_link - Checks the HDCP 2.x link on a specified connector
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_check_link)(struct drm_connector *connector,
+				void *driver_data);
+
+	/**
+	 * @hdcp2_disable - Disables HDCP 2.x on the specified connector
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp2_disable)(struct drm_connector *connector,
+			     void *driver_data);
+
+	/**
+	 * @hdcp1_read_an_aksv - Reads transmitter's An & Aksv from hardware
+	 *
+	 * Use this function if hardware allows reading the transmitter's An and
+	 * Aksv values from the kernel. If your hardware will not allow this,
+	 * use hdcp1_send_an_aksv() and implement the transmission in the
+	 * driver.
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_read_an_aksv)(struct drm_connector *connector, u32 *an,
+				  u32 *aksv, void *driver_data);
+
+	/**
+	 * @hdcp1_send_an_aksv - Sends transmitter's An & Aksv to the receiver
+	 *
+	 * Only implement this on hardware where An or Aksv are not accessible
+	 * from the kernel. If these values can be read, use
+	 * hdcp1_read_an_aksv() instead.
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_send_an_aksv)(struct drm_connector *connector,
+				  void *driver_data);
+
+	/**
+	 * @hdcp1_store_receiver_info - Stores the receiver's info in the transmitter
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_store_receiver_info)(struct drm_connector *connector,
+					 u32 *ksv, u32 status, u8 caps,
+					 bool repeater_present,
+					 void *driver_data);
+
+	/**
+	 * @hdcp1_enable_encryption - Enables encryption of the outgoing signal
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_enable_encryption)(struct drm_connector *connector,
+				       void *driver_data);
+
+	/**
+	 * @hdcp1_wait_for_r0 - Wait for transmitter to calculate R0
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_wait_for_r0)(struct drm_connector *connector,
+				 void *driver_data);
+
+	/**
+	 * @hdcp1_match_ri - Matches the given Ri from the receiver with Ri in
+	 * the transmitter
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_match_ri)(struct drm_connector *connector, u32 ri_prime,
+			      void *driver_data);
+
+	/**
+	 * @hdcp1_post_encryption - Allows the driver to confirm encryption and
+	 * perform any post-processing
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_post_encryption)(struct drm_connector *connector,
+				     void *driver_data);
+
+	/**
+	 * @hdcp1_store_ksv_fifo - Write the receiver's KSV list to transmitter
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_store_ksv_fifo)(struct drm_connector *connector,
+				    u8 *ksv_fifo, u8 num_downstream,
+				    u8 *bstatus, u32 *vprime,
+				    void *driver_data);
+
+	/**
+	 * @hdcp1_check_link - Allows the driver to check the HDCP 1.x status
+	 * on a specified connector
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_check_link)(struct drm_connector *connector,
+				void *driver_data);
+
+	/**
+	 * @hdcp1_disable - Disables HDCP 1.x on the specified connector
+	 *
+	 * Returns: 0 on success, -errno on failure
+	 */
+	int (*hdcp1_disable)(struct drm_connector *connector,
+			     void *driver_data);
+
+	/**
+	 * @remote_read - Read HDCP keys from the remote HDCP engine
+
+	 * @data: drm_hdcp_helper_data containing the adapter
+	 * @offset: address of the register to read
+	 * @value: Pointer to the value to be filled
+	 * @len: The length of the data to be read
+	 *
+	 * Return 0 on success
+	 */
+	int (*remote_read)(struct drm_hdcp_helper_data *data,
+			   unsigned int offset, u8 *value, size_t len);
+
+	/**
+	 * @remote_write - Write HDCP keys to the remote HDCP engine
+
+	 * @data: drm_hdcp_helper_data containing the adapter
+	 * @offset: address of the register to read
+	 * @value: Pointer to the value to be filled
+	 * @len: The length of the data to be read
+	 *
+	 * Return 0 on success
+	 */
+	int (*remote_write)(struct drm_hdcp_helper_data *data,
+			    unsigned int offset, u8 *value, size_t len);
+
+	/**
+	 * @hdcp1_capable - Checks if the sink is capable of HDCP 1.x.
+	 *
+	 * @data: pointer to the HDCP helper data.
+	 * @capable: pointer to a bool which will contain true if the sink is capable.
+	 *
+	 * Returns:
+	 * -errno if the transacation between source and sink fails.
+	 */
+	int (*hdcp1_capable)(struct drm_hdcp_helper_data *data, bool *capable);
+
+	/**
+	 * @hdcp1_ksv_fifo_ready - Read KSV FIFO ready bit.
+	 *
+	 * @data: pointer to the HDCP helper data.
+	 *
+	 * Returns:
+	 * -errno if the transacation between source and sink fails.
+	 * 0 on success.
+	 * -EAGAIN otherwise.
+	 */
+	int (*hdcp1_ksv_fifo_ready)(struct drm_hdcp_helper_data *data);
+
+	/**
+	 * @wait_r0 - Wait for R0' to become available.
+	 *
+	 * @data: pointer to the HDCP helper data.
+	 * @r0_prime_remaining_us: time remaining for R0' to become available.
+	 *
+	 * Returns 0 on success.
+	 */
+	int (*wait_r0)(struct drm_hdcp_helper_data *data,
+		       unsigned long r0_prime_remaining_us);
+
+	/*
+	 * @hdcp1_check_link_registers - This function checks the HDCP1
+	 * link registers and returns true if the link is valid and false otherwise.
+	 *
+	 * @dev: DRM device for printing error messages
+	 * @data: HDCP helper data
+	 * @should_retry: if true, the caller will retry checking the registers
+	 *
+	 * Returns 0 on success.
+	 */
+	int (*hdcp1_check_link_registers)(struct drm_device *dev,
+					  struct drm_hdcp_helper_data *data,
+					  bool *shoud_retry);
+
+	/**
+	 * @hdcp1_read_ksv_fifo - Read HDCP1.x KSV FIFO
+	 * @data: Helper data
+	 * @fifo: Buffer to store values
+	 * @num_downstream: Number of downstream devices
+	 *
+	 * Return: Zero on success or negative error code on failure.
+	 */
+	int (*hdcp1_read_ksv_fifo)(struct drm_hdcp_helper_data *data, u8 *fifo,
+				   u8 num_downstream);
+};
+
  #endif
include/drm/display/drm_hdcp_helper.h +50 -1
index 703421fcdf96c..a2e25bad78dbd 100644
@@ -15,11 +15,60 @@ struct drm_atomic_state;
  struct drm_device;
  struct drm_connector;
 
+struct drm_hdcp_helper_data;
+struct drm_dp_aux;
+struct i2c_adapter;
+struct mutex;
+
  int drm_hdcp_check_ksvs_revoked(struct drm_device *dev, u8 *ksvs, u32 ksv_count);
  int drm_connector_attach_content_protection_property(struct drm_connector *connector,
  						     bool hdcp_content_type);
  void drm_hdcp_update_content_protection(struct drm_connector *connector, u64 val);
  bool drm_hdcp_has_changed(struct drm_connector *connector,
-			   struct drm_atomic_state *state);
+			  struct drm_atomic_state *state);
+
+int drm_hdcp_remote_dpcd_read(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *value, size_t len);
+int drm_hdcp_remote_ddc_read(struct drm_hdcp_helper_data *data,
+			     unsigned int offset, u8 *value, size_t len);
+int drm_hdcp_remote_dpcd_write(struct drm_hdcp_helper_data *data,
+			       unsigned int offset, u8 *value, size_t len);
+int drm_hdcp_remote_ddc_write(struct drm_hdcp_helper_data *data,
+			      unsigned int offset, u8 *buffer, size_t len);
+
+int drm_hdcp_helper_hdcp1_capable_dp(struct drm_hdcp_helper_data *data,
+				     bool *capable);
+int drm_hdcp_helper_hdcp1_capable_hdmi(struct drm_hdcp_helper_data *data,
+				       bool *capable);
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp(struct drm_hdcp_helper_data *data);
+int drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi(struct drm_hdcp_helper_data *data);
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_dp(struct drm_hdcp_helper_data *data,
+					   u8 *fifo, u8 num_downstream);
+int drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi(struct drm_hdcp_helper_data *data,
+					     u8 *fifo, u8 num_downstream);
+int drm_hdcp_helper_wait_r0_dp(struct drm_hdcp_helper_data *data,
+			       unsigned long r0_prime_remaining_us);
+int drm_hdcp_helper_wait_r0_hdmi(struct drm_hdcp_helper_data *data,
+				 unsigned long r0_prime_remaining_us);
+void drm_hdcp_helper_atomic_commit(struct drm_hdcp_helper_data *data,
+				   struct drm_atomic_state *state,
+				   struct mutex *driver_mutex);
+
+int drm_hdcp_hdcp1_check_link_registers_dp(struct drm_device *dev,
+					   struct drm_hdcp_helper_data *data,
+					   bool *shoud_retry);
+int drm_hdcp_hdcp1_check_link_registers_hdmi(struct drm_device *dev,
+					     struct drm_hdcp_helper_data *data,
+					     bool *shoud_retry);
+void drm_hdcp_helper_schedule_hdcp_check(struct drm_hdcp_helper_data *data);
+
+struct drm_hdcp_helper_data *drm_hdcp_helper_initialize_dp(
+	struct drm_connector *connector, struct drm_dp_aux *aux,
+	const struct drm_hdcp_helper_funcs *funcs, bool hdcp_content_type);
+struct drm_hdcp_helper_data *
+drm_hdcp_helper_initialize_hdmi(struct drm_connector *connector,
+				const struct drm_hdcp_helper_funcs *funcs,
+				bool hdcp_content_type);
+void drm_hdcp_helper_destroy(struct drm_hdcp_helper_data *data);
 
  #endif
Signature · 2 lines
With best wishes
Dmitry
#8 lore raw

PATCH v8 5/10 drm/i915/hdcp: Consolidate HDCP setup/state cache

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Stick all of the setup for HDCP into a dedicated function. No functional change, but this will facilitate moving HDCP logic into helpers.

Change-Id Ib358a503fa4520d072e477f708f1705b19f1c4fc

---
Changes in v2:
-None
Changes in v3:
-None
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-None
Changes in v7:
- None
Changes in v8:
-None

 drivers/gpu/drm/i915/display/intel_hdcp.c | 52 +++++++++++++++--------
 1 file changed, 35 insertions(+), 17 deletions(-)
drivers/gpu/drm/i915/display/intel_hdcp.c +35 -17
index 396d2cef000aa..0a20bc41be55d 100644
@@ -2190,6 +2190,37 @@ static enum mei_fw_tc intel_get_mei_fw_tc(enum transcoder cpu_transcoder)
 	}
 }
 
+static int
+_intel_hdcp_setup(struct intel_connector *connector,
+		  const struct intel_crtc_state *pipe_config, u8 content_type)
+{
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+	struct intel_digital_port *dig_port = intel_attached_dig_port(connector);
+	struct intel_hdcp *hdcp = &connector->hdcp;
+	int ret = 0;
+
+	if (!connector->encoder) {
+		drm_err(&dev_priv->drm, "[%s:%d] encoder is not initialized\n",
+			connector->base.name, connector->base.base.id);
+		return -ENODEV;
+	}
+
+	hdcp->content_type = content_type;
+
+	if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) {
+		hdcp->cpu_transcoder = pipe_config->mst_master_transcoder;
+		hdcp->stream_transcoder = pipe_config->cpu_transcoder;
+	} else {
+		hdcp->cpu_transcoder = pipe_config->cpu_transcoder;
+		hdcp->stream_transcoder = INVALID_TRANSCODER;
+	}
+
+	if (DISPLAY_VER(dev_priv) >= 12)
+		dig_port->hdcp_port_data.fw_tc = intel_get_mei_fw_tc(hdcp->cpu_transcoder);
+
+	return ret;
+}
+
 static int initialize_hdcp_port_data(struct intel_connector *connector,
 				     struct intel_digital_port *dig_port,
 				     const struct intel_hdcp_shim *shim)
@@ -2329,28 +2360,14 @@ int intel_hdcp_enable(struct intel_connector *connector,
 	if (!hdcp->shim)
 		return -ENOENT;
 
-	if (!connector->encoder) {
-		drm_err(&dev_priv->drm, "[%s:%d] encoder is not initialized\n",
-			connector->base.name, connector->base.base.id);
-		return -ENODEV;
-	}
-
 	mutex_lock(&hdcp->mutex);
 	mutex_lock(&dig_port->hdcp_mutex);
 	drm_WARN_ON(&dev_priv->drm,
 		    hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED);
-	hdcp->content_type = content_type;
-
-	if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) {
-		hdcp->cpu_transcoder = pipe_config->mst_master_transcoder;
-		hdcp->stream_transcoder = pipe_config->cpu_transcoder;
-	} else {
-		hdcp->cpu_transcoder = pipe_config->cpu_transcoder;
-		hdcp->stream_transcoder = INVALID_TRANSCODER;
-	}
 
-	if (DISPLAY_VER(dev_priv) >= 12)
-		dig_port->hdcp_port_data.fw_tc = intel_get_mei_fw_tc(hdcp->cpu_transcoder);
+	ret = _intel_hdcp_setup(connector, pipe_config, content_type);
+	if (ret)
+		goto out;
 
 	/*
 	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
@@ -2378,6 +2395,7 @@ int intel_hdcp_enable(struct intel_connector *connector,
 					true);
 	}
 
+out:
 	mutex_unlock(&dig_port->hdcp_mutex);
 	mutex_unlock(&hdcp->mutex);
 	return ret;
Signature · 1 line
2.40.0.348.gf938b09366-goog
#9 lore raw

PATCH v8 6/10 drm/i915/hdcp: Retain hdcp_capable return codes

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

The shim functions return error codes, but they are discarded in intel_hdcp.c. This patch plumbs the return codes through so they are properly handled.

---
Changes in v2:
-None
Changes in v3:
-None
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebased
Changes in v7:
-None
Changes in v8:
-None

 .../drm/i915/display/intel_display_debugfs.c  |  9 +++-
 drivers/gpu/drm/i915/display/intel_hdcp.c     | 51 ++++++++++---------
 drivers/gpu/drm/i915/display/intel_hdcp.h     |  4 +-
 3 files changed, 37 insertions(+), 27 deletions(-)
drivers/gpu/drm/i915/display/intel_display_debugfs.c +7 -2
index 7bcd90384a46d..a14b86a07e545 100644
@@ -494,6 +494,7 @@ static void intel_panel_info(struct seq_file *m,
 static void intel_hdcp_info(struct seq_file *m,
 			    struct intel_connector *intel_connector)
 {
+	int ret;
 	bool hdcp_cap, hdcp2_cap;
 
 	if (!intel_connector->hdcp.shim) {
@@ -501,8 +502,12 @@ static void intel_hdcp_info(struct seq_file *m,
 		goto out;
 	}
 
-	hdcp_cap = intel_hdcp_capable(intel_connector);
-	hdcp2_cap = intel_hdcp2_capable(intel_connector);
+	ret = intel_hdcp_capable(intel_connector, &hdcp_cap);
+	if (ret)
+		hdcp_cap = false;
+	ret = intel_hdcp2_capable(intel_connector, &hdcp2_cap);
+	if (ret)
+		hdcp2_cap = false;
 
 	if (hdcp_cap)
 		seq_puts(m, "HDCP1.4 ");
drivers/gpu/drm/i915/display/intel_hdcp.c +28 -23

112 lines not on this page · raw message

drivers/gpu/drm/i915/display/intel_hdcp.h +2 -2
index 7c5fd84a7b65a..f06f6e5a2b1ad 100644
@@ -33,8 +33,8 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state,
 			    const struct intel_crtc_state *crtc_state,
 			    const struct drm_connector_state *conn_state);
 bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
-bool intel_hdcp_capable(struct intel_connector *connector);
-bool intel_hdcp2_capable(struct intel_connector *connector);
+int intel_hdcp_capable(struct intel_connector *connector, bool *capable);
+int intel_hdcp2_capable(struct intel_connector *connector, bool *capable);
 void intel_hdcp_component_init(struct drm_i915_private *dev_priv);
 void intel_hdcp_component_fini(struct drm_i915_private *dev_priv);
 void intel_hdcp_cleanup(struct intel_connector *connector);
Signature · 1 line
2.40.0.348.gf938b09366-goog
Reviewed-by on v8 6/10
#10 lore raw

RE: [PATCH v8 06/10] drm/i915/hdcp: Retain hdcp_capable return codes

Quoted 201 lines

-----Original Message-----
From: Mark Yacoub <[email protected]>
Sent: Saturday, April 1, 2023 3:42 AM
To: Jani Nikula <[email protected]>; Joonas Lahtinen <[email protected]>; Vivi, Rodrigo <[email protected]>; Tvrtko Ursulin <[email protected]>; David Airlie <[email protected]>; Daniel Vetter <[email protected]>
Cc: [email protected]; Kandpal, Suraj <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; Nikula, Jani <[email protected]>; Mark Yacoub <[email protected]>; linux- [email protected]
Subject: [PATCH v8 06/10] drm/i915/hdcp: Retain hdcp_capable return codes

From: Sean Paul <[email protected]>

The shim functions return error codes, but they are discarded in intel_hdcp.c. This patch plumbs the return codes through so they are properly handled.

---
Changes in v2:
-None
Changes in v3:
-None
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebased
Changes in v7:
-None
Changes in v8:

(quoted diff) +0 -1
-None
 
 .../drm/i915/display/intel_display_debugfs.c  |  9 +++-
 drivers/gpu/drm/i915/display/intel_hdcp.c     | 51 ++++++++++---------
 drivers/gpu/drm/i915/display/intel_hdcp.h     |  4 +-
 3 files changed, 37 insertions(+), 27 deletions(-)
drivers/gpu/drm/i915/display/intel_display_debugfs.c +7 -2
index 7bcd90384a46d..a14b86a07e545 100644
@@ -494,6 +494,7 @@ static void intel_panel_info(struct seq_file *m,  static
 void intel_hdcp_info(struct seq_file *m,
 			    struct intel_connector *intel_connector)  {
+	int ret;
 	bool hdcp_cap, hdcp2_cap;
 
 	if (!intel_connector->hdcp.shim) {
@@ -501,8 +502,12 @@ static void intel_hdcp_info(struct seq_file *m,
 		goto out;
 	}
 
-	hdcp_cap = intel_hdcp_capable(intel_connector);
-	hdcp2_cap = intel_hdcp2_capable(intel_connector);
+	ret = intel_hdcp_capable(intel_connector, &hdcp_cap);
+	if (ret)
+		hdcp_cap = false;
+	ret = intel_hdcp2_capable(intel_connector, &hdcp2_cap);
+	if (ret)
+		hdcp2_cap = false;
 
 	if (hdcp_cap)
 		seq_puts(m, "HDCP1.4 ");
drivers/gpu/drm/i915/display/intel_hdcp.c +18 -15
index 0a20bc41be55d..61a862ae1f286 100644
@@ -177,50 +177,49 @@ int intel_hdcp_read_valid_bksv(struct
 intel_digital_port *dig_port,  }
 
 /* Is HDCP1.4 capable on Platform and Sink */ -bool
 intel_hdcp_capable(struct intel_connector *connector)
+int intel_hdcp_capable(struct intel_connector *connector, bool
+*capable)
 {
 	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
 	const struct intel_hdcp_shim *shim = connector->hdcp.shim;
-	bool capable = false;
 	u8 bksv[5];
 
+	*capable = false;
+
 	if (!shim)
-		return capable;
+		return 0;
 
-	if (shim->hdcp_capable) {
-		shim->hdcp_capable(dig_port, &capable);
-	} else {
-		if (!intel_hdcp_read_valid_bksv(dig_port, shim, bksv))
-			capable = true;
-	}
+	if (shim->hdcp_capable)
+		return shim->hdcp_capable(dig_port, capable);
+
+	if (!intel_hdcp_read_valid_bksv(dig_port, shim, bksv))
+		*capable = true;
 
-	return capable;
+	return 0;
 }
 
 /* Is HDCP2.2 capable on Platform and Sink */ -bool
 intel_hdcp2_capable(struct intel_connector *connector)
+int intel_hdcp2_capable(struct intel_connector *connector, bool
+*capable)
 {
 	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	struct intel_hdcp *hdcp = &connector->hdcp;
-	bool capable = false;
+
+	*capable = false;
 
 	/* I915 support for HDCP2.2 */
 	if (!hdcp->hdcp2_supported)
-		return false;
+		return 0;
 
 	/* MEI interface is solid */
 	mutex_lock(&dev_priv->display.hdcp.comp_mutex);
 	if (!dev_priv->display.hdcp.comp_added ||  !dev_priv-
 >display.hdcp.master) {
 		mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
-		return false;
+		return 0;
 	}
 	mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
 
 	/* Sink's capability for HDCP2.2 */
-	hdcp->shim->hdcp_2_2_capable(dig_port, &capable);
-
-	return capable;
+	return hdcp->shim->hdcp_2_2_capable(dig_port, capable);
 }
 
 static bool intel_hdcp_in_use(struct drm_i915_private *dev_priv, @@ -

2355,6 +2354,7 @@ int intel_hdcp_enable(struct intel_connector *connector,
struct intel_digital_port *dig_port =
intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
 	struct intel_hdcp *hdcp = &connector->hdcp;
 	unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
+	bool capable;
 	int ret = -EINVAL;
 
 	if (!hdcp->shim)
@@ -2373,21 +2373,27 @@ int intel_hdcp_enable(struct intel_connector
 *connector,
 	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the

setup

drivers/gpu/drm/i915/display/intel_hdcp.c +3 -2
 	 * is capable of HDCP2.2, it is preferred to use HDCP2.2.
 	 */
-	if (intel_hdcp2_capable(connector)) {
+	ret = intel_hdcp2_capable(connector, &capable);
+	if (capable) {
 		ret = _intel_hdcp2_enable(connector);
-		if (!ret)
+		if (!ret) {
 			check_link_interval =

DRM_HDCP2_CHECK_PERIOD_MS;

drivers/gpu/drm/i915/display/intel_hdcp.c +7 -2
+			goto out;
+		}
 	}
 
 	/*
 	 * When HDCP2.2 fails and Content Type is not Type1, HDCP1.4 will
 	 * be attempted.
 	 */
-	if (ret && intel_hdcp_capable(connector) &&
-	    hdcp->content_type != DRM_MODE_HDCP_CONTENT_TYPE1) {
+	ret = intel_hdcp_capable(connector, &capable);
+	if (ret)
+		goto out;
+
+	if (capable && hdcp->content_type !=

DRM_MODE_HDCP_CONTENT_TYPE1)

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -1
 		ret = _intel_hdcp_enable(connector);
-	}
 
+out:
 	if (!ret) {
 		schedule_delayed_work(&hdcp->check_work,

check_link_interval);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -0
 		intel_hdcp_update_value(connector,

drivers/gpu/drm/i915/display/intel_hdcp.c int intel_hdcp_enable(struct intel_connector · line 2403

Quoted 3 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -0
@@ -2395,7 +2401,6 @@ int intel_hdcp_enable(struct intel_connector
 *connector,
 					true);
 	}

Hi Mark,
These changes here in intel_hdcp_enable are not necessary as this function is getting removed entirely in the next patch.

With that fixed

Quoted 25 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -1
-out:
 	mutex_unlock(&dig_port->hdcp_mutex);
 	mutex_unlock(&hdcp->mutex);
 	return ret;
drivers/gpu/drm/i915/display/intel_hdcp.h +0 -0
index 7c5fd84a7b65a..f06f6e5a2b1ad 100644
@@ -33,8 +33,8 @@ void intel_hdcp_update_pipe(struct intel_atomic_state
 *state,
 			    const struct intel_crtc_state *crtc_state,
 			    const struct drm_connector_state *conn_state);

bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port); -bool intel_hdcp_capable(struct intel_connector *connector); -bool intel_hdcp2_capable(struct intel_connector *connector);

drivers/gpu/drm/i915/display/intel_hdcp.h +3 -0
+int intel_hdcp_capable(struct intel_connector *connector, bool
+*capable); int intel_hdcp2_capable(struct intel_connector *connector,
+bool *capable);
 void intel_hdcp_component_init(struct drm_i915_private *dev_priv);  void

intel_hdcp_component_fini(struct drm_i915_private *dev_priv); void intel_hdcp_cleanup(struct intel_connector *connector); --
2.40.0.348.gf938b09366-goog

#11 lore raw

RE: [PATCH v8 06/10] drm/i915/hdcp: Retain hdcp_capable return codes

Quoted 228 lines

-----Original Message-----
From: Kandpal, Suraj
Sent: Monday, April 3, 2023 12:12 PM
To: Mark Yacoub <[email protected]>; Jani Nikula <[email protected]>; Joonas Lahtinen <[email protected]>; Vivi, Rodrigo <[email protected]>; Tvrtko Ursulin <[email protected]>; David Airlie <[email protected]>; Daniel Vetter <[email protected]>
Cc: [email protected]; [email protected]; dri- [email protected]; [email protected]; intel- [email protected]; Nikula, Jani <[email protected]>; linux- [email protected]
Subject: RE: [PATCH v8 06/10] drm/i915/hdcp: Retain hdcp_capable return codes

Quoted 201 lines

-----Original Message-----
From: Mark Yacoub <[email protected]>
Sent: Saturday, April 1, 2023 3:42 AM
To: Jani Nikula <[email protected]>; Joonas Lahtinen <[email protected]>; Vivi, Rodrigo <[email protected]>; Tvrtko Ursulin <[email protected]>; David Airlie <[email protected]>; Daniel Vetter <[email protected]>
Cc: [email protected]; Kandpal, Suraj <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; Nikula, Jani <[email protected]>; Mark Yacoub <[email protected]>; linux- [email protected]
Subject: [PATCH v8 06/10] drm/i915/hdcp: Retain hdcp_capable return codes

From: Sean Paul <[email protected]>

The shim functions return error codes, but they are discarded in intel_hdcp.c. This patch plumbs the return codes through so they are properly handled.

---
Changes in v2:
-None
Changes in v3:
-None
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebased
Changes in v7:
-None
Changes in v8:

(quoted diff) +0 -1
-None
 
 .../drm/i915/display/intel_display_debugfs.c  |  9 +++-
 drivers/gpu/drm/i915/display/intel_hdcp.c     | 51 ++++++++++---------
 drivers/gpu/drm/i915/display/intel_hdcp.h     |  4 +-
 3 files changed, 37 insertions(+), 27 deletions(-)
drivers/gpu/drm/i915/display/intel_display_debugfs.c +7 -2
index 7bcd90384a46d..a14b86a07e545 100644
@@ -494,6 +494,7 @@ static void intel_panel_info(struct seq_file *m,
 static void intel_hdcp_info(struct seq_file *m,
 			    struct intel_connector *intel_connector)  {
+	int ret;
 	bool hdcp_cap, hdcp2_cap;
 
 	if (!intel_connector->hdcp.shim) {
@@ -501,8 +502,12 @@ static void intel_hdcp_info(struct seq_file *m,
 		goto out;
 	}
 
-	hdcp_cap = intel_hdcp_capable(intel_connector);
-	hdcp2_cap = intel_hdcp2_capable(intel_connector);
+	ret = intel_hdcp_capable(intel_connector, &hdcp_cap);
+	if (ret)
+		hdcp_cap = false;
+	ret = intel_hdcp2_capable(intel_connector, &hdcp2_cap);
+	if (ret)
+		hdcp2_cap = false;
 
 	if (hdcp_cap)
 		seq_puts(m, "HDCP1.4 ");
drivers/gpu/drm/i915/display/intel_hdcp.c +18 -15
index 0a20bc41be55d..61a862ae1f286 100644
@@ -177,50 +177,49 @@ int intel_hdcp_read_valid_bksv(struct
 intel_digital_port *dig_port,  }
 
 /* Is HDCP1.4 capable on Platform and Sink */ -bool
 intel_hdcp_capable(struct intel_connector *connector)
+int intel_hdcp_capable(struct intel_connector *connector, bool
+*capable)
 {
 	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
 	const struct intel_hdcp_shim *shim = connector->hdcp.shim;
-	bool capable = false;
 	u8 bksv[5];
 
+	*capable = false;
+
 	if (!shim)
-		return capable;
+		return 0;
 
-	if (shim->hdcp_capable) {
-		shim->hdcp_capable(dig_port, &capable);
-	} else {
-		if (!intel_hdcp_read_valid_bksv(dig_port, shim, bksv))
-			capable = true;
-	}
+	if (shim->hdcp_capable)
+		return shim->hdcp_capable(dig_port, capable);
+
+	if (!intel_hdcp_read_valid_bksv(dig_port, shim, bksv))
+		*capable = true;
 
-	return capable;
+	return 0;
 }
 
 /* Is HDCP2.2 capable on Platform and Sink */ -bool
 intel_hdcp2_capable(struct intel_connector *connector)
+int intel_hdcp2_capable(struct intel_connector *connector, bool
+*capable)
 {
 	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	struct intel_hdcp *hdcp = &connector->hdcp;
-	bool capable = false;
+
+	*capable = false;
 
 	/* I915 support for HDCP2.2 */
 	if (!hdcp->hdcp2_supported)
-		return false;
+		return 0;
 
 	/* MEI interface is solid */
 	mutex_lock(&dev_priv->display.hdcp.comp_mutex);
 	if (!dev_priv->display.hdcp.comp_added ||  !dev_priv-
 >display.hdcp.master) {
 		mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
-		return false;
+		return 0;
 	}
 	mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
 
 	/* Sink's capability for HDCP2.2 */
-	hdcp->shim->hdcp_2_2_capable(dig_port, &capable);
-
-	return capable;
+	return hdcp->shim->hdcp_2_2_capable(dig_port, capable);
 }
 
 static bool intel_hdcp_in_use(struct drm_i915_private *dev_priv, @@ -

2355,6 +2354,7 @@ int intel_hdcp_enable(struct intel_connector *connector,
struct intel_digital_port *dig_port =
intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
 	struct intel_hdcp *hdcp = &connector->hdcp;
 	unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
+	bool capable;
 	int ret = -EINVAL;
 
 	if (!hdcp->shim)
@@ -2373,21 +2373,27 @@ int intel_hdcp_enable(struct intel_connector
 *connector,
 	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the

setup

drivers/gpu/drm/i915/display/intel_hdcp.c +3 -2
 	 * is capable of HDCP2.2, it is preferred to use HDCP2.2.
 	 */
-	if (intel_hdcp2_capable(connector)) {
+	ret = intel_hdcp2_capable(connector, &capable);
+	if (capable) {
 		ret = _intel_hdcp2_enable(connector);
-		if (!ret)
+		if (!ret) {
 			check_link_interval =

DRM_HDCP2_CHECK_PERIOD_MS;

drivers/gpu/drm/i915/display/intel_hdcp.c +7 -2
+			goto out;
+		}
 	}
 
 	/*
 	 * When HDCP2.2 fails and Content Type is not Type1, HDCP1.4 will
 	 * be attempted.
 	 */
-	if (ret && intel_hdcp_capable(connector) &&
-	    hdcp->content_type != DRM_MODE_HDCP_CONTENT_TYPE1) {
+	ret = intel_hdcp_capable(connector, &capable);
+	if (ret)
+		goto out;
+
+	if (capable && hdcp->content_type !=

DRM_MODE_HDCP_CONTENT_TYPE1)

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -1
 		ret = _intel_hdcp_enable(connector);
-	}
 
+out:
 	if (!ret) {
 		schedule_delayed_work(&hdcp->check_work,

check_link_interval);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -0
 		intel_hdcp_update_value(connector,

drivers/gpu/drm/i915/display/intel_hdcp.c int intel_hdcp_enable(struct intel_connector · line 2403

Quoted 3 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -0
@@ -2395,7 +2401,6 @@ int intel_hdcp_enable(struct intel_connector
 *connector,
 					true);
 	}

Hi Mark,
These changes here in intel_hdcp_enable are not necessary as this function is getting removed entirely in the next patch.

Quoted 4 lines

With that fixed

Just to clarify I mean the addition of "goto out" I understand since you are changing the capability function You need those changes to build this patch.

Regards,
Suraj Kandpal

Quoted 26 lines
Quoted 11 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -1
-out:
 	mutex_unlock(&dig_port->hdcp_mutex);
 	mutex_unlock(&hdcp->mutex);
 	return ret;
drivers/gpu/drm/i915/display/intel_hdcp.h +0 -0
index 7c5fd84a7b65a..f06f6e5a2b1ad 100644

drivers/gpu/drm/i915/display/intel_hdcp.h void intel_hdcp_update_pipe(struct · line 35

Quoted 3 lines
drivers/gpu/drm/i915/display/intel_hdcp.h +0 -0
@@ -33,8 +33,8 @@ void intel_hdcp_update_pipe(struct
 intel_atomic_state *state,
 			    const struct intel_crtc_state *crtc_state,
 			    const struct drm_connector_state *conn_state);

bool

Quoted 11 lines
drivers/gpu/drm/i915/display/intel_hdcp.h +3 -1
 is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
-bool intel_hdcp_capable(struct intel_connector *connector); -bool
 intel_hdcp2_capable(struct intel_connector *connector);
+int intel_hdcp_capable(struct intel_connector *connector, bool
+*capable); int intel_hdcp2_capable(struct intel_connector *connector,
+bool *capable);
 void intel_hdcp_component_init(struct drm_i915_private *dev_priv);

void intel_hdcp_component_fini(struct drm_i915_private *dev_priv); void intel_hdcp_cleanup(struct intel_connector *connector); --
2.40.0.348.gf938b09366-goog

#12 lore raw

PATCH v8 7/10 drm/i915/hdcp: Use HDCP helpers for i915

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Now that all of the HDCP 1.x logic has been migrated to the central HDCP helpers, use it in the i915 driver.

The majority of the driver code for HDCP 1.x will live in intel_hdcp.c, however there are a few helper hooks which are connector-specific and need to be partially or fully implemented in the intel_dp_hdcp.c or intel_hdmi.c.

We'll leave most of the HDCP 2.x code alone since we don't have another implementation of HDCP 2.x to use as reference for what should and should not live in the drm helpers. The helper will call the overly general enable/disable/is_capable HDCP 2.x callbacks and leave the interesting stuff for the driver. Once we have another HDCP 2.x implementation, we should do a similar migration.

---
Changes in v2:
-Fix mst helper function pointer reported by 0-day
Changes in v3:
-Add forward declaration for drm_atomic_state in intel_hdcp.h identified
by 0-day
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebased.
Changes in v7:
-Added to drm_hdcp_helper_funcs new functions that are unique between DP and HDMI
-Adjusted the function signatures to take "driver data" Changes in v8:
-None

 drivers/gpu/drm/i915/display/intel_ddi.c      |  32 +-
 .../drm/i915/display/intel_display_debugfs.c  |   6 +-
 .../drm/i915/display/intel_display_types.h    |  51 +-
 drivers/gpu/drm/i915/display/intel_dp_hdcp.c  | 368 +++----
 drivers/gpu/drm/i915/display/intel_dp_mst.c   |  16 +-
 drivers/gpu/drm/i915/display/intel_hdcp.c     | 960 ++++--------------
 drivers/gpu/drm/i915/display/intel_hdcp.h     |  37 +-
 drivers/gpu/drm/i915/display/intel_hdmi.c     | 276 ++---
 8 files changed, 484 insertions(+), 1262 deletions(-)
drivers/gpu/drm/i915/display/intel_ddi.c +24 -8
index 254559abedfba..8a2f20c929e9c 100644
@@ -28,6 +28,7 @@
 #include <linux/string_helpers.h>
 
 #include <drm/display/drm_scdc_helper.h>
+#include <drm/display/drm_hdcp_helper.h>
 #include <drm/drm_privacy_screen_consumer.h>
 
 #include "i915_drv.h"
@@ -2956,6 +2957,10 @@ static void intel_enable_ddi(struct intel_atomic_state *state,
 			     const struct intel_crtc_state *crtc_state,
 			     const struct drm_connector_state *conn_state)
 {
+	struct intel_connector *connector =
+		to_intel_connector(conn_state->connector);
+	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
 	drm_WARN_ON(state->base.dev, crtc_state->has_pch_encoder);
 
 	if (!intel_crtc_is_bigjoiner_slave(crtc_state))
@@ -2975,12 +2980,10 @@ static void intel_enable_ddi(struct intel_atomic_state *state,
 	else
 		intel_enable_ddi_dp(state, encoder, crtc_state, conn_state);
 
-	/* Enable hdcp if it's desired */
-	if (conn_state->content_protection ==
-	    DRM_MODE_CONTENT_PROTECTION_DESIRED)
-		intel_hdcp_enable(to_intel_connector(conn_state->connector),
-				  crtc_state,
-				  (u8)conn_state->hdcp_content_type);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector->hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
 }
 
 static void intel_disable_ddi_dp(struct intel_atomic_state *state,
@@ -3026,7 +3029,14 @@ static void intel_disable_ddi(struct intel_atomic_state *state,
 			      const struct intel_crtc_state *old_crtc_state,
 			      const struct drm_connector_state *old_conn_state)
 {
-	intel_hdcp_disable(to_intel_connector(old_conn_state->connector));
+	struct intel_connector *connector =
+		to_intel_connector(old_conn_state->connector);
+	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector->hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
 
 	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
 		intel_disable_ddi_hdmi(state, encoder, old_crtc_state,
@@ -3054,13 +3064,19 @@ void intel_ddi_update_pipe(struct intel_atomic_state *state,
 			   const struct intel_crtc_state *crtc_state,
 			   const struct drm_connector_state *conn_state)
 {
+	struct intel_connector *connector =
+		to_intel_connector(conn_state->connector);
+	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
 
 	if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) &&
 	    !intel_encoder_is_mst(encoder))
 		intel_ddi_update_pipe_dp(state, encoder, crtc_state,
 					 conn_state);
 
-	intel_hdcp_update_pipe(state, encoder, crtc_state, conn_state);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector->hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
 }
 
 static void
drivers/gpu/drm/i915/display/intel_display_debugfs.c +4 -2
index a14b86a07e545..b6a9c00a1b541 100644
@@ -7,6 +7,7 @@
 
 #include <drm/drm_debugfs.h>
 #include <drm/drm_fourcc.h>
+#include <drm/display/drm_hdcp_helper.h>
 
 #include "i915_debugfs.h"
 #include "i915_irq.h"
@@ -502,10 +503,11 @@ static void intel_hdcp_info(struct seq_file *m,
 		goto out;
 	}
 
-	ret = intel_hdcp_capable(intel_connector, &hdcp_cap);
+	ret = intel_connector->hdcp_helper_data->funcs->hdcp1_capable(
+		intel_connector->hdcp_helper_data, &hdcp_cap);
 	if (ret)
 		hdcp_cap = false;
-	ret = intel_hdcp2_capable(intel_connector, &hdcp2_cap);
+	ret = intel_hdcp2_capable(&intel_connector->base, &hdcp2_cap);
 	if (ret)
 		hdcp2_cap = false;
 
drivers/gpu/drm/i915/display/intel_display_types.h +1 -50

71 lines not on this page · raw message

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +141 -227

442 lines not on this page · raw message

drivers/gpu/drm/i915/display/intel_dp_mst.c +9 -7
index 2106b3de225a0..489dd91f933d6 100644
@@ -23,6 +23,7 @@
  *
  */
 
+#include <drm/display/drm_hdcp_helper.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_edid.h>
@@ -41,7 +42,6 @@
 #include "intel_dp_hdcp.h"
 #include "intel_dp_mst.h"
 #include "intel_dpio_phy.h"
-#include "intel_hdcp.h"
 #include "intel_hotplug.h"
 #include "skl_scaler.h"
 
@@ -544,7 +544,10 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state,
 	drm_dbg_kms(&i915->drm, "active links %d\n",
 		    intel_dp->active_mst_links);
 
-	intel_hdcp_disable(intel_mst->connector);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector->hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
 
 	drm_dp_remove_payload(&intel_dp->mst_mgr, new_mst_state,
 			      old_payload, new_payload);
@@ -752,11 +755,10 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
 	intel_audio_codec_enable(encoder, pipe_config, conn_state);
 
 	/* Enable hdcp if it's desired */
-	if (conn_state->content_protection ==
-	    DRM_MODE_CONTENT_PROTECTION_DESIRED)
-		intel_hdcp_enable(to_intel_connector(conn_state->connector),
-				  pipe_config,
-				  (u8)conn_state->hdcp_content_type);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector->hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
 }
 
 static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
drivers/gpu/drm/i915/display/intel_hdcp.c +172 -788

1,192 lines not on this page · raw message

drivers/gpu/drm/i915/display/intel_hdcp.h +29 -8
index f06f6e5a2b1ad..7614bbf453133 100644
@@ -10,8 +10,10 @@
 
 #define HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS	50
 
+struct drm_atomic_state;
 struct drm_connector;
 struct drm_connector_state;
+struct drm_hdcp_an;
 struct drm_i915_private;
 struct intel_atomic_state;
 struct intel_connector;
@@ -25,16 +27,35 @@ enum transcoder;
 int intel_hdcp_init(struct intel_connector *connector,
 		    struct intel_digital_port *dig_port,
 		    const struct intel_hdcp_shim *hdcp_shim);
-int intel_hdcp_enable(struct intel_connector *connector,
-		      const struct intel_crtc_state *pipe_config, u8 content_type);
-int intel_hdcp_disable(struct intel_connector *connector);
-void intel_hdcp_update_pipe(struct intel_atomic_state *state,
-			    struct intel_encoder *encoder,
-			    const struct intel_crtc_state *crtc_state,
-			    const struct drm_connector_state *conn_state);
+int intel_hdcp_setup(struct drm_connector *drm_connector,
+		     struct drm_atomic_state *state, void *driver_data);
+int intel_hdcp_load_keys(struct drm_connector *drm_connector,
+			 void *driver_data);
 bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
 int intel_hdcp_capable(struct intel_connector *connector, bool *capable);
-int intel_hdcp2_capable(struct intel_connector *connector, bool *capable);
+int intel_hdcp2_capable(struct drm_connector *drm_connector, bool *capable);
+int intel_hdcp2_enable(struct drm_connector *drm_connector, void *driver_data);
+int intel_hdcp2_disable(struct drm_connector *drm_connector, void *driver_data);
+int intel_hdcp2_check_link(struct drm_connector *drm_connector,
+			   void *driver_data);
+int intel_hdcp1_store_receiver_info(struct drm_connector *drm_connector,
+				    u32 *ksv, u32 status, u8 caps,
+				    bool repeater_present, void *driver_data);
+int intel_hdcp1_read_an(struct drm_connector *drm_connector,
+			struct drm_hdcp_an *an);
+int intel_hdcp1_enable_encryption(struct drm_connector *drm_connector,
+				  void *driver_data);
+int intel_hdcp1_wait_for_r0(struct drm_connector *drm_connector,
+			    void *driver_data);
+int intel_hdcp1_match_ri(struct drm_connector *drm_connector, u32 ri_prime,
+			 void *driver_data);
+int intel_hdcp1_post_encryption(struct drm_connector *drm_connector,
+				void *driver_data);
+int intel_hdcp1_store_ksv_fifo(struct drm_connector *drm_connector,
+			       u8 *ksv_fifo, u8 num_downstream, u8 *bstatus,
+			       u32 *v_prime, void *driver_data);
+int intel_hdcp1_check_link(struct drm_connector *drm_connector);
+int intel_hdcp1_disable(struct drm_connector *drm_connector, void *driver_data);
 void intel_hdcp_component_init(struct drm_i915_private *dev_priv);
 void intel_hdcp_component_fini(struct drm_i915_private *dev_priv);
 void intel_hdcp_cleanup(struct intel_connector *connector);
drivers/gpu/drm/i915/display/intel_hdmi.c +104 -172

346 lines not on this page · raw message

Signature · 1 line
2.40.0.348.gf938b09366-goog
#13 lore raw

RE: [PATCH v8 07/10] drm/i915/hdcp: Use HDCP helpers for i915

Quoted 730 lines

-----Original Message-----
From: Mark Yacoub <[email protected]>
Sent: Saturday, April 1, 2023 3:42 AM
To: Jani Nikula <[email protected]>; Joonas Lahtinen <[email protected]>; Vivi, Rodrigo <[email protected]>; Tvrtko Ursulin <[email protected]>; David Airlie <[email protected]>; Daniel Vetter <[email protected]>
Cc: [email protected]; Kandpal, Suraj <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; Nikula, Jani <[email protected]>; Mark Yacoub <[email protected]>; linux- [email protected]
Subject: [PATCH v8 07/10] drm/i915/hdcp: Use HDCP helpers for i915

From: Sean Paul <[email protected]>

Now that all of the HDCP 1.x logic has been migrated to the central HDCP helpers, use it in the i915 driver.

The majority of the driver code for HDCP 1.x will live in intel_hdcp.c, however there are a few helper hooks which are connector-specific and need to be partially or fully implemented in the intel_dp_hdcp.c or intel_hdmi.c.

We'll leave most of the HDCP 2.x code alone since we don't have another implementation of HDCP 2.x to use as reference for what should and should not live in the drm helpers. The helper will call the overly general enable/disable/is_capable HDCP 2.x callbacks and leave the interesting stuff for the driver. Once we have another HDCP 2.x implementation, we should do a similar migration.

---
Changes in v2:
-Fix mst helper function pointer reported by 0-day
Changes in v3:
-Add forward declaration for drm_atomic_state in intel_hdcp.h identified
by 0-day
Changes in v4:
-None
Changes in v5:
-None
Changes in v6:
-Rebased.
Changes in v7:
-Added to drm_hdcp_helper_funcs new functions that are unique between DP
and HDMI
-Adjusted the function signatures to take "driver data" Changes in v8:

(quoted diff) +0 -1
-None
 
 drivers/gpu/drm/i915/display/intel_ddi.c      |  32 +-
 .../drm/i915/display/intel_display_debugfs.c  |   6 +-
 .../drm/i915/display/intel_display_types.h    |  51 +-
 drivers/gpu/drm/i915/display/intel_dp_hdcp.c  | 368 +++----
 drivers/gpu/drm/i915/display/intel_dp_mst.c   |  16 +-
 drivers/gpu/drm/i915/display/intel_hdcp.c     | 960 ++++--------------
 drivers/gpu/drm/i915/display/intel_hdcp.h     |  37 +-
 drivers/gpu/drm/i915/display/intel_hdmi.c     | 276 ++---
 8 files changed, 484 insertions(+), 1262 deletions(-)
drivers/gpu/drm/i915/display/intel_ddi.c +5 -0
index 254559abedfba..8a2f20c929e9c 100644
@@ -28,6 +28,7 @@
 #include <linux/string_helpers.h>
 
 #include <drm/display/drm_scdc_helper.h>
+#include <drm/display/drm_hdcp_helper.h>
 #include <drm/drm_privacy_screen_consumer.h>
 
 #include "i915_drv.h"
@@ -2956,6 +2957,10 @@ static void intel_enable_ddi(struct
 intel_atomic_state *state,
 			     const struct intel_crtc_state *crtc_state,
 			     const struct drm_connector_state *conn_state)
 {
+	struct intel_connector *connector =
+		to_intel_connector(conn_state->connector);
+	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
 	drm_WARN_ON(state->base.dev, crtc_state->has_pch_encoder);
 
drivers/gpu/drm/i915/display/intel_ddi.c +4 -6
 	if (!intel_crtc_is_bigjoiner_slave(crtc_state))
@@ -2975,12 +2980,10 @@ static void intel_enable_ddi(struct
 intel_atomic_state *state,
 	else
 		intel_enable_ddi_dp(state, encoder, crtc_state, conn_state);
 
-	/* Enable hdcp if it's desired */
-	if (conn_state->content_protection ==
-	    DRM_MODE_CONTENT_PROTECTION_DESIRED)
-		intel_hdcp_enable(to_intel_connector(conn_state-
 >connector),
-				  crtc_state,
-				  (u8)conn_state->hdcp_content_type);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector-
 >hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);

}

static void intel_disable_ddi_dp(struct intel_atomic_state *state,

drivers/gpu/drm/i915/display/intel_ddi.c +8 -1
@@ -3026,7 +3029,14 @@ static void intel_disable_ddi(struct
 intel_atomic_state *state,
 			      const struct intel_crtc_state *old_crtc_state,
 			      const struct drm_connector_state
 *old_conn_state)
 {
-	intel_hdcp_disable(to_intel_connector(old_conn_state->connector));
+	struct intel_connector *connector =
+		to_intel_connector(old_conn_state->connector);
+	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector-
 >hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
drivers/gpu/drm/i915/display/intel_ddi.c +7 -1
 	if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
 		intel_disable_ddi_hdmi(state, encoder, old_crtc_state,
@@ -3054,13 +3064,19 @@ void intel_ddi_update_pipe(struct
 intel_atomic_state *state,
 			   const struct intel_crtc_state *crtc_state,
 			   const struct drm_connector_state *conn_state)
 {
+	struct intel_connector *connector =
+		to_intel_connector(conn_state->connector);
+	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
 
 	if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) &&
 	    !intel_encoder_is_mst(encoder))
 		intel_ddi_update_pipe_dp(state, encoder, crtc_state,
 					 conn_state);
 
-	intel_hdcp_update_pipe(state, encoder, crtc_state, conn_state);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector-
 >hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
 }

static void

drivers/gpu/drm/i915/display/intel_display_debugfs.c +4 -2
index a14b86a07e545..b6a9c00a1b541 100644
@@ -7,6 +7,7 @@
 
 #include <drm/drm_debugfs.h>
 #include <drm/drm_fourcc.h>
+#include <drm/display/drm_hdcp_helper.h>
 
 #include "i915_debugfs.h"
 #include "i915_irq.h"
@@ -502,10 +503,11 @@ static void intel_hdcp_info(struct seq_file *m,
 		goto out;
 	}
 
-	ret = intel_hdcp_capable(intel_connector, &hdcp_cap);
+	ret = intel_connector->hdcp_helper_data->funcs->hdcp1_capable(
+		intel_connector->hdcp_helper_data, &hdcp_cap);
 	if (ret)
 		hdcp_cap = false;
-	ret = intel_hdcp2_capable(intel_connector, &hdcp2_cap);
+	ret = intel_hdcp2_capable(&intel_connector->base, &hdcp2_cap);
 	if (ret)
 		hdcp2_cap = false;
 
drivers/gpu/drm/i915/display/intel_display_types.h +0 -8
index 582234f0c49ac..1867a1becdf43 100644
@@ -432,64 +432,14 @@ enum check_link_response {
  *		to send it over DP AUX vs DDC
  */
 struct intel_hdcp_shim {
-	/* Outputs the transmitter's An and Aksv values to the receiver. */
-	int (*write_an_aksv)(struct intel_digital_port *dig_port, u8 *an);
-
-	/* Reads the receiver's key selection vector */
-	int (*read_bksv)(struct intel_digital_port *dig_port, u8 *bksv);
-
-	/*
-	 * Reads BINFO from DP receivers and BSTATUS from HDMI receivers.

The

drivers/gpu/drm/i915/display/intel_display_types.h +1 -42
-	 * definitions are the same in the respective specs, but the names are
-	 * different. Call it BSTATUS since that's the name the HDMI spec
-	 * uses and it was there first.
-	 */
-	int (*read_bstatus)(struct intel_digital_port *dig_port,
-			    u8 *bstatus);
-
-	/* Determines whether a repeater is present downstream */
-	int (*repeater_present)(struct intel_digital_port *dig_port,
-				bool *repeater_present);
-
-	/* Reads the receiver's Ri' value */
-	int (*read_ri_prime)(struct intel_digital_port *dig_port, u8 *ri);
-
-	/* Determines if the receiver's KSV FIFO is ready for consumption */
-	int (*read_ksv_ready)(struct intel_digital_port *dig_port,
-			      bool *ksv_ready);
-
-	/* Reads the ksv fifo for num_downstream devices */
-	int (*read_ksv_fifo)(struct intel_digital_port *dig_port,
-			     int num_downstream, u8 *ksv_fifo);
-
-	/* Reads a 32-bit part of V' from the receiver */
-	int (*read_v_prime_part)(struct intel_digital_port *dig_port,
-				 int i, u32 *part);
-
 	/* Enables HDCP signalling on the port */
 	int (*toggle_signalling)(struct intel_digital_port *dig_port,
 				 enum transcoder cpu_transcoder,
 				 bool enable);
 
-	/* Enable/Disable stream encryption on DP MST Transport Link */
-	int (*stream_encryption)(struct intel_connector *connector,
-				 bool enable);
-
-	/* Ensures the link is still protected */
-	bool (*check_link)(struct intel_digital_port *dig_port,
-			   struct intel_connector *connector);
-
-	/* Detects panel's hdcp capability. This is optional for HDMI. */
-	int (*hdcp_capable)(struct intel_digital_port *dig_port,
-			    bool *hdcp_capable);
-
 	/* HDCP adaptation(DP/HDMI) required on the port */
 	enum hdcp_wired_protocol protocol;
 
-	/* Detects whether sink is HDCP2.2 capable */
-	int (*hdcp_2_2_capable)(struct intel_digital_port *dig_port,
-				bool *capable);
-
 	/* Write HDCP2.2 messages */
 	int (*write_2_2_msg)(struct intel_digital_port *dig_port,
 			     void *buf, size_t size);
@@ -611,6 +561,7 @@ struct intel_connector {
 	struct work_struct modeset_retry_work;
 
 	struct intel_hdcp hdcp;
+	struct drm_hdcp_helper_data *hdcp_helper_data;
 };
 
 struct intel_digital_connector_state {
drivers/gpu/drm/i915/display/intel_dp_hdcp.c +12 -83
index e0c1771614072..694798a169f03 100644
@@ -48,17 +48,24 @@ static void intel_dp_hdcp_wait_for_cp_irq(struct
 intel_hdcp *hdcp, int timeout)
 		DRM_DEBUG_KMS("Timedout at waiting for CP_IRQ\n");
 }
 
-static
-int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *dig_port,
-				u8 *an)
+static int intel_dp_hdcp_send_an_aksv(struct drm_connector
 *drm_connector,
+				      void *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
 	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+	struct drm_hdcp_an an;
 	u8 aksv[DRM_HDCP_KSV_LEN] = {};
 	ssize_t dpcd_ret;
+	int ret;
+
+	ret = intel_hdcp1_read_an(drm_connector, &an);
+	if (ret)
+		return ret;
 
-	/* Output An first, that's easy */
 	dpcd_ret = drm_dp_dpcd_write(&dig_port->dp.aux,
 DP_AUX_HDCP_AN,
-				     an, DRM_HDCP_AN_LEN);
+				     an.bytes, DRM_HDCP_AN_LEN);
 	if (dpcd_ret != DRM_HDCP_AN_LEN) {
 		drm_dbg_kms(&i915->drm,
 			    "Failed to write An over DP/AUX (%zd)\n",
@@ -84,158 +91,6 @@ int intel_dp_hdcp_write_an_aksv(struct
 intel_digital_port *dig_port,
 	return 0;
 }
 
-static int intel_dp_hdcp_read_bksv(struct intel_digital_port *dig_port,
-				   u8 *bksv)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-
-	ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BKSV,
 bksv,
-			       DRM_HDCP_KSV_LEN);
-	if (ret != DRM_HDCP_KSV_LEN) {
-		drm_dbg_kms(&i915->drm,
-			    "Read Bksv from DP/AUX failed (%zd)\n", ret);
-		return ret >= 0 ? -EIO : ret;
-	}
-	return 0;
-}
-
-static int intel_dp_hdcp_read_bstatus(struct intel_digital_port *dig_port,
-				      u8 *bstatus)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-
-	/*
-	 * For some reason the HDMI and DP HDCP specs call this register
-	 * definition by different names. In the HDMI spec, it's called
 BSTATUS,
-	 * but in DP it's called BINFO.
-	 */
-	ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BINFO,
-			       bstatus, DRM_HDCP_BSTATUS_LEN);
-	if (ret != DRM_HDCP_BSTATUS_LEN) {
-		drm_dbg_kms(&i915->drm,
-			    "Read bstatus from DP/AUX failed (%zd)\n", ret);
-		return ret >= 0 ? -EIO : ret;
-	}
-	return 0;
-}
-
-static
-int intel_dp_hdcp_read_bcaps(struct intel_digital_port *dig_port,
-			     u8 *bcaps)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-
-	ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BCAPS,
-			       bcaps, 1);
-	if (ret != 1) {
-		drm_dbg_kms(&i915->drm,
-			    "Read bcaps from DP/AUX failed (%zd)\n", ret);
-		return ret >= 0 ? -EIO : ret;
-	}
-
-	return 0;
-}
-
-static
-int intel_dp_hdcp_repeater_present(struct intel_digital_port *dig_port,
-				   bool *repeater_present)
-{
-	ssize_t ret;
-	u8 bcaps;
-
-	ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps);
-	if (ret)
-		return ret;
-
-	*repeater_present = bcaps & DP_BCAPS_REPEATER_PRESENT;
-	return 0;
-}
-
-static
-int intel_dp_hdcp_read_ri_prime(struct intel_digital_port *dig_port,
-				u8 *ri_prime)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-
-	ret = drm_dp_dpcd_read(&dig_port->dp.aux,

DP_AUX_HDCP_RI_PRIME,

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +0 -3
-			       ri_prime, DRM_HDCP_RI_LEN);
-	if (ret != DRM_HDCP_RI_LEN) {
-		drm_dbg_kms(&i915->drm, "Read Ri' from DP/AUX failed

(%zd)\n",

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +0 -15
-			    ret);
-		return ret >= 0 ? -EIO : ret;
-	}
-	return 0;
-}
-
-static
-int intel_dp_hdcp_read_ksv_ready(struct intel_digital_port *dig_port,
-				 bool *ksv_ready)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-	u8 bstatus;
-
-	ret = drm_dp_dpcd_read(&dig_port->dp.aux,

DP_AUX_HDCP_BSTATUS,

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +0 -21
-			       &bstatus, 1);
-	if (ret != 1) {
-		drm_dbg_kms(&i915->drm,
-			    "Read bstatus from DP/AUX failed (%zd)\n", ret);
-		return ret >= 0 ? -EIO : ret;
-	}
-	*ksv_ready = bstatus & DP_BSTATUS_READY;
-	return 0;
-}
-
-static
-int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *dig_port,
-				int num_downstream, u8 *ksv_fifo)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-	int i;
-
-	/* KSV list is read via 15 byte window (3 entries @ 5 bytes each) */
-	for (i = 0; i < num_downstream; i += 3) {
-		size_t len = min(num_downstream - i, 3) *

DRM_HDCP_KSV_LEN;

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +0 -7
-		ret = drm_dp_dpcd_read(&dig_port->dp.aux,
-				       DP_AUX_HDCP_KSV_FIFO,
-				       ksv_fifo + i * DRM_HDCP_KSV_LEN,
-				       len);
-		if (ret != len) {
-			drm_dbg_kms(&i915->drm,
-				    "Read ksv[%d] from DP/AUX failed

(%zd)\n",

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +0 -62
-				    i, ret);
-			return ret >= 0 ? -EIO : ret;
-		}
-	}
-	return 0;
-}
-
-static
-int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *dig_port,
-				    int i, u32 *part)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-
-	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
-		return -EINVAL;
-
-	ret = drm_dp_dpcd_read(&dig_port->dp.aux,
-			       DP_AUX_HDCP_V_PRIME(i), part,
-			       DRM_HDCP_V_PRIME_PART_LEN);
-	if (ret != DRM_HDCP_V_PRIME_PART_LEN) {
-		drm_dbg_kms(&i915->drm,
-			    "Read v'[%d] from DP/AUX failed (%zd)\n", i, ret);
-		return ret >= 0 ? -EIO : ret;
-	}
-	return 0;
-}
-
 static
 int intel_dp_hdcp_toggle_signalling(struct intel_digital_port *dig_port,
 				    enum transcoder cpu_transcoder,
@@ -245,40 +100,6 @@ int intel_dp_hdcp_toggle_signalling(struct
 intel_digital_port *dig_port,
 	return 0;
 }
 
-static
-bool intel_dp_hdcp_check_link(struct intel_digital_port *dig_port,
-			      struct intel_connector *connector)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	ssize_t ret;
-	u8 bstatus;
-
-	ret = drm_dp_dpcd_read(&dig_port->dp.aux,
 DP_AUX_HDCP_BSTATUS,
-			       &bstatus, 1);
-	if (ret != 1) {
-		drm_dbg_kms(&i915->drm,
-			    "Read bstatus from DP/AUX failed (%zd)\n", ret);
-		return false;
-	}
-
-	return !(bstatus & (DP_BSTATUS_LINK_FAILURE |
 DP_BSTATUS_REAUTH_REQ));
-}
-
-static
-int intel_dp_hdcp_capable(struct intel_digital_port *dig_port,
-			  bool *hdcp_capable)
-{
-	ssize_t ret;
-	u8 bcaps;
-
-	ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps);
-	if (ret)
-		return ret;
-
-	*hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE;
-	return 0;
-}
-

struct hdcp2_dp_errata_stream_type {

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +9 -3
 	u8	msg_id;
 	u8	stream_type;
@@ -621,13 +442,19 @@ int intel_dp_hdcp2_check_link(struct
 intel_digital_port *dig_port,
 	return ret;
 }
 
-static
-int intel_dp_hdcp2_capable(struct intel_digital_port *dig_port,
-			   bool *capable)
+static int intel_dp_hdcp2_capable(struct drm_connector *drm_connector,
+				  bool *capable, void *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
 	u8 rx_caps[3];
 	int ret;
 
+	ret = intel_hdcp2_capable(drm_connector, capable);
+	if (ret || !capable)
+		return ret;
+
 	*capable = false;
drivers/gpu/drm/i915/display/intel_dp_hdcp.c +15 -11
 	ret = drm_dp_dpcd_read(&dig_port->dp.aux,
 			       DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
@@ -643,22 +470,11 @@ int intel_dp_hdcp2_capable(struct
 intel_digital_port *dig_port,
 }
 
 static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
-	.write_an_aksv = intel_dp_hdcp_write_an_aksv,
-	.read_bksv = intel_dp_hdcp_read_bksv,
-	.read_bstatus = intel_dp_hdcp_read_bstatus,
-	.repeater_present = intel_dp_hdcp_repeater_present,
-	.read_ri_prime = intel_dp_hdcp_read_ri_prime,
-	.read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
-	.read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
-	.read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
 	.toggle_signalling = intel_dp_hdcp_toggle_signalling,
-	.check_link = intel_dp_hdcp_check_link,
-	.hdcp_capable = intel_dp_hdcp_capable,
 	.write_2_2_msg = intel_dp_hdcp2_write_msg,
 	.read_2_2_msg = intel_dp_hdcp2_read_msg,
 	.config_stream_type = intel_dp_hdcp2_config_stream_type,
 	.check_2_2_link = intel_dp_hdcp2_check_link,
-	.hdcp_2_2_capable = intel_dp_hdcp2_capable,
 	.protocol = HDCP_PROTOCOL_DP,
 };
@@ -714,6 +530,48 @@ intel_dp_mst_hdcp_stream_encryption(struct
 intel_connector *connector,
 	return 0;
 }
 
+static int
+intel_dp_mst_hdcp1_post_encryption(struct drm_connector
 *drm_connector,
+				   void *driver_data)
+{
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	int ret;
+
+	ret = intel_hdcp1_post_encryption(drm_connector, driver_data);
+	if (ret)
+		return ret;
+
+	return intel_dp_mst_hdcp_stream_encryption(connector, true);
+}
+
+static int intel_dp_mst_hdcp1_disable(struct drm_connector

*drm_connector,

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +3 -0
+				      void *driver_data)
+{
+	struct intel_connector *connector =

to_intel_connector(drm_connector);

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +90 -22
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
+	struct drm_i915_private *i915 = to_i915(connector->base.dev);
+	int ret;
+
+	ret = intel_dp_mst_hdcp_stream_encryption(connector, true);
+	if (ret) {
+		drm_err(&i915->drm,
+			"[%s:%d] Failed to disable HDCP 1.4 stream enc\n",
+			connector->base.name, connector->base.base.id);
+		return ret;
+	}
+
+	/*
+	 * If there are other connectors on this port using HDCP,
+	 * don't disable it until it disabled HDCP encryption for
+	 * all connectors in MST topology.
+	*/
+	if (dig_port->num_hdcp_streams > 0)
+		return 0;
+
+	return intel_hdcp1_disable(drm_connector, driver_data);
+}
+
 static int
 intel_dp_mst_hdcp2_stream_encryption(struct intel_connector *connector,
 				     bool enable)
@@ -772,45 +630,101 @@ int intel_dp_mst_hdcp2_check_link(struct
 intel_digital_port *dig_port,
 }
 
 static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = {
-	.write_an_aksv = intel_dp_hdcp_write_an_aksv,
-	.read_bksv = intel_dp_hdcp_read_bksv,
-	.read_bstatus = intel_dp_hdcp_read_bstatus,
-	.repeater_present = intel_dp_hdcp_repeater_present,
-	.read_ri_prime = intel_dp_hdcp_read_ri_prime,
-	.read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
-	.read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
-	.read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
 	.toggle_signalling = intel_dp_hdcp_toggle_signalling,
-	.stream_encryption = intel_dp_mst_hdcp_stream_encryption,
-	.check_link = intel_dp_hdcp_check_link,
-	.hdcp_capable = intel_dp_hdcp_capable,
 	.write_2_2_msg = intel_dp_hdcp2_write_msg,
 	.read_2_2_msg = intel_dp_hdcp2_read_msg,
 	.config_stream_type = intel_dp_hdcp2_config_stream_type,
 	.stream_2_2_encryption = intel_dp_mst_hdcp2_stream_encryption,
 	.check_2_2_link = intel_dp_mst_hdcp2_check_link,
-	.hdcp_2_2_capable = intel_dp_hdcp2_capable,
 	.protocol = HDCP_PROTOCOL_DP,
 };
 
+static const struct drm_hdcp_helper_funcs intel_dp_hdcp_helper_funcs = {
+	.setup = intel_hdcp_setup,
+	.load_keys = intel_hdcp_load_keys,
+	.hdcp2_capable = intel_dp_hdcp2_capable,
+	.hdcp2_enable = intel_hdcp2_enable,
+	.hdcp2_check_link = intel_hdcp2_check_link,
+	.hdcp2_disable = intel_hdcp2_disable,
+	.hdcp1_send_an_aksv = intel_dp_hdcp_send_an_aksv,
+	.hdcp1_store_receiver_info = intel_hdcp1_store_receiver_info,
+	.hdcp1_enable_encryption = intel_hdcp1_enable_encryption,
+	.hdcp1_wait_for_r0 = intel_hdcp1_wait_for_r0,
+	.hdcp1_match_ri = intel_hdcp1_match_ri,
+	.hdcp1_post_encryption = intel_hdcp1_post_encryption,
+	.hdcp1_store_ksv_fifo = intel_hdcp1_store_ksv_fifo,
+	.hdcp1_disable = intel_hdcp1_disable,
+	/* Common DRM functions that are different between DP vs HDMI*/
+	.remote_read = drm_hdcp_remote_dpcd_read,
+	.remote_write = drm_hdcp_remote_dpcd_write,
+	.hdcp1_capable = drm_hdcp_helper_hdcp1_capable_dp,
+	.hdcp1_ksv_fifo_ready =
 drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp,
+	.wait_r0 = drm_hdcp_helper_wait_r0_dp,
+	.hdcp1_check_link_registers =
 drm_hdcp_hdcp1_check_link_registers_dp,
+	.hdcp1_read_ksv_fifo = drm_hdcp_helper_hdcp1_read_ksv_fifo_dp
+};
+
+static const struct drm_hdcp_helper_funcs
 intel_dp_mst_hdcp_helper_funcs = {
+	.setup = intel_hdcp_setup,
+	.load_keys = intel_hdcp_load_keys,
+	.hdcp2_capable = intel_dp_hdcp2_capable,
+	.hdcp2_enable = intel_hdcp2_enable,
+	.hdcp2_check_link = intel_hdcp2_check_link,
+	.hdcp2_disable = intel_hdcp2_disable,
+	.hdcp1_send_an_aksv = intel_dp_hdcp_send_an_aksv,
+	.hdcp1_store_receiver_info = intel_hdcp1_store_receiver_info,
+	.hdcp1_enable_encryption = intel_hdcp1_enable_encryption,
+	.hdcp1_wait_for_r0 = intel_hdcp1_wait_for_r0,
+	.hdcp1_match_ri = intel_hdcp1_match_ri,
+	.hdcp1_post_encryption = intel_dp_mst_hdcp1_post_encryption,
+	.hdcp1_store_ksv_fifo = intel_hdcp1_store_ksv_fifo,
+	.hdcp1_disable = intel_dp_mst_hdcp1_disable,
+	/* Common DRM functions that are different between DP vs HDMI*/
+	.remote_read = drm_hdcp_remote_dpcd_read,
+	.remote_write = drm_hdcp_remote_dpcd_write,
+	.hdcp1_capable = drm_hdcp_helper_hdcp1_capable_dp,
+	.hdcp1_ksv_fifo_ready =
 drm_hdcp_helper_hdcp1_ksv_fifo_ready_dp,
+	.wait_r0 = drm_hdcp_helper_wait_r0_dp,
+	.hdcp1_check_link_registers =
 drm_hdcp_hdcp1_check_link_registers_dp,
+	.hdcp1_read_ksv_fifo = drm_hdcp_helper_hdcp1_read_ksv_fifo_dp
+};
+
 int intel_dp_hdcp_init(struct intel_digital_port *dig_port,
-		       struct intel_connector *intel_connector)
+		       struct intel_connector *connector)
 {
-	struct drm_device *dev = intel_connector->base.dev;
-	struct drm_i915_private *dev_priv = to_i915(dev);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	struct intel_encoder *intel_encoder = &dig_port->base;
 	enum port port = intel_encoder->port;
 	struct intel_dp *intel_dp = &dig_port->dp;
+	struct drm_hdcp_helper_data *data;
+	const struct drm_hdcp_helper_funcs *helper_funcs;
+	const struct intel_hdcp_shim *intel_shim;
+	int ret;
 
-	if (!is_hdcp_supported(dev_priv, port))
+	if (!is_hdcp_supported(dev_priv, port) || intel_dp_is_edp(intel_dp))
 		return 0;
 
-	if (intel_connector->mst_port)
-		return intel_hdcp_init(intel_connector, dig_port,
-				       &intel_dp_mst_hdcp_shim);
-	else if (!intel_dp_is_edp(intel_dp))
-		return intel_hdcp_init(intel_connector, dig_port,
-				       &intel_dp_hdcp_shim);
+	if (connector->mst_port) {
+		helper_funcs = &intel_dp_mst_hdcp_helper_funcs;
+		intel_shim = &intel_dp_mst_hdcp_shim;
+	} else {
+		helper_funcs = &intel_dp_hdcp_helper_funcs;
+		intel_shim = &intel_dp_hdcp_shim;
+	}
+
+	data = drm_hdcp_helper_initialize_dp(&connector->base, &dig_port-
Quoted 1 line

dp.aux,

Quoted 2 lines

+

	 helper_funcs, true);

Can you fix the alignment here seems off

Quoted 556 lines
drivers/gpu/drm/i915/display/intel_dp_hdcp.c +2 -0
+	if (IS_ERR(data)) {
+		drm_dbg_kms(&dev_priv->drm, "HDCP init failed,

skipping.\n");

drivers/gpu/drm/i915/display/intel_dp_hdcp.c +9 -0
+		return PTR_ERR(data);
+	}
+
+	ret = intel_hdcp_init(connector, dig_port, intel_shim);
+	if (ret) {
+		drm_hdcp_helper_destroy(data);
+		return ret;
+	}
 
+	connector->hdcp_helper_data = data;
 	return 0;
 }
drivers/gpu/drm/i915/display/intel_dp_mst.c +5 -2
index 2106b3de225a0..489dd91f933d6 100644
@@ -23,6 +23,7 @@
  *
  */
 
+#include <drm/display/drm_hdcp_helper.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_edid.h>
@@ -41,7 +42,6 @@
 #include "intel_dp_hdcp.h"
 #include "intel_dp_mst.h"
 #include "intel_dpio_phy.h"
-#include "intel_hdcp.h"
 #include "intel_hotplug.h"
 #include "skl_scaler.h"
 
@@ -544,7 +544,10 @@ static void intel_mst_disable_dp(struct
 intel_atomic_state *state,
 	drm_dbg_kms(&i915->drm, "active links %d\n",
 		    intel_dp->active_mst_links);
 
-	intel_hdcp_disable(intel_mst->connector);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector-
 >hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);
 
drivers/gpu/drm/i915/display/intel_dp_mst.c +4 -5
 	drm_dp_remove_payload(&intel_dp->mst_mgr, new_mst_state,
 			      old_payload, new_payload);
@@ -752,11 +755,10 @@ static void intel_mst_enable_dp(struct
 intel_atomic_state *state,
 	intel_audio_codec_enable(encoder, pipe_config, conn_state);
 
 	/* Enable hdcp if it's desired */
-	if (conn_state->content_protection ==
-	    DRM_MODE_CONTENT_PROTECTION_DESIRED)
-		intel_hdcp_enable(to_intel_connector(conn_state-
 >connector),
-				  pipe_config,
-				  (u8)conn_state->hdcp_content_type);
+	if (connector->hdcp_helper_data)
+		drm_hdcp_helper_atomic_commit(connector-
 >hdcp_helper_data,
+					      &state->base,
+					      &dig_port->hdcp_mutex);

}

static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -59
index 61a862ae1f286..96e6c4e587d8c 100644
@@ -140,67 +140,10 @@ static int intel_hdcp_prepare_streams(struct
 intel_connector *connector)
 	return 0;
 }
 
-static
-bool intel_hdcp_is_ksv_valid(u8 *ksv)
-{
-	int i, ones = 0;
-	/* KSV has 20 1's and 20 0's */
-	for (i = 0; i < DRM_HDCP_KSV_LEN; i++)
-		ones += hweight8(ksv[i]);
-	if (ones != 20)
-		return false;
-
-	return true;
-}
-
-static
-int intel_hdcp_read_valid_bksv(struct intel_digital_port *dig_port,
-			       const struct intel_hdcp_shim *shim, u8 *bksv)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	int ret, i, tries = 2;
-
-	/* HDCP spec states that we must retry the bksv if it is invalid */
-	for (i = 0; i < tries; i++) {
-		ret = shim->read_bksv(dig_port, bksv);
-		if (ret)
-			return ret;
-		if (intel_hdcp_is_ksv_valid(bksv))
-			break;
-	}
-	if (i == tries) {
-		drm_dbg_kms(&i915->drm, "Bksv is invalid\n");
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
-/* Is HDCP1.4 capable on Platform and Sink */
-int intel_hdcp_capable(struct intel_connector *connector, bool *capable)
-{
-	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
-	const struct intel_hdcp_shim *shim = connector->hdcp.shim;
-	u8 bksv[5];
-
-	*capable = false;
-
-	if (!shim)
-		return 0;
-
-	if (shim->hdcp_capable)
-		return shim->hdcp_capable(dig_port, capable);
-
-	if (!intel_hdcp_read_valid_bksv(dig_port, shim, bksv))
-		*capable = true;
-
-	return 0;
-}
-
 /* Is HDCP2.2 capable on Platform and Sink */
-int intel_hdcp2_capable(struct intel_connector *connector, bool *capable)
+int intel_hdcp2_capable(struct drm_connector *drm_connector, bool
 *capable)
 {
-	struct intel_digital_port *dig_port =

intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
+	struct intel_connector *connector =

to_intel_connector(drm_connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +7 -7
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	struct intel_hdcp *hdcp = &connector->hdcp;
@@ -218,16 +161,26 @@ int intel_hdcp2_capable(struct intel_connector
 *connector, bool *capable)
 	}
 	mutex_unlock(&dev_priv->display.hdcp.comp_mutex);
 
-	/* Sink's capability for HDCP2.2 */
-	return hdcp->shim->hdcp_2_2_capable(dig_port, capable);
+	return 0;
 }
 
-static bool intel_hdcp_in_use(struct drm_i915_private *dev_priv,
-			      enum transcoder cpu_transcoder, enum port
 port)
+int intel_hdcp1_check_link(struct drm_connector *drm_connector)
 {
-	return intel_de_read(dev_priv,
-	                     HDCP_STATUS(dev_priv, cpu_transcoder, port)) &
-	       HDCP_STATUS_ENC;
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+	enum transcoder cpu_transcoder = connector-
Quoted 1 line

hdcp.cpu_transcoder;

drivers/gpu/drm/i915/display/intel_hdcp.c +10 -21
+	enum port port = dig_port->base.port;
+	u32 val;
+
+	val = intel_de_read(dev_priv,
+			    HDCP_STATUS(dev_priv, cpu_transcoder, port));
+
+	if (val & HDCP_STATUS_ENC)
+		return 0;
+
+	return -EINVAL;
 }
 
 static bool intel_hdcp2_in_use(struct drm_i915_private *dev_priv,
@@ -238,27 +191,6 @@ static bool intel_hdcp2_in_use(struct
 drm_i915_private *dev_priv,
 	       LINK_ENCRYPTION_STATUS;
 }
 
-static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *dig_port,
-				    const struct intel_hdcp_shim *shim)
-{
-	int ret, read_ret;
-	bool ksv_ready;
-
-	/* Poll for ksv list ready (spec says max time allowed is 5s) */
-	ret = __wait_for(read_ret = shim->read_ksv_ready(dig_port,
-							 &ksv_ready),
-			 read_ret || ksv_ready, 5 * 1000 * 1000, 1000,
-			 100 * 1000);
-	if (ret)
-		return ret;
-	if (read_ret)
-		return read_ret;
-	if (!ksv_ready)
-		return -ETIMEDOUT;
-
-	return 0;
-}
-
 static bool hdcp_key_loadable(struct drm_i915_private *dev_priv)
 {
drivers/gpu/drm/i915/display/intel_hdcp.c +5 -1
 	enum i915_power_well_id id;
@@ -294,11 +226,18 @@ static void intel_hdcp_clear_keys(struct
 drm_i915_private *dev_priv)
 		       HDCP_KEY_LOAD_DONE | HDCP_KEY_LOAD_STATUS |
 HDCP_FUSE_IN_PROGRESS | HDCP_FUSE_ERROR | HDCP_FUSE_DONE);
 }
 
-static int intel_hdcp_load_keys(struct drm_i915_private *dev_priv)
+int intel_hdcp_load_keys(struct drm_connector *drm_connector, void
 *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	int ret;
 	u32 val;
 
+	if (!hdcp_key_loadable(dev_priv)) {
+		drm_err(&dev_priv->drm, "HDCP key Load is not

possible\n");

drivers/gpu/drm/i915/display/intel_hdcp.c +3 -0
+		return -ENXIO;
+	}
+
 	val = intel_de_read(dev_priv, HDCP_KEY_STATUS);
 	if ((val & HDCP_KEY_LOAD_DONE) && (val &

HDCP_KEY_LOAD_STATUS))

drivers/gpu/drm/i915/display/intel_hdcp.c +5 -2
 		return 0;
@@ -308,8 +247,11 @@ static int intel_hdcp_load_keys(struct
 drm_i915_private *dev_priv)
 	 * out of reset. So if Key is not already loaded, its an error state.
 	 */
 	if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
-		if (!(intel_de_read(dev_priv, HDCP_KEY_STATUS) &
 HDCP_KEY_LOAD_DONE))
-			return -ENXIO;
+		if (!(intel_de_read(dev_priv, HDCP_KEY_STATUS) &
+		      HDCP_KEY_LOAD_DONE)) {
+			ret = -ENXIO;
+			goto err;
+		}
 
drivers/gpu/drm/i915/display/intel_hdcp.c +1 -1
 	/*
 	 * Initiate loading the HDCP key from fuses.
@@ -325,7 +267,7 @@ static int intel_hdcp_load_keys(struct
 drm_i915_private *dev_priv)
 			drm_err(&dev_priv->drm,
 				"Failed to initiate HDCP key load (%d)\n",
 				ret);
-			return ret;
+			goto err;
 		}
 	} else {

intel_de_write(dev_priv, HDCP_KEY_CONF,
HDCP_KEY_LOAD_TRIGGER);

drivers/gpu/drm/i915/display/intel_hdcp.c +17 -15
@@ -335,15 +277,21 @@ static int intel_hdcp_load_keys(struct
 drm_i915_private *dev_priv)
 	ret = __intel_wait_for_register(&dev_priv->uncore,
 HDCP_KEY_STATUS,
 					HDCP_KEY_LOAD_DONE,
 HDCP_KEY_LOAD_DONE,
 					10, 1, &val);
-	if (ret)
-		return ret;
-	else if (!(val & HDCP_KEY_LOAD_STATUS))
-		return -ENXIO;
+	if (ret) {
+		goto err;
+	} else if (!(val & HDCP_KEY_LOAD_STATUS)) {
+		ret = -ENXIO;
+		goto err;
+	}
 
 	/* Send Aksv over to PCH display for use in authentication */
 	intel_de_write(dev_priv, HDCP_KEY_CONF,
 HDCP_AKSV_SEND_TRIGGER);
 
 	return 0;
+
+err:
+	intel_hdcp_clear_keys(dev_priv);
+	return ret;
 }
 
 /* Returns updated SHA-1 index */
@@ -399,25 +347,21 @@ u32 intel_hdcp_get_repeater_ctl(struct
 drm_i915_private *dev_priv,
 	}
 }
 
-static
-int intel_hdcp_validate_v_prime(struct intel_connector *connector,
-				const struct intel_hdcp_shim *shim,
-				u8 *ksv_fifo, u8 num_downstream, u8
 *bstatus)
+int intel_hdcp1_store_ksv_fifo(struct drm_connector *drm_connector,
+			       u8 *ksv_fifo, u8 num_downstream, u8 *bstatus,
+			       u32 *v_prime, void *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
 	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	enum transcoder cpu_transcoder = connector-
 >hdcp.cpu_transcoder;
 	enum port port = dig_port->base.port;
-	u32 vprime, sha_text, sha_leftovers, rep_ctl;
+	u32 sha_text, sha_leftovers, rep_ctl;
 	int ret, i, j, sha_idx;
 
 	/* Process V' values from the receiver */
-	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) {
-		ret = shim->read_v_prime_part(dig_port, i, &vprime);
-		if (ret)
-			return ret;
-		intel_de_write(dev_priv, HDCP_SHA_V_PRIME(i), vprime);
-	}
+	for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++)
+		intel_de_write(dev_priv, HDCP_SHA_V_PRIME(i), v_prime[i]);
 
 	/*
 	 * We need to write the concatenation of all device KSVs, BINFO (DP)

||

drivers/gpu/drm/i915/display/intel_hdcp.c +4 -26
@@ -642,131 +586,38 @@ int intel_hdcp_validate_v_prime(struct
 intel_connector *connector,
 	return 0;
 }
 
-/* Implements Part 2 of the HDCP authorization procedure */
-static
-int intel_hdcp_auth_downstream(struct intel_connector *connector)
+int intel_hdcp1_store_receiver_info(struct drm_connector *drm_connector,
+				    u32 *ksv, u32 status, u8 caps,
+				    bool repeater_present, void *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
 	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
-	const struct intel_hdcp_shim *shim = connector->hdcp.shim;
-	u8 bstatus[2], num_downstream, *ksv_fifo;
-	int ret, i, tries = 3;
-
-	ret = intel_hdcp_poll_ksv_fifo(dig_port, shim);
-	if (ret) {
-		drm_dbg_kms(&dev_priv->drm,
-			    "KSV list failed to become ready (%d)\n", ret);
-		return ret;
-	}
-
-	ret = shim->read_bstatus(dig_port, bstatus);
-	if (ret)
-		return ret;
-
-	if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) ||
-	    DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) {
-		drm_dbg_kms(&dev_priv->drm, "Max Topology Limit
 Exceeded\n");
-		return -EPERM;
-	}
-
-	/*
-	 * When repeater reports 0 device count, HDCP1.4 spec allows

disabling

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -13
-	 * the HDCP encryption. That implies that repeater can't have its own
-	 * display. As there is no consumption of encrypted content in the
-	 * repeater with 0 downstream devices, we are failing the
-	 * authentication.
-	 */
-	num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]);
-	if (num_downstream == 0) {
-		drm_dbg_kms(&dev_priv->drm,
-			    "Repeater with zero downstream devices\n");
-		return -EINVAL;
-	}
-
-	ksv_fifo = kcalloc(DRM_HDCP_KSV_LEN, num_downstream,

GFP_KERNEL);

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -15
-	if (!ksv_fifo) {
-		drm_dbg_kms(&dev_priv->drm, "Out of mem: ksv_fifo\n");
-		return -ENOMEM;
-	}
-
-	ret = shim->read_ksv_fifo(dig_port, num_downstream, ksv_fifo);
-	if (ret)
-		goto err;
-
-	if (drm_hdcp_check_ksvs_revoked(&dev_priv->drm, ksv_fifo,
-					num_downstream) > 0) {
-		drm_err(&dev_priv->drm, "Revoked Ksv(s) in ksv_fifo\n");
-		ret = -EPERM;
-		goto err;
-	}
+	enum transcoder cpu_transcoder = connector-
Quoted 1 line

hdcp.cpu_transcoder;

drivers/gpu/drm/i915/display/intel_hdcp.c +2 -11
+	enum port port = dig_port->base.port;
 
-	/*
-	 * When V prime mismatches, DP Spec mandates re-read of
-	 * V prime atleast twice.
-	 */
-	for (i = 0; i < tries; i++) {
-		ret = intel_hdcp_validate_v_prime(connector, shim,
-						  ksv_fifo, num_downstream,
-						  bstatus);
-		if (!ret)
-			break;
-	}
+	intel_de_write(dev_priv, HDCP_BKSVLO(dev_priv, cpu_transcoder,

port),

drivers/gpu/drm/i915/display/intel_hdcp.c +2 -0
+		       ksv[0]);
+	intel_de_write(dev_priv, HDCP_BKSVHI(dev_priv, cpu_transcoder,

port),

drivers/gpu/drm/i915/display/intel_hdcp.c +5 -6
+		       ksv[1]);
 
-	if (i == tries) {
-		drm_dbg_kms(&dev_priv->drm,
-			    "V Prime validation failed.(%d)\n", ret);
-		goto err;
-	}
+	if (repeater_present)
+		intel_de_write(dev_priv, HDCP_REP_CTL,
+			       intel_hdcp_get_repeater_ctl(
+				       dev_priv, cpu_transcoder, port));
 
-	drm_dbg_kms(&dev_priv->drm, "HDCP is enabled (%d downstream

devices)\n",

drivers/gpu/drm/i915/display/intel_hdcp.c +4 -7
-		    num_downstream);
-	ret = 0;
-err:
-	kfree(ksv_fifo);
-	return ret;
+	return 0;
 }
 
-/* Implements Part 1 of the HDCP authorization procedure */
-static int intel_hdcp_auth(struct intel_connector *connector)
+int intel_hdcp1_read_an(struct drm_connector *drm_connector,
+			struct drm_hdcp_an *an)
 {
+	struct intel_connector *connector =

to_intel_connector(drm_connector);
struct intel_digital_port *dig_port =
intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -2
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
-	struct intel_hdcp *hdcp = &connector->hdcp;
-	const struct intel_hdcp_shim *shim = hdcp->shim;
 	enum transcoder cpu_transcoder = connector-
Quoted 1 line

hdcp.cpu_transcoder;

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -18
 	enum port port = dig_port->base.port;
-	unsigned long r0_prime_gen_start;
-	int ret, i, tries = 2;
-	union {
-		u32 reg[2];
-		u8 shim[DRM_HDCP_AN_LEN];
-	} an;
-	union {
-		u32 reg[2];
-		u8 shim[DRM_HDCP_KSV_LEN];
-	} bksv;
-	union {
-		u32 reg;
-		u8 shim[DRM_HDCP_RI_LEN];
-	} ri;
-	bool repeater_present, hdcp_capable;
-
-	/*
-	 * Detects whether the display is HDCP capable. Although we check

for

drivers/gpu/drm/i915/display/intel_hdcp.c +7 -46
-	 * valid Bksv below, the HDCP over DP spec requires that we check
-	 * whether the display supports HDCP before we write An. For HDMI
-	 * displays, this is not necessary.
-	 */
-	if (shim->hdcp_capable) {
-		ret = shim->hdcp_capable(dig_port, &hdcp_capable);
-		if (ret)
-			return ret;
-		if (!hdcp_capable) {
-			drm_dbg_kms(&dev_priv->drm,
-				    "Panel is not HDCP capable\n");
-			return -EINVAL;
-		}
-	}
+	int i;
 
 	/* Initialize An with 2 random values and acquire it */
 	for (i = 0; i < 2; i++)
@@ -784,92 +635,83 @@ static int intel_hdcp_auth(struct intel_connector
 *connector)
 		return -ETIMEDOUT;
 	}
 
-	an.reg[0] = intel_de_read(dev_priv,
-				  HDCP_ANLO(dev_priv, cpu_transcoder,
 port));
-	an.reg[1] = intel_de_read(dev_priv,
-				  HDCP_ANHI(dev_priv, cpu_transcoder,
 port));
-	ret = shim->write_an_aksv(dig_port, an.shim);
-	if (ret)
-		return ret;
+	an->words[0] = intel_de_read(dev_priv,
+				     HDCP_ANLO(dev_priv, cpu_transcoder,
 port));
+	an->words[1] = intel_de_read(dev_priv,
+				     HDCP_ANHI(dev_priv, cpu_transcoder,
 port));
 
-	r0_prime_gen_start = jiffies;
-
-	memset(&bksv, 0, sizeof(bksv));
-
-	ret = intel_hdcp_read_valid_bksv(dig_port, shim, bksv.shim);
-	if (ret < 0)
-		return ret;
-
-	if (drm_hdcp_check_ksvs_revoked(&dev_priv->drm, bksv.shim, 1) >
 0) {
-		drm_err(&dev_priv->drm, "BKSV is revoked\n");
-		return -EPERM;
-	}
-
-	intel_de_write(dev_priv, HDCP_BKSVLO(dev_priv, cpu_transcoder,
 port),
-		       bksv.reg[0]);
-	intel_de_write(dev_priv, HDCP_BKSVHI(dev_priv, cpu_transcoder,
 port),
-		       bksv.reg[1]);
-
-	ret = shim->repeater_present(dig_port, &repeater_present);
-	if (ret)
-		return ret;
-	if (repeater_present)
-		intel_de_write(dev_priv, HDCP_REP_CTL,
-			       intel_hdcp_get_repeater_ctl(dev_priv,
 cpu_transcoder, port));
+	return 0;
+}
 
-	ret = shim->toggle_signalling(dig_port, cpu_transcoder, true);

drivers/gpu/drm/i915/display/intel_hdcp.c static int intel_hdcp_auth(struct intel_connector · line 662

Quoted 10 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +7 -2
-	if (ret)
-		return ret;
+int intel_hdcp1_enable_encryption(struct drm_connector *drm_connector,
+				  void *driver_data)
+{
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);

Whenever new code is added we are going with convention of naming drm_i915_private Variable as i915 I see some more places in this patch that needs the same renaming.

Quoted 9 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +3 -0
+	enum transcoder cpu_transcoder = connector-
 >hdcp.cpu_transcoder;
+	enum port port = dig_port->base.port;
 
 	intel_de_write(dev_priv, HDCP_CONF(dev_priv, cpu_transcoder,
 port),
 		       HDCP_CONF_AUTH_AND_ENC);
 
+	return 0;

drivers/gpu/drm/i915/display/intel_hdcp.c static int intel_hdcp_auth(struct intel_connector *connector) · line 668

Quoted 10 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +9 -0
+}
+
+int intel_hdcp1_wait_for_r0(struct drm_connector *drm_connector,
+			    void *driver_data)
+{
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);

I915 rename here.

Quoted 53 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +3 -1
+	enum transcoder cpu_transcoder = connector-
 >hdcp.cpu_transcoder;
+	enum port port = dig_port->base.port;
+
 	/* Wait for R0 ready */
-	if (wait_for(intel_de_read(dev_priv, HDCP_STATUS(dev_priv,

cpu_transcoder, port)) &

drivers/gpu/drm/i915/display/intel_hdcp.c +2 -1
-		     (HDCP_STATUS_R0_READY | HDCP_STATUS_ENC), 1)) {
+	if (wait_for((intel_de_read(dev_priv,
+

HDCP_STATUS(dev_priv, cpu_transcoder, port))) &

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
+			     (HDCP_STATUS_R0_READY | HDCP_STATUS_ENC),

1)) {
drm_err(&dev_priv->drm, "Timed out waiting for R0
ready\n");

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -2
 		return -ETIMEDOUT;
 	}
 
-	/*
-	 * Wait for R0' to become available. The spec says 100ms from Aksv,

but

drivers/gpu/drm/i915/display/intel_hdcp.c +2 -12
-	 * some monitors can take longer than this. We'll set the timeout at
-	 * 300ms just to be sure.
-	 *
-	 * On DP, there's an R0_READY bit available but no such bit
-	 * exists on HDMI. Since the upper-bound is the same, we'll just do
-	 * the stupid thing instead of polling on one and not the other.
-	 */
-	wait_remaining_ms_from_jiffies(r0_prime_gen_start, 300);
-
-	tries = 3;
+	return 0;
+}
 
-	/*
-	 * DP HDCP Spec mandates the two more reattempt to read R0,

incase

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -10
-	 * of R0 mismatch.
-	 */
-	for (i = 0; i < tries; i++) {
-		ri.reg = 0;
-		ret = shim->read_ri_prime(dig_port, ri.shim);
-		if (ret)
-			return ret;
-		intel_de_write(dev_priv,
-			       HDCP_RPRIME(dev_priv, cpu_transcoder, port),
-			       ri.reg);
+int intel_hdcp1_match_ri(struct drm_connector *drm_connector, u32

ri_prime,

drivers/gpu/drm/i915/display/intel_hdcp.c +3 -0
+			 void *driver_data)
+{
+	struct intel_connector *connector =

to_intel_connector(drm_connector);

drivers/gpu/drm/i915/display/intel_hdcp.c int intel_hdcp2_capable(struct intel_connector *connector, bool *capable) · line 172

Quoted 3 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +3 -0
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);

I915 rename here.

Quoted 37 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
+	enum transcoder cpu_transcoder = connector-
Quoted 1 line

hdcp.cpu_transcoder;

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -2
+	enum port port = dig_port->base.port;
 
-		/* Wait for Ri prime match */
-		if (!wait_for(intel_de_read(dev_priv,

HDCP_STATUS(dev_priv, cpu_transcoder, port)) &

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -1
-			      (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC),

1))

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -2
-			break;
-	}
+	intel_de_write(dev_priv, HDCP_RPRIME(dev_priv, cpu_transcoder,

port),

drivers/gpu/drm/i915/display/intel_hdcp.c +4 -5
+		       ri_prime);
 
-	if (i == tries) {
-		drm_dbg_kms(&dev_priv->drm,
-			    "Timed out waiting for Ri prime match (%x)\n",
-			    intel_de_read(dev_priv, HDCP_STATUS(dev_priv,
-					  cpu_transcoder, port)));
+	/* Wait for Ri prime match */
+	if (wait_for(intel_de_read(dev_priv,
+				   HDCP_STATUS(dev_priv, cpu_transcoder,

port)) &

drivers/gpu/drm/i915/display/intel_hdcp.c +10 -1
+			     (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC),
+		     1))
 		return -ETIMEDOUT;
-	}
+
+	return 0;
+}
+
+int intel_hdcp1_post_encryption(struct drm_connector *drm_connector,
+				void *driver_data)
+{
+	struct intel_connector *connector =

to_intel_connector(drm_connector);

drivers/gpu/drm/i915/display/intel_hdcp.c int intel_hdcp2_capable(struct intel_connector *connector, bool *capable) · line 172

Quoted 3 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +3 -0
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);

I915 rename here.

Regards,
Suraj Kandpal

Quoted 1,245 lines
drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
+	enum transcoder cpu_transcoder = connector-
Quoted 1 line

hdcp.cpu_transcoder;

drivers/gpu/drm/i915/display/intel_hdcp.c +3 -18
+	enum port port = dig_port->base.port;
 
 	/* Wait for encryption confirmation */
 	if (intel_de_wait_for_set(dev_priv,
@@ -880,56 +722,22 @@ static int intel_hdcp_auth(struct intel_connector
 *connector)
 		return -ETIMEDOUT;
 	}
 
-	/* DP MST Auth Part 1 Step 2.a and Step 2.b */
-	if (shim->stream_encryption) {
-		ret = shim->stream_encryption(connector, true);
-		if (ret) {
-			drm_err(&dev_priv->drm, "[%s:%d] Failed to enable
 HDCP 1.4 stream enc\n",
-				connector->base.name, connector-
 >base.base.id);
-			return ret;
-		}
-		drm_dbg_kms(&dev_priv->drm, "HDCP 1.4 transcoder: %s
 stream encrypted\n",
-			    transcoder_name(hdcp->stream_transcoder));
-	}
-
-	if (repeater_present)
-		return intel_hdcp_auth_downstream(connector);
-
-	drm_dbg_kms(&dev_priv->drm, "HDCP is enabled (no repeater
 present)\n");
 	return 0;
 }
 
-static int _intel_hdcp_disable(struct intel_connector *connector)
+int intel_hdcp1_disable(struct drm_connector *drm_connector, void
 *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
 	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	struct intel_hdcp *hdcp = &connector->hdcp;
 	enum port port = dig_port->base.port;
 	enum transcoder cpu_transcoder = hdcp->cpu_transcoder;
 	u32 repeater_ctl;
-	int ret;
 
 	drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP is being

disabled...\n",

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -4
 		    connector->base.name, connector->base.base.id);
 
-	if (hdcp->shim->stream_encryption) {
-		ret = hdcp->shim->stream_encryption(connector, false);
-		if (ret) {
-			drm_err(&dev_priv->drm, "[%s:%d] Failed to disable

HDCP 1.4 stream enc\n",

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -1
-				connector->base.name, connector-
Quoted 1 line

base.base.id);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -3
-			return ret;
-		}
-		drm_dbg_kms(&dev_priv->drm, "HDCP 1.4 transcoder: %s

stream encryption disabled\n",

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -10
-			    transcoder_name(hdcp->stream_transcoder));
-		/*
-		 * If there are other connectors on this port using HDCP,
-		 * don't disable it until it disabled HDCP encryption for
-		 * all connectors in MST topology.
-		 */
-		if (dig_port->num_hdcp_streams > 0)
-			return 0;
-	}
-
 	hdcp->hdcp_encrypted = false;
 	intel_de_write(dev_priv, HDCP_CONF(dev_priv, cpu_transcoder,

port), 0);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -14
 	if (intel_de_wait_for_clear(dev_priv,
@@ -945,190 +753,9 @@ static int _intel_hdcp_disable(struct
 intel_connector *connector)
 	intel_de_write(dev_priv, HDCP_REP_CTL,
 		       intel_de_read(dev_priv, HDCP_REP_CTL) &
 ~repeater_ctl);
 
-	ret = hdcp->shim->toggle_signalling(dig_port, cpu_transcoder, false);
-	if (ret) {
-		drm_err(&dev_priv->drm, "Failed to disable HDCP
 signalling\n");
-		return ret;
-	}
-
-	drm_dbg_kms(&dev_priv->drm, "HDCP is disabled\n");
 	return 0;
 }
 
-static int _intel_hdcp_enable(struct intel_connector *connector)
-{
-	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
-	struct intel_hdcp *hdcp = &connector->hdcp;
-	int i, ret, tries = 3;
-
-	drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP is being

enabled...\n",

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -4
-		    connector->base.name, connector->base.base.id);
-
-	if (!hdcp_key_loadable(dev_priv)) {
-		drm_err(&dev_priv->drm, "HDCP key Load is not

possible\n");

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -11
-		return -ENXIO;
-	}
-
-	for (i = 0; i < KEY_LOAD_TRIES; i++) {
-		ret = intel_hdcp_load_keys(dev_priv);
-		if (!ret)
-			break;
-		intel_hdcp_clear_keys(dev_priv);
-	}
-	if (ret) {
-		drm_err(&dev_priv->drm, "Could not load HDCP keys,

(%d)\n",

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -13
-			ret);
-		return ret;
-	}
-
-	/* Incase of authentication failures, HDCP spec expects reauth. */
-	for (i = 0; i < tries; i++) {
-		ret = intel_hdcp_auth(connector);
-		if (!ret) {
-			hdcp->hdcp_encrypted = true;
-			return 0;
-		}
-
-		drm_dbg_kms(&dev_priv->drm, "HDCP Auth failure (%d)\n",

ret);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -11
-
-		/* Ensuring HDCP encryption and signalling are stopped. */
-		_intel_hdcp_disable(connector);
-	}
-
-	drm_dbg_kms(&dev_priv->drm,
-		    "HDCP authentication failed (%d tries/%d)\n", tries, ret);
-	return ret;
-}
-
-static struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp

*hdcp)

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -9
-{
-	return container_of(hdcp, struct intel_connector, hdcp);
-}
-
-static void intel_hdcp_update_value(struct intel_connector *connector,
-				    u64 value, bool update_property)
-{
-	struct drm_device *dev = connector->base.dev;
-	struct intel_digital_port *dig_port =

intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -3
-	struct intel_hdcp *hdcp = &connector->hdcp;
-
-	drm_WARN_ON(connector->base.dev, !mutex_is_locked(&hdcp-
Quoted 1 line

mutex));

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -8
-
-	if (hdcp->value == value)
-		return;
-
-	drm_WARN_ON(dev, !mutex_is_locked(&dig_port->hdcp_mutex));
-
-	if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
-		if (!drm_WARN_ON(dev, dig_port->num_hdcp_streams ==

0))

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -16
-			dig_port->num_hdcp_streams--;
-	} else if (value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
-		dig_port->num_hdcp_streams++;
-	}
-
-	hdcp->value = value;
-	if (update_property) {
-		drm_connector_get(&connector->base);
-		schedule_work(&hdcp->prop_work);
-	}
-}
-
-/* Implements Part 3 of the HDCP authorization procedure */
-static int intel_hdcp_check_link(struct intel_connector *connector)
-{
-	struct intel_digital_port *dig_port =

intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -20
-	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
-	struct intel_hdcp *hdcp = &connector->hdcp;
-	enum port port = dig_port->base.port;
-	enum transcoder cpu_transcoder;
-	int ret = 0;
-
-	mutex_lock(&hdcp->mutex);
-	mutex_lock(&dig_port->hdcp_mutex);
-
-	cpu_transcoder = hdcp->cpu_transcoder;
-
-	/* Check_link valid only when HDCP1.4 is enabled */
-	if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_ENABLED ||
-	    !hdcp->hdcp_encrypted) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	if (drm_WARN_ON(&dev_priv->drm,
-			!intel_hdcp_in_use(dev_priv, cpu_transcoder, port)))

{

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -4
-		drm_err(&dev_priv->drm,
-			"%s:%d HDCP link stopped encryption,%x\n",
-			connector->base.name, connector->base.base.id,
-			intel_de_read(dev_priv, HDCP_STATUS(dev_priv,

cpu_transcoder, port)));

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -3
-		ret = -ENXIO;
-		intel_hdcp_update_value(connector,
-

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -6
-					true);
-		goto out;
-	}
-
-	if (hdcp->shim->check_link(dig_port, connector)) {
-		if (hdcp->value !=

DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -2
-			intel_hdcp_update_value(connector,
-
	DRM_MODE_CONTENT_PROTECTION_ENABLED, true);
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -11
-		}
-		goto out;
-	}
-
-	drm_dbg_kms(&dev_priv->drm,
-		    "[%s:%d] HDCP link failed, retrying authentication\n",
-		    connector->base.name, connector->base.base.id);
-
-	ret = _intel_hdcp_disable(connector);
-	if (ret) {
-		drm_err(&dev_priv->drm, "Failed to disable hdcp (%d)\n",

ret);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -2
-		intel_hdcp_update_value(connector,
-

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -7
-					true);
-		goto out;
-	}
-
-	ret = _intel_hdcp_enable(connector);
-	if (ret) {
-		drm_err(&dev_priv->drm, "Failed to enable hdcp (%d)\n",

ret);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -2
-		intel_hdcp_update_value(connector,
-

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -18
-					true);
-		goto out;
-	}
-
-out:
-	mutex_unlock(&dig_port->hdcp_mutex);
-	mutex_unlock(&hdcp->mutex);
-	return ret;
-}
-
-static void intel_hdcp_prop_work(struct work_struct *work)
-{
-	struct intel_hdcp *hdcp = container_of(work, struct intel_hdcp,
-					       prop_work);
-	struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
-	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
-
-	drm_modeset_lock(&dev_priv-
Quoted 1 line

drm.mode_config.connection_mutex, NULL);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -4
-	mutex_lock(&hdcp->mutex);
-
-	/*
-	 * This worker is only used to flip between ENABLED/DESIRED. Either

of

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -9
-	 * those to UNDESIRED is handled by core. If value == UNDESIRED,
-	 * we're running just after hdcp has been disabled, so just exit
-	 */
-	if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
-		drm_hdcp_update_content_protection(&connector->base,
-						   hdcp->value);
-
-	mutex_unlock(&hdcp->mutex);
-	drm_modeset_unlock(&dev_priv-
Quoted 1 line

drm.mode_config.connection_mutex);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -4
-
-	drm_connector_put(&connector->base);
-}
-
 bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port

port)
{

drivers/gpu/drm/i915/display/intel_hdcp.c +2 -1
 	return RUNTIME_INFO(dev_priv)->has_hdcp &&
@@ -1961,8 +1588,9 @@ static int hdcp2_authenticate_and_encrypt(struct
 intel_connector *connector)
 	return ret;
 }
 
-static int _intel_hdcp2_enable(struct intel_connector *connector)
+int intel_hdcp2_enable(struct drm_connector *drm_connector, void
 *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
drivers/gpu/drm/i915/display/intel_hdcp.c +2 -2
 	struct drm_i915_private *i915 = to_i915(connector->base.dev);
 	struct intel_hdcp *hdcp = &connector->hdcp;
 	int ret;
@@ -1986,8 +1614,8 @@ static int _intel_hdcp2_enable(struct
 intel_connector *connector)
 	return 0;
 }
 
-static int
-_intel_hdcp2_disable(struct intel_connector *connector, bool
 hdcp2_link_recovery)
+static int _intel_hdcp2_disable(struct intel_connector *connector,
+				bool hdcp2_link_recovery, void *driver_data)
 {

struct intel_digital_port *dig_port =
intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +9 -1
 	struct drm_i915_private *i915 = to_i915(connector->base.dev);
@@ -2024,9 +1652,17 @@ _intel_hdcp2_disable(struct intel_connector
 *connector, bool hdcp2_link_recovery
 	return ret;
 }
 
+int intel_hdcp2_disable(struct drm_connector *drm_connector, void
 *driver_data)
+{
+	return _intel_hdcp2_disable(to_intel_connector(drm_connector),
 false,
+				    driver_data);
+}
+
 /* Implements the Link Integrity Check for HDCP2.2 */
-static int intel_hdcp2_check_link(struct intel_connector *connector)
+int intel_hdcp2_check_link(struct drm_connector *drm_connector,
+			   void *driver_data)
 {
+	struct intel_connector *connector =

to_intel_connector(drm_connector);
struct intel_digital_port *dig_port =
intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +7 -56
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	struct intel_hdcp *hdcp = &connector->hdcp;
@@ -2034,109 +1670,39 @@ static int intel_hdcp2_check_link(struct
 intel_connector *connector)
 	enum transcoder cpu_transcoder;
 	int ret = 0;
 
-	mutex_lock(&hdcp->mutex);
-	mutex_lock(&dig_port->hdcp_mutex);
 	cpu_transcoder = hdcp->cpu_transcoder;
 
 	/* hdcp2_check_link is expected only when HDCP2.2 is Enabled */
-	if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_ENABLED ||
-	    !hdcp->hdcp2_encrypted) {
-		ret = -EINVAL;
-		goto out;
-	}
+	if (!hdcp->hdcp2_encrypted)
+		return -EINVAL;
 
 	if (drm_WARN_ON(&dev_priv->drm,
 			!intel_hdcp2_in_use(dev_priv, cpu_transcoder,
 port))) {
 		drm_err(&dev_priv->drm,
 			"HDCP2.2 link stopped the encryption, %x\n",
 			intel_de_read(dev_priv, HDCP2_STATUS(dev_priv,
 cpu_transcoder, port)));
-		ret = -ENXIO;
-		_intel_hdcp2_disable(connector, true);
-		intel_hdcp_update_value(connector,
-
 	DRM_MODE_CONTENT_PROTECTION_DESIRED,
-					true);
-		goto out;
+		return -ENXIO;
 	}
 
 	ret = hdcp->shim->check_2_2_link(dig_port, connector);
-	if (ret == HDCP_LINK_PROTECTED) {
-		if (hdcp->value !=
 DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
-			intel_hdcp_update_value(connector,
-
 	DRM_MODE_CONTENT_PROTECTION_ENABLED,
-					true);
-		}
-		goto out;
-	}
+	if (ret == HDCP_LINK_PROTECTED)
+		return 0;
 
 	if (ret == HDCP_TOPOLOGY_CHANGE) {
-		if (hdcp->value ==
 DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
-			goto out;
-
 		drm_dbg_kms(&dev_priv->drm,
 			    "HDCP2.2 Downstream topology change\n");
 		ret = hdcp2_authenticate_repeater_topology(connector);
-		if (!ret) {
-			intel_hdcp_update_value(connector,
-
 	DRM_MODE_CONTENT_PROTECTION_ENABLED,
-					true);
-			goto out;
-		}
-		drm_dbg_kms(&dev_priv->drm,
-			    "[%s:%d] Repeater topology auth failed.(%d)\n",
-			    connector->base.name, connector->base.base.id,
-			    ret);
-	} else {
-		drm_dbg_kms(&dev_priv->drm,
-			    "[%s:%d] HDCP2.2 link failed, retrying auth\n",
-			    connector->base.name, connector->base.base.id);
-	}
-
-	ret = _intel_hdcp2_disable(connector, true);
-	if (ret) {
-		drm_err(&dev_priv->drm,
-			"[%s:%d] Failed to disable hdcp2.2 (%d)\n",
-			connector->base.name, connector->base.base.id,
 ret);
-		intel_hdcp_update_value(connector,
-
 	DRM_MODE_CONTENT_PROTECTION_DESIRED, true);
-		goto out;
-	}
+		if (!ret)
+			return 0;
 
-	ret = _intel_hdcp2_enable(connector);
-	if (ret) {
 		drm_dbg_kms(&dev_priv->drm,
-			    "[%s:%d] Failed to enable hdcp2.2 (%d)\n",
-			    connector->base.name, connector->base.base.id,
-			    ret);
-		intel_hdcp_update_value(connector,
-

DRM_MODE_CONTENT_PROTECTION_DESIRED,

drivers/gpu/drm/i915/display/intel_hdcp.c +2 -2
-					true);
-		goto out;
+			    "[%s:%d] Repeater topology auth failed.(%d)\n",
+			    connector->base.name, connector->base.base.id,

ret);

drivers/gpu/drm/i915/display/intel_hdcp.c +13 -30
 	}
 
-out:
-	mutex_unlock(&dig_port->hdcp_mutex);
-	mutex_unlock(&hdcp->mutex);
 	return ret;
 }
 
-static void intel_hdcp_check_work(struct work_struct *work)
-{
-	struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
-					       struct intel_hdcp,
-					       check_work);
-	struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
-
-	if (drm_connector_is_unregistered(&connector->base))
-		return;
-
-	if (!intel_hdcp2_check_link(connector))
-		schedule_delayed_work(&hdcp->check_work,
-				      DRM_HDCP2_CHECK_PERIOD_MS);
-	else if (!intel_hdcp_check_link(connector))
-		schedule_delayed_work(&hdcp->check_work,
-				      DRM_HDCP_CHECK_PERIOD_MS);
-}
-
 static int i915_hdcp_component_bind(struct device *i915_kdev,
 				    struct device *mei_kdev, void *data)
 {
@@ -2189,22 +1755,28 @@ static enum mei_fw_tc
 intel_get_mei_fw_tc(enum transcoder cpu_transcoder)
 	}
 }
 
-static int
-_intel_hdcp_setup(struct intel_connector *connector,
-		  const struct intel_crtc_state *pipe_config, u8 content_type)
+int intel_hdcp_setup(struct drm_connector *connector,
+		     struct drm_atomic_state *state, void *driver_data)
 {
-	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
-	struct intel_digital_port *dig_port =
 intel_attached_dig_port(connector);
-	struct intel_hdcp *hdcp = &connector->hdcp;
+	struct drm_i915_private *dev_priv = to_i915(connector->dev);
+	struct intel_connector *intel_connector =
 to_intel_connector(connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(intel_connector);
+	struct drm_connector_state *conn_state;
+	struct drm_crtc_state *crtc_state;
+	struct intel_crtc_state *pipe_config;
+	struct intel_hdcp *hdcp = &intel_connector->hdcp;
 	int ret = 0;
 
-	if (!connector->encoder) {
+	if (!intel_connector->encoder) {
 		drm_err(&dev_priv->drm, "[%s:%d] encoder is not
 initialized\n",
-			connector->base.name, connector->base.base.id);
+			connector->name, connector->base.id);
 		return -ENODEV;
 	}
 
-	hdcp->content_type = content_type;
+	conn_state = drm_atomic_get_new_connector_state(state,

connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
+	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state-
Quoted 1 line

crtc);

drivers/gpu/drm/i915/display/intel_hdcp.c +1 -0
+	pipe_config = to_intel_crtc_state(crtc_state);
 
 	if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) {
 		hdcp->cpu_transcoder = pipe_config-
Quoted 1 line

mst_master_transcoder;

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -1
@@ -2321,7 +1893,6 @@ int intel_hdcp_init(struct intel_connector
 *connector,
 {
 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
 	struct intel_hdcp *hdcp = &connector->hdcp;
-	int ret;
 
 	if (!shim)
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -14
 		return -EINVAL;
@@ -2329,174 +1900,12 @@ int intel_hdcp_init(struct intel_connector
 *connector,
 	if (is_hdcp2_supported(dev_priv))
 		intel_hdcp2_init(connector, dig_port, shim);
 
-	ret =
-	drm_connector_attach_content_protection_property(&connector-
 >base,
-							 hdcp-
 >hdcp2_supported);
-	if (ret) {
-		hdcp->hdcp2_supported = false;
-		kfree(dig_port->hdcp_port_data.streams);
-		return ret;
-	}
-
 	hdcp->shim = shim;
-	mutex_init(&hdcp->mutex);
-	INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
-	INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
 	init_waitqueue_head(&hdcp->cp_irq_queue);
 
 	return 0;
 }
 
-int intel_hdcp_enable(struct intel_connector *connector,
-		      const struct intel_crtc_state *pipe_config, u8

content_type)

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -3
-{
-	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
-	struct intel_digital_port *dig_port =

intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -12
-	struct intel_hdcp *hdcp = &connector->hdcp;
-	unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
-	bool capable;
-	int ret = -EINVAL;
-
-	if (!hdcp->shim)
-		return -ENOENT;
-
-	mutex_lock(&hdcp->mutex);
-	mutex_lock(&dig_port->hdcp_mutex);
-	drm_WARN_ON(&dev_priv->drm,
-		    hdcp->value ==

DRM_MODE_CONTENT_PROTECTION_ENABLED);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -7
-
-	ret = _intel_hdcp_setup(connector, pipe_config, content_type);
-	if (ret)
-		goto out;
-
-	/*
-	 * Considering that HDCP2.2 is more secure than HDCP1.4, If the

setup

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -7
-	 * is capable of HDCP2.2, it is preferred to use HDCP2.2.
-	 */
-	ret = intel_hdcp2_capable(connector, &capable);
-	if (capable) {
-		ret = _intel_hdcp2_enable(connector);
-		if (!ret) {
-			check_link_interval =

DRM_HDCP2_CHECK_PERIOD_MS;

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -13
-			goto out;
-		}
-	}
-
-	/*
-	 * When HDCP2.2 fails and Content Type is not Type1, HDCP1.4 will
-	 * be attempted.
-	 */
-	ret = intel_hdcp_capable(connector, &capable);
-	if (ret)
-		goto out;
-
-	if (capable && hdcp->content_type !=

DRM_MODE_HDCP_CONTENT_TYPE1)

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -5
-		ret = _intel_hdcp_enable(connector);
-
-out:
-	if (!ret) {
-		schedule_delayed_work(&hdcp->check_work,

check_link_interval);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -2
-		intel_hdcp_update_value(connector,
-

DRM_MODE_CONTENT_PROTECTION_ENABLED,

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -11
-					true);
-	}
-
-	mutex_unlock(&dig_port->hdcp_mutex);
-	mutex_unlock(&hdcp->mutex);
-	return ret;
-}
-
-int intel_hdcp_disable(struct intel_connector *connector)
-{
-	struct intel_digital_port *dig_port =

intel_attached_dig_port(connector);

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -10
-	struct intel_hdcp *hdcp = &connector->hdcp;
-	int ret = 0;
-
-	if (!hdcp->shim)
-		return -ENOENT;
-
-	mutex_lock(&hdcp->mutex);
-	mutex_lock(&dig_port->hdcp_mutex);
-
-	if (hdcp->value ==

DRM_MODE_CONTENT_PROTECTION_UNDESIRED)

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -4
-		goto out;
-
-	intel_hdcp_update_value(connector,
-
	DRM_MODE_CONTENT_PROTECTION_UNDESIRED, false);
drivers/gpu/drm/i915/display/intel_hdcp.c +0 -21
-	if (hdcp->hdcp2_encrypted)
-		ret = _intel_hdcp2_disable(connector, false);
-	else if (hdcp->hdcp_encrypted)
-		ret = _intel_hdcp_disable(connector);
-
-out:
-	mutex_unlock(&dig_port->hdcp_mutex);
-	mutex_unlock(&hdcp->mutex);
-	cancel_delayed_work_sync(&hdcp->check_work);
-	return ret;
-}
-
-void intel_hdcp_update_pipe(struct intel_atomic_state *state,
-			    struct intel_encoder *encoder,
-			    const struct intel_crtc_state *crtc_state,
-			    const struct drm_connector_state *conn_state)
-{
-	struct intel_connector *connector =
-				to_intel_connector(conn_state->connector);
-	struct intel_hdcp *hdcp = &connector->hdcp;
-	bool content_protection_type_changed, desired_and_not_enabled =

false;

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -25
-
-	if (!connector->hdcp.shim)
-		return;
-
-	content_protection_type_changed =
-		(conn_state->hdcp_content_type != hdcp->content_type &&
-		 conn_state->content_protection !=
-		 DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
-
-	/*
-	 * During the HDCP encryption session if Type change is requested,
-	 * disable the HDCP and reenable it with new TYPE value.
-	 */
-	if (conn_state->content_protection ==
-	    DRM_MODE_CONTENT_PROTECTION_UNDESIRED ||
-	    content_protection_type_changed)
-		intel_hdcp_disable(connector);
-
-	/*
-	 * Mark the hdcp state as DESIRED after the hdcp disable of type
-	 * change procedure.
-	 */
-	if (content_protection_type_changed) {
-		mutex_lock(&hdcp->mutex);
-		hdcp->value =

DRM_MODE_CONTENT_PROTECTION_DESIRED;

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -11
-		drm_connector_get(&connector->base);
-		schedule_work(&hdcp->prop_work);
-		mutex_unlock(&hdcp->mutex);
-	}
-
-	if (conn_state->content_protection ==
-	    DRM_MODE_CONTENT_PROTECTION_DESIRED) {
-		mutex_lock(&hdcp->mutex);
-		/* Avoid enabling hdcp, if it already ENABLED */
-		desired_and_not_enabled =
-			hdcp->value !=

DRM_MODE_CONTENT_PROTECTION_ENABLED;

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -3
-		mutex_unlock(&hdcp->mutex);
-		/*
-		 * If HDCP already ENABLED and CP property is DESIRED,

schedule

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -3
-		 * prop_work to update correct CP property to user space.
-		 */
-		if (!desired_and_not_enabled &&

!content_protection_type_changed) {

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -28
-			drm_connector_get(&connector->base);
-			schedule_work(&hdcp->prop_work);
-		}
-	}
-
-	if (desired_and_not_enabled || content_protection_type_changed)
-		intel_hdcp_enable(connector,
-				  crtc_state,
-				  (u8)conn_state->hdcp_content_type);
-}
-
 void intel_hdcp_component_fini(struct drm_i915_private *dev_priv)
 {
 	mutex_lock(&dev_priv->display.hdcp.comp_mutex);
@@ -2518,33 +1927,8 @@ void intel_hdcp_cleanup(struct intel_connector
 *connector)
 	if (!hdcp->shim)
 		return;
 
-	/*
-	 * If the connector is registered, it's possible userspace could kick
-	 * off another HDCP enable, which would re-spawn the workers.
-	 */
-	drm_WARN_ON(connector->base.dev,
-		connector->base.registration_state ==
 DRM_CONNECTOR_REGISTERED);
-
-	/*
-	 * Now that the connector is not registered, check_work won't be
 run,
-	 * but cancel any outstanding instances of it
-	 */
-	cancel_delayed_work_sync(&hdcp->check_work);
-
-	/*
-	 * We don't cancel prop_work in the same way as check_work since it
-	 * requires connection_mutex which could be held while calling this
-	 * function. Instead, we rely on the connector references grabbed

before

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -1
-	 * scheduling prop_work to ensure the connector is alive when

prop_work

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -2
-	 * is run. So if we're in the destroy path (which is where this
-	 * function should be called), we're "guaranteed" that prop_work is

not

drivers/gpu/drm/i915/display/intel_hdcp.c +0 -3
-	 * active (tl;dr This Should Never Happen).
-	 */
-	drm_WARN_ON(connector->base.dev, work_pending(&hdcp-
Quoted 1 line

prop_work));

drivers/gpu/drm/i915/display/intel_hdcp.c +2 -4
-
-	mutex_lock(&hdcp->mutex);
+	drm_hdcp_helper_destroy(connector->hdcp_helper_data);
 	hdcp->shim = NULL;
-	mutex_unlock(&hdcp->mutex);
 }
 
 /* Handles the CP_IRQ raised from the DP HDCP sink */
@@ -2558,5 +1942,5 @@ void intel_hdcp_handle_cp_irq(struct
 intel_connector *connector)
 	atomic_inc(&connector->hdcp.cp_irq_count);
 	wake_up_all(&connector->hdcp.cp_irq_queue);
 
-	schedule_delayed_work(&hdcp->check_work, 0);
+	drm_hdcp_helper_schedule_hdcp_check(connector-
Quoted 1 line

hdcp_helper_data);

}

drivers/gpu/drm/i915/display/intel_hdcp.h +8 -8
index f06f6e5a2b1ad..7614bbf453133 100644
@@ -10,8 +10,10 @@
 
 #define HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS	50
 
+struct drm_atomic_state;
 struct drm_connector;
 struct drm_connector_state;
+struct drm_hdcp_an;
 struct drm_i915_private;
 struct intel_atomic_state;
 struct intel_connector;
@@ -25,16 +27,35 @@ enum transcoder;
 int intel_hdcp_init(struct intel_connector *connector,
 		    struct intel_digital_port *dig_port,
 		    const struct intel_hdcp_shim *hdcp_shim);
-int intel_hdcp_enable(struct intel_connector *connector,
-		      const struct intel_crtc_state *pipe_config, u8
 content_type);
-int intel_hdcp_disable(struct intel_connector *connector);
-void intel_hdcp_update_pipe(struct intel_atomic_state *state,
-			    struct intel_encoder *encoder,
-			    const struct intel_crtc_state *crtc_state,
-			    const struct drm_connector_state *conn_state);
+int intel_hdcp_setup(struct drm_connector *drm_connector,
+		     struct drm_atomic_state *state, void *driver_data);
+int intel_hdcp_load_keys(struct drm_connector *drm_connector,
+			 void *driver_data);
 bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port
 port);
 int intel_hdcp_capable(struct intel_connector *connector, bool *capable);
-int intel_hdcp2_capable(struct intel_connector *connector, bool *capable);
+int intel_hdcp2_capable(struct drm_connector *drm_connector, bool
 *capable);
+int intel_hdcp2_enable(struct drm_connector *drm_connector, void

*driver_data);
+int intel_hdcp2_disable(struct drm_connector *drm_connector, void *driver_data);

drivers/gpu/drm/i915/display/intel_hdcp.h +12 -0
+int intel_hdcp2_check_link(struct drm_connector *drm_connector,
+			   void *driver_data);
+int intel_hdcp1_store_receiver_info(struct drm_connector *drm_connector,
+				    u32 *ksv, u32 status, u8 caps,
+				    bool repeater_present, void *driver_data);
+int intel_hdcp1_read_an(struct drm_connector *drm_connector,
+			struct drm_hdcp_an *an);
+int intel_hdcp1_enable_encryption(struct drm_connector *drm_connector,
+				  void *driver_data);
+int intel_hdcp1_wait_for_r0(struct drm_connector *drm_connector,
+			    void *driver_data);
+int intel_hdcp1_match_ri(struct drm_connector *drm_connector, u32

ri_prime,

drivers/gpu/drm/i915/display/intel_hdcp.h +8 -0
+			 void *driver_data);
+int intel_hdcp1_post_encryption(struct drm_connector *drm_connector,
+				void *driver_data);
+int intel_hdcp1_store_ksv_fifo(struct drm_connector *drm_connector,
+			       u8 *ksv_fifo, u8 num_downstream, u8 *bstatus,
+			       u32 *v_prime, void *driver_data);
+int intel_hdcp1_check_link(struct drm_connector *drm_connector);
+int intel_hdcp1_disable(struct drm_connector *drm_connector, void

*driver_data);
void intel_hdcp_component_init(struct drm_i915_private *dev_priv); void intel_hdcp_component_fini(struct drm_i915_private *dev_priv); void intel_hdcp_cleanup(struct intel_connector *connector);

drivers/gpu/drm/i915/display/intel_hdmi.c +12 -4
index c0ce6d3dc5056..5bdec16b81d1a 100644
@@ -1330,17 +1330,25 @@ static int intel_hdmi_hdcp_write(struct
 intel_digital_port *dig_port,
 	return ret;
 }
 
-static
-int intel_hdmi_hdcp_write_an_aksv(struct intel_digital_port *dig_port,
-				  u8 *an)
+static int intel_hdmi_hdcp1_send_an_aksv(struct drm_connector
 *drm_connector,
+					 void *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
 	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
 	struct intel_hdmi *hdmi = &dig_port->hdmi;
 	struct i2c_adapter *adapter = intel_gmbus_get_adapter(i915,
 							      hdmi->ddc_bus);
+	struct drm_hdcp_an an;
 	int ret;
 
-	ret = intel_hdmi_hdcp_write(dig_port, DRM_HDCP_DDC_AN, an,
+	/* Output An first, that's easy */
+	ret = intel_hdcp1_read_an(drm_connector, &an);
+	if (ret)
+		return ret;
+
+	ret = intel_hdmi_hdcp_write(dig_port, DRM_HDCP_DDC_AN,

an.bytes,

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -0
 				    DRM_HDCP_AN_LEN);
 	if (ret) {
 		drm_dbg_kms(&i915->drm, "Write An over DDC failed

(%d)\n",

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -38
@@ -1356,120 +1364,6 @@ int intel_hdmi_hdcp_write_an_aksv(struct
 intel_digital_port *dig_port,
 	return 0;
 }
 
-static int intel_hdmi_hdcp_read_bksv(struct intel_digital_port *dig_port,
-				     u8 *bksv)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-
-	int ret;
-	ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_BKSV, bksv,
-				   DRM_HDCP_KSV_LEN);
-	if (ret)
-		drm_dbg_kms(&i915->drm, "Read Bksv over DDC failed
 (%d)\n",
-			    ret);
-	return ret;
-}
-
-static
-int intel_hdmi_hdcp_read_bstatus(struct intel_digital_port *dig_port,
-				 u8 *bstatus)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-
-	int ret;
-	ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_BSTATUS,
-				   bstatus, DRM_HDCP_BSTATUS_LEN);
-	if (ret)
-		drm_dbg_kms(&i915->drm, "Read bstatus over DDC failed
 (%d)\n",
-			    ret);
-	return ret;
-}
-
-static
-int intel_hdmi_hdcp_repeater_present(struct intel_digital_port *dig_port,
-				     bool *repeater_present)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	int ret;
-	u8 val;
-
-	ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_BCAPS,

&val, 1);

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -2
-	if (ret) {
-		drm_dbg_kms(&i915->drm, "Read bcaps over DDC failed

(%d)\n",

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -4
-			    ret);
-		return ret;
-	}
-	*repeater_present = val &

DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT;

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -14
-	return 0;
-}
-
-static
-int intel_hdmi_hdcp_read_ri_prime(struct intel_digital_port *dig_port,
-				  u8 *ri_prime)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-
-	int ret;
-	ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_RI_PRIME,
-				   ri_prime, DRM_HDCP_RI_LEN);
-	if (ret)
-		drm_dbg_kms(&i915->drm, "Read Ri' over DDC failed

(%d)\n",

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -13
-			    ret);
-	return ret;
-}
-
-static
-int intel_hdmi_hdcp_read_ksv_ready(struct intel_digital_port *dig_port,
-				   bool *ksv_ready)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	int ret;
-	u8 val;
-
-	ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_BCAPS,

&val, 1);

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -2
-	if (ret) {
-		drm_dbg_kms(&i915->drm, "Read bcaps over DDC failed

(%d)\n",

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -15
-			    ret);
-		return ret;
-	}
-	*ksv_ready = val & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
-	return 0;
-}
-
-static
-int intel_hdmi_hdcp_read_ksv_fifo(struct intel_digital_port *dig_port,
-				  int num_downstream, u8 *ksv_fifo)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	int ret;
-	ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_KSV_FIFO,
-				   ksv_fifo, num_downstream *

DRM_HDCP_KSV_LEN);

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -22
-	if (ret) {
-		drm_dbg_kms(&i915->drm,
-			    "Read ksv fifo over DDC failed (%d)\n", ret);
-		return ret;
-	}
-	return 0;
-}
-
-static
-int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *dig_port,
-				      int i, u32 *part)
-{
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	int ret;
-
-	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
-		return -EINVAL;
-
-	ret = intel_hdmi_hdcp_read(dig_port, DRM_HDCP_DDC_V_PRIME(i),
-				   part, DRM_HDCP_V_PRIME_PART_LEN);
-	if (ret)
-		drm_dbg_kms(&i915->drm, "Read V'[%d] over DDC failed

(%d)\n",

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -4
-			    i, ret);
-	return ret;
-}
-
 static int kbl_repositioning_enc_en_signal(struct intel_connector

*connector,

drivers/gpu/drm/i915/display/intel_hdmi.c +8 -15
 					   enum transcoder cpu_transcoder)
 {
@@ -1538,50 +1432,42 @@ int intel_hdmi_hdcp_toggle_signalling(struct
 intel_digital_port *dig_port,
 	return 0;
 }
 
-static
-bool intel_hdmi_hdcp_check_link_once(struct intel_digital_port *dig_port,
-				     struct intel_connector *connector)
+static int
+intel_hdmi_hdcp1_enable_encryption(struct drm_connector
 *drm_connector,
+				   void *driver_data)
 {
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	enum port port = dig_port->base.port;
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
 	enum transcoder cpu_transcoder = connector-
 >hdcp.cpu_transcoder;
 	int ret;
-	union {
-		u32 reg;
-		u8 shim[DRM_HDCP_RI_LEN];
-	} ri;
 
-	ret = intel_hdmi_hdcp_read_ri_prime(dig_port, ri.shim);
+	ret = intel_hdmi_hdcp_toggle_signalling(dig_port, cpu_transcoder,
 true);
 	if (ret)
-		return false;
-
-	intel_de_write(i915, HDCP_RPRIME(i915, cpu_transcoder, port),
 ri.reg);
+		return ret;
 
-	/* Wait for Ri prime match */
-	if (wait_for((intel_de_read(i915, HDCP_STATUS(i915,

cpu_transcoder, port)) &

drivers/gpu/drm/i915/display/intel_hdmi.c +0 -4
-		      (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC)) ==
-		     (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC), 1)) {
-		drm_dbg_kms(&i915->drm, "Ri' mismatch detected (%x)\n",
-			intel_de_read(i915, HDCP_STATUS(i915,

cpu_transcoder,

drivers/gpu/drm/i915/display/intel_hdmi.c +4 -9
-							port)));
-		return false;
-	}
-	return true;
+	return intel_hdcp1_enable_encryption(drm_connector, driver_data);
 }
 
-static
-bool intel_hdmi_hdcp_check_link(struct intel_digital_port *dig_port,
-				struct intel_connector *connector)
+static int intel_hdmi_hdcp1_disable(struct drm_connector *drm_connector,
+				    void *driver_data)
 {
-	struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
-	int retry;
+	struct intel_connector *connector =

to_intel_connector(drm_connector);

drivers/gpu/drm/i915/display/intel_hdmi.c +3 -0
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
+	enum transcoder cpu_transcoder = connector-
Quoted 1 line

hdcp.cpu_transcoder;

drivers/gpu/drm/i915/display/intel_hdmi.c +5 -3
+	struct drm_i915_private *i915 = to_i915(connector->base.dev);
+	int ret;
 
-	for (retry = 0; retry < 3; retry++)
-		if (intel_hdmi_hdcp_check_link_once(dig_port, connector))
-			return true;
+	ret = intel_hdcp1_disable(drm_connector, driver_data);
+	if (ret) {
+		drm_err(&i915->drm, "[%s:%d] Failed to disable HDCP

1.4\n",

drivers/gpu/drm/i915/display/intel_hdmi.c +14 -5
+			connector->base.name, connector->base.base.id);
+		return ret;
+	}
 
-	drm_err(&i915->drm, "Link check failed\n");
-	return false;
+	return intel_hdmi_hdcp_toggle_signalling(dig_port, cpu_transcoder,
+						 false);
 }
 
 struct hdcp2_hdmi_msg_timeout {
@@ -1747,13 +1633,19 @@ int intel_hdmi_hdcp2_check_link(struct
 intel_digital_port *dig_port,
 	return ret;
 }
 
-static
-int intel_hdmi_hdcp2_capable(struct intel_digital_port *dig_port,
-			     bool *capable)
+static int intel_hdmi_hdcp2_capable(struct drm_connector
 *drm_connector,
+				    bool *capable, void *driver_data)
 {
+	struct intel_connector *connector =
 to_intel_connector(drm_connector);
+	struct intel_digital_port *dig_port =
+		intel_attached_dig_port(connector);
 	u8 hdcp2_version;
 	int ret;
 
+	ret = intel_hdcp2_capable(drm_connector, capable);
+	if (ret || !capable)
+		return ret;
+
drivers/gpu/drm/i915/display/intel_hdmi.c +0 -0
 	*capable = false;
 	ret = intel_hdmi_hdcp_read(dig_port,

HDCP_2_2_HDMI_REG_VER_OFFSET,

drivers/gpu/drm/i915/display/intel_hdmi.c +23 -11
 				   &hdcp2_version, sizeof(hdcp2_version));
@@ -1764,23 +1656,38 @@ int intel_hdmi_hdcp2_capable(struct
 intel_digital_port *dig_port,
 }
 
 static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
-	.write_an_aksv = intel_hdmi_hdcp_write_an_aksv,
-	.read_bksv = intel_hdmi_hdcp_read_bksv,
-	.read_bstatus = intel_hdmi_hdcp_read_bstatus,
-	.repeater_present = intel_hdmi_hdcp_repeater_present,
-	.read_ri_prime = intel_hdmi_hdcp_read_ri_prime,
-	.read_ksv_ready = intel_hdmi_hdcp_read_ksv_ready,
-	.read_ksv_fifo = intel_hdmi_hdcp_read_ksv_fifo,
-	.read_v_prime_part = intel_hdmi_hdcp_read_v_prime_part,
 	.toggle_signalling = intel_hdmi_hdcp_toggle_signalling,
-	.check_link = intel_hdmi_hdcp_check_link,
 	.write_2_2_msg = intel_hdmi_hdcp2_write_msg,
 	.read_2_2_msg = intel_hdmi_hdcp2_read_msg,
-	.check_2_2_link	= intel_hdmi_hdcp2_check_link,
-	.hdcp_2_2_capable = intel_hdmi_hdcp2_capable,
+	.check_2_2_link = intel_hdmi_hdcp2_check_link,
 	.protocol = HDCP_PROTOCOL_HDMI,
 };
 
+static const struct drm_hdcp_helper_funcs intel_hdmi_hdcp_helper_funcs =
 {
+	.setup = intel_hdcp_setup,
+	.load_keys = intel_hdcp_load_keys,
+	.hdcp2_capable = intel_hdmi_hdcp2_capable,
+	.hdcp2_enable = intel_hdcp2_enable,
+	.hdcp2_check_link = intel_hdcp2_check_link,
+	.hdcp2_disable = intel_hdcp2_disable,
+	.hdcp1_send_an_aksv = intel_hdmi_hdcp1_send_an_aksv,
+	.hdcp1_store_receiver_info = intel_hdcp1_store_receiver_info,
+	.hdcp1_enable_encryption = intel_hdmi_hdcp1_enable_encryption,
+	.hdcp1_wait_for_r0 = intel_hdcp1_wait_for_r0,
+	.hdcp1_match_ri = intel_hdcp1_match_ri,
+	.hdcp1_post_encryption = intel_hdcp1_post_encryption,
+	.hdcp1_store_ksv_fifo = intel_hdcp1_store_ksv_fifo,
+	.hdcp1_disable = intel_hdmi_hdcp1_disable,
+	/* Common DRM functions that are different between HDMI vs DP*/
+	.remote_read = drm_hdcp_remote_ddc_read,
+	.remote_write = drm_hdcp_remote_ddc_write,
+	.hdcp1_capable = drm_hdcp_helper_hdcp1_capable_hdmi,
+	.hdcp1_ksv_fifo_ready =
 drm_hdcp_helper_hdcp1_ksv_fifo_ready_hdmi,
+	.wait_r0 = drm_hdcp_helper_wait_r0_hdmi,
+	.hdcp1_check_link_registers =

drm_hdcp_hdcp1_check_link_registers_hdmi,

drivers/gpu/drm/i915/display/intel_hdmi.c +1 -0
+	.hdcp1_read_ksv_fifo =

drm_hdcp_helper_hdcp1_read_ksv_fifo_hdmi,

drivers/gpu/drm/i915/display/intel_hdmi.c +2 -0
+};
+
 static int intel_hdmi_source_max_tmds_clock(struct intel_encoder

*encoder)
{

drivers/gpu/drm/i915/display/intel_hdmi.c +31 -0
 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
@@ -2928,6 +2835,37 @@ void intel_infoframe_init(struct intel_digital_port
 *dig_port)
 	}
 }
 
+static void intel_hdmi_hdcp_init(struct intel_digital_port *dig_port,
+				 struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+	struct intel_encoder *intel_encoder = &dig_port->base;
+	enum port port = intel_encoder->port;
+	struct drm_hdcp_helper_data *data;
+	int ret;
+
+	if (!is_hdcp_supported(dev_priv, port))
+		return;
+
+	data = drm_hdcp_helper_initialize_hdmi(
+		&connector->base, &intel_hdmi_hdcp_helper_funcs, true);
+	if (IS_ERR(data)) {
+		drm_dbg_kms(&dev_priv->drm, "HDCP init failed ret=%ld\n",
+			    PTR_ERR(data));
+		return;
+	}
+
+	ret = intel_hdcp_init(connector, dig_port, &intel_hdmi_hdcp_shim);
+	if (ret) {
+		drm_hdcp_helper_destroy(data);
+		drm_dbg_kms(&dev_priv->drm, "Intel HDCP init failed
 ret=%d\n",
+			    ret);
+		return;
+	}
+
+	connector->hdcp_helper_data = data;
+}
+
 void intel_hdmi_init_connector(struct intel_digital_port *dig_port,
drivers/gpu/drm/i915/display/intel_hdmi.c +1 -7
 			       struct intel_connector *intel_connector)
 {
@@ -2982,13 +2920,7 @@ void intel_hdmi_init_connector(struct
 intel_digital_port *dig_port,
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
 	intel_hdmi->attached_connector = intel_connector;
 
-	if (is_hdcp_supported(dev_priv, port)) {
-		int ret = intel_hdcp_init(intel_connector, dig_port,
-					  &intel_hdmi_hdcp_shim);
-		if (ret)
-			drm_dbg_kms(&dev_priv->drm,
-				    "HDCP init failed, skipping.\n");
-	}
+	intel_hdmi_hdcp_init(dig_port, intel_connector);
 
 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be

written
* 0xd. Failure to do so will result in spurious interrupts being
--
2.40.0.348.gf938b09366-goog

#14 lore raw

PATCH v8 8/10 dt-bindings: msm/dp: Add bindings for HDCP registers

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Add the bindings for the MSM DisplayPort HDCP registers which are required to write the HDCP key into the display controller as well as the registers to enable HDCP authentication/key exchange/encryption.

---
Changes in v2:
-Drop register range names (Stephen)
-Fix yaml errors (Rob)
Changes in v3:
-Add new compatible string for dp-hdcp
-Add descriptions to reg
-Add minItems/maxItems to reg
-Make reg depend on the new hdcp compatible string Changes in v4:
-Rebase on Bjorn's multi-dp patchset
Changes in v4.5:
-Remove maxItems from reg (Rob)
-Remove leading zeros in example (Rob)
Changes in v5:
-None
Changes in v6:
-Rebased: modify minItems instead of adding it as new line. Changes in v7:
-Revert the change to minItems
-Added the maxItems to Reg
Changes in v8:
-None

 .../devicetree/bindings/display/msm/dp-controller.yaml     | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/display/msm/dp-controller.yaml +6 -1
index 0e8d8df686dc9..4763a2ff12fb7 100644
@@ -34,6 +34,8 @@ properties:
       - description: link register block
       - description: p0 register block
       - description: p1 register block
+      - description: (Optional) Registers for HDCP device key injection
+      - description: (Optional) Registers for HDCP TrustZone interaction
 
   interrupts:
     maxItems: 1
@@ -159,6 +161,7 @@ allOf:
         aux-bus: false
         reg:
           minItems: 5
+          maxItems: 7
       required:
         - "#sound-dai-cells"
 
@@ -176,7 +179,9 @@ examples:
               <0xae90200 0x200>,
               <0xae90400 0xc00>,
               <0xae91000 0x400>,
-              <0xae91400 0x400>;
+              <0xae91400 0x400>,
+              <0xaed1000 0x174>,
+              <0xaee1000 0x2c>;
         interrupt-parent = <&mdss>;
         interrupts = <12>;
         clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
Signature · 1 line
2.40.0.348.gf938b09366-goog
#15 lore raw

PATCH v8 9/10 arm64: dts: qcom: sc7180: Add support for HDCP in dp-controller

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Add the register ranges required for HDCP key injection and HDCP TrustZone interaction as described in the dt-bindings for the sc7180 dp controller.

---
Changes in v3:
-Split off into a new patch containing just the dts change (Stephen) -Add hdcp compatible string (Stephen)
Changes in v4:
-Rebase on Bjorn's multi-dp patchset
Changes in v5:
-Put the tz register offsets in trogdor dtsi (Rob C)
Changes in v6:
-Rebased: Removed modifications in sc7180.dtsi as it's already upstream Changes in v7:
-Change registers offset
Changes in v8:
-None

 arch/arm64/boot/dts/qcom/sc7180-trogdor.dtsi | 8 ++++++++
 1 file changed, 8 insertions(+)
arch/arm64/boot/dts/qcom/sc7180-trogdor.dtsi +8 -0
index 423630c4d02c7..89d913fa6e3eb 100644
@@ -822,6 +822,14 @@ &mdss_dp {
 	status = "okay";
 	pinctrl-names = "default";
 	pinctrl-0 = <&dp_hot_plug_det>;
+
+	reg = <0 0x0ae90000 0 0x200>,
+	      <0 0x0ae90200 0 0x200>,
+	      <0 0x0ae90400 0 0xc00>,
+	      <0 0x0ae91000 0 0x400>,
+	      <0 0x0ae91400 0 0x400>,
+	      <0 0x0aed1000 0 0x174>,
+	      <0 0x0aee1000 0 0x2c>;
 };
 
 &mdss_dp_out {
Signature · 1 line
2.40.0.348.gf938b09366-goog
Reviewed-by on v8 9/10
#16 lore raw

Re: [PATCH v8 09/10] arm64: dts: qcom: sc7180: Add support for HDCP in dp-controller

Quoted 25 lines · Mark Yacoub

From: Sean Paul <[email protected]>

Add the register ranges required for HDCP key injection and HDCP TrustZone interaction as described in the dt-bindings for the sc7180 dp controller.

---
Changes in v3:

(quoted diff) +0 -2
-Split off into a new patch containing just the dts change (Stephen)
-Add hdcp compatible string (Stephen)

Changes in v4:
-Rebase on Bjorn's multi-dp patchset
Changes in v5:
-Put the tz register offsets in trogdor dtsi (Rob C)
Changes in v6:
-Rebased: Removed modifications in sc7180.dtsi as it's already upstream Changes in v7:
-Change registers offset
Changes in v8:
-None

Quoted 2 lines
  arch/arm64/boot/dts/qcom/sc7180-trogdor.dtsi | 8 ++++++++
  1 file changed, 8 insertions(+)
Signature · 2 lines
With best wishes
Dmitry
Reviewed-by on v8 9/10
#17 lore raw

Re: [PATCH v8 09/10] arm64: dts: qcom: sc7180: Add support for HDCP in dp-controller

In reply to Mark Yacoub
Quoted 25 lines · Mark Yacoub

From: Sean Paul <[email protected]>

Add the register ranges required for HDCP key injection and HDCP TrustZone interaction as described in the dt-bindings for the sc7180 dp controller.

---
Changes in v3:

(quoted diff) +0 -2
-Split off into a new patch containing just the dts change (Stephen)
-Add hdcp compatible string (Stephen)

Changes in v4:
-Rebase on Bjorn's multi-dp patchset
Changes in v5:
-Put the tz register offsets in trogdor dtsi (Rob C)
Changes in v6:
-Rebased: Removed modifications in sc7180.dtsi as it's already upstream Changes in v7:
-Change registers offset
Changes in v8:
-None

Quoted 2 lines
  arch/arm64/boot/dts/qcom/sc7180-trogdor.dtsi | 8 ++++++++
  1 file changed, 8 insertions(+)
Signature · 2 lines
With best wishes
Dmitry
#18 lore raw

PATCH v8 10/10 drm/msm: Implement HDCP 1.x using the new drm HDCP helpers

In reply to Mark Yacoub

From: Sean Paul <[email protected]>

Add HDCP 1.x support to msm DP bridges using the new HDCP helpers.

---

Changes in v2:
-Squash [1] into this patch with the following changes (Stephen)
-Update the sc7180 dtsi file
-Remove resource names and just use index (Stephen)
Changes in v3:
-Split out the dtsi change from v2 (Stephen)
-Fix set-but-unused warning identified by 0-day
-Fix up a couple of style nits (Stephen)
-Store HDCP key directly in dp_hdcp struct (Stephen)
-Remove wmb in HDCP key initialization, move an_seed (Stephen) -Use FIELD_PREP for bstatus/bcaps (Stephen)
-#define read_poll_timeout values (Stephen)
-Remove unnecessary parentheses in dp_hdcp_store_ksv_fifo (Stephen) -Add compatible string for hdcp (Stephen)
-Rename dp_hdcp_write_* functions (Abhinav)
-Add 1us delay between An reads (Abhinav)
-Delete unused dp_hdcp_read_* functions
Changes in v4:
-Rebase on Bjorn's multi-dp patchset
Changes in v5:
-Change return check of drm_hdcp_helper_initialize_dp() (Stephen) Changes in v6:
-Change the tracking of the state from connector state to bridge as state as drm_connector_state is no longer tracked and the functionality has moved to msm_dp_bridge
Changes in v7:
-Use dp bridge to maintain the state with no use for connector Changes in v8:
-Move the hdcp read/write to dp_catalog

 drivers/gpu/drm/msm/Kconfig         |   1 +
 drivers/gpu/drm/msm/Makefile        |   1 +
 drivers/gpu/drm/msm/dp/dp_catalog.c | 156 +++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h |  18 ++
 drivers/gpu/drm/msm/dp/dp_debug.c   |  46 +++-
 drivers/gpu/drm/msm/dp/dp_debug.h   |  11 +-
 drivers/gpu/drm/msm/dp/dp_display.c |  39 ++-
 drivers/gpu/drm/msm/dp/dp_display.h |   5 +
 drivers/gpu/drm/msm/dp/dp_drm.c     |  39 ++-
 drivers/gpu/drm/msm/dp/dp_drm.h     |   7 +
 drivers/gpu/drm/msm/dp/dp_hdcp.c    | 397 ++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_hdcp.h    |  33 +++
 drivers/gpu/drm/msm/dp/dp_parser.c  |  14 +
 drivers/gpu/drm/msm/dp/dp_parser.h  |   4 +
 drivers/gpu/drm/msm/dp/dp_reg.h     |  30 ++-
 drivers/gpu/drm/msm/msm_atomic.c    |  19 ++
 16 files changed, 808 insertions(+), 12 deletions(-)
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hdcp.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_hdcp.h
drivers/gpu/drm/msm/Kconfig +1 -0
index 4d8fddbdcd9e0..1c369ca2ea6e3 100644
@@ -15,6 +15,7 @@ config DRM_MSM
 	select REGULATOR
 	select DRM_DP_AUX_BUS
 	select DRM_DISPLAY_DP_HELPER
+	select DRM_DISPLAY_HDCP_HELPER
 	select DRM_DISPLAY_HELPER
 	select DRM_KMS_HELPER
 	select DRM_PANEL
drivers/gpu/drm/msm/Makefile +1 -0
index 7274c41228ed9..a73e7b858af27 100644
@@ -122,6 +122,7 @@ msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
 	dp/dp_ctrl.o \
 	dp/dp_display.o \
 	dp/dp_drm.o \
+	dp/dp_hdcp.o \
 	dp/dp_hpd.o \
 	dp/dp_link.o \
 	dp/dp_panel.o \
drivers/gpu/drm/msm/dp/dp_catalog.c +156 -0

177 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_catalog.h +18 -0
index 1f717f45c1158..565dbe3d802e3 100644
@@ -135,4 +135,22 @@ void dp_catalog_audio_config_sdp(struct dp_catalog *catalog);
 void dp_catalog_audio_init(struct dp_catalog *catalog);
 void dp_catalog_audio_sfe_level(struct dp_catalog *catalog);
 
+/* HDCP APIs */
+u32 dp_catalog_hdcp_read_ahb(struct dp_catalog *dp_catalog, u32 offset);
+void dp_catalog_hdcp_write_ahb(struct dp_catalog *dp_catalog, u32 offset,
+			       u32 val);
+void dp_catalog_hdcp_disable_hdcp1(struct dp_catalog *dp_catalog);
+void dp_catalog_hdcp_read_aksv(struct dp_catalog *dp_catalog, u32 *aksv);
+void dp_catalog_hdcp_write_aksv(struct dp_catalog *dp_catalog, u32 *aksv);
+void dp_catalog_hdcp_read_an(struct dp_catalog *dp_catalog, u32 *an);
+void dp_catalog_hdcp_clear_a_info(struct dp_catalog *dp_catalog);
+void dp_catalog_hdcp_write_key_words(struct dp_catalog *dp_catalog,
+				     int key_offset, u32 *words);
+void dp_catalog_hdcp_post_write_key(struct dp_catalog *dp_catalog);
+void dp_catalog_hdcp_hdcp1_store_receiver_info(struct dp_catalog *dp_catalog,
+					       u32 *ksv, u32 status, u8 bcaps);
+void dp_catalog_hdcp_reset_sha_comp_block(struct dp_catalog *dp_catalog);
+void dp_catalog_hdcp_write_tz(struct dp_catalog *dp_catalog, u32 offset,
+			      u32 val);
+
 #endif /* _DP_CATALOG_H_ */
drivers/gpu/drm/msm/dp/dp_debug.c +44 -2

91 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_debug.h +7 -4
index 8c0d0b5178fdf..ab6c5e8e5ea63 100644
@@ -6,6 +6,7 @@
 #ifndef _DP_DEBUG_H_
 #define _DP_DEBUG_H_
 
+#include "dp_hdcp.h"
 #include "dp_panel.h"
 #include "dp_link.h"
 
@@ -34,6 +35,7 @@ struct dp_debug {
  * @panel: instance of panel module
  * @usbpd: instance of usbpd module
  * @link: instance of link module
+ * @hdcp: instance of hdcp module
  * @connector: double pointer to display connector
  * @minor: pointer to drm minor number after device registration
  * return: pointer to allocated debug module data
@@ -42,9 +44,10 @@ struct dp_debug {
  * for debugfs input to be communicated with existing modules
  */
 struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
-		struct dp_usbpd *usbpd, struct dp_link *link,
-		struct drm_connector *connector,
-		struct drm_minor *minor);
+			      struct dp_usbpd *usbpd, struct dp_link *link,
+			      struct dp_hdcp *hdcp,
+			      struct drm_connector *connector,
+			      struct drm_minor *minor);
 
 /**
  * dp_debug_put()
@@ -59,7 +62,7 @@ void dp_debug_put(struct dp_debug *dp_debug);
 
 static inline
 struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
-		struct dp_usbpd *usbpd, struct dp_link *link,
+		struct dp_usbpd *usbpd, struct dp_link *link, struct dp_hdcp *hdcp,
 		struct drm_connector *connector, struct drm_minor *minor)
 {
 	return ERR_PTR(-EINVAL);
drivers/gpu/drm/msm/dp/dp_display.c +38 -1
index bde1a7ce442ff..7c90d7cefedbc 100644
@@ -27,6 +27,7 @@
 #include "dp_drm.h"
 #include "dp_audio.h"
 #include "dp_debug.h"
+#include "dp_hdcp.h"
 
 #define HPD_STRING_SIZE 30
 
@@ -97,6 +98,7 @@ struct dp_display_private {
 	struct dp_panel   *panel;
 	struct dp_ctrl    *ctrl;
 	struct dp_debug   *debug;
+	struct dp_hdcp *hdcp;
 
 	struct dp_usbpd_cb usbpd_cb;
 	struct dp_display_mode dp_mode;
@@ -190,6 +192,14 @@ static struct dp_display_private *dev_get_dp_display_private(struct device *dev)
 	return container_of(dp, struct dp_display_private, dp_display);
 }
 
+struct dp_hdcp *dp_display_bridge_to_hdcp(struct drm_bridge *bridge)
+{
+	struct msm_dp *dp_display = msm_dp_from_bridge(bridge);
+	struct dp_display_private *dp =
+		container_of(dp_display, struct dp_display_private, dp_display);
+	return dp->hdcp;
+}
+
 static int dp_add_event(struct dp_display_private *dp_priv, u32 event,
 						u32 data, u32 delay)
 {
@@ -752,6 +762,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
 static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
 {
 	dp_debug_put(dp->debug);
+	dp_hdcp_put(dp->hdcp);
 	dp_audio_put(dp->audio);
 	dp_panel_put(dp->panel);
 	dp_aux_put(dp->aux);
@@ -852,8 +863,18 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
 	dp->ctrl->wide_bus_en = dp->wide_bus_en;
 	dp->catalog->wide_bus_en = dp->wide_bus_en;
 
+	dp->hdcp = dp_hdcp_get(dp->parser, dp->aux);
+	if (IS_ERR(dp->hdcp)) {
+		rc = PTR_ERR(dp->hdcp);
+		DRM_ERROR("failed to initialize hdcp, rc = %d\n", rc);
+		dp->hdcp = NULL;
+		goto error_hdcp;
+	}
+
 	return rc;
 
+error_hdcp:
+	dp_audio_put(dp->audio);
 error_ctrl:
 	dp_panel_put(dp->panel);
 error_link:
@@ -965,6 +986,15 @@ int dp_display_set_plugged_cb(struct msm_dp *dp_display,
 	return 0;
 }
 
+void dp_display_hdcp_commit(struct msm_dp *dp, struct drm_atomic_state *state)
+{
+	struct dp_display_private *dp_display =
+		container_of(dp, struct dp_display_private, dp_display);
+
+	if (dp_display->hdcp)
+		dp_hdcp_commit(dp_display->hdcp, state);
+}
+
 /**
  * dp_bridge_mode_valid - callback to determine if specified mode is valid
  * @bridge: Pointer to drm bridge structure
@@ -1528,7 +1558,7 @@ void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
 	dev = &dp->pdev->dev;
 
 	dp->debug = dp_debug_get(dev, dp->panel, dp->usbpd,
-					dp->link, dp->dp_display.connector,
+					dp->link, dp->hdcp, dp->dp_display.connector,
 					minor);
 	if (IS_ERR(dp->debug)) {
 		rc = PTR_ERR(dp->debug);
@@ -1649,6 +1679,13 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
 
 	dp_priv->panel->connector = dp_display->connector;
 
+	ret = dp_hdcp_attach(dp_priv->hdcp, dp_display->connector,
+			     dp_display->bridge, dp_priv->catalog);
+	if (ret) {
+		DRM_ERROR("Failed to attach hdcp, ret=%d\n", ret);
+		return ret;
+	}
+
 	return 0;
 }
 
drivers/gpu/drm/msm/dp/dp_display.h +5 -0
index 371337d0fae26..1d64b86b0b354 100644
@@ -31,8 +31,13 @@ struct msm_dp {
 	struct dp_audio *dp_audio;
 };
 
+struct drm_atomic_state;
+
 int dp_display_set_plugged_cb(struct msm_dp *dp_display,
 		hdmi_codec_plugged_cb fn, struct device *codec_dev);
+struct dp_hdcp *dp_display_bridge_to_hdcp(struct drm_bridge *bridge);
+void dp_display_hdcp_commit(struct msm_dp *dp_display,
+			    struct drm_atomic_state *state);
 int dp_display_get_modes(struct msm_dp *dp_display);
 int dp_display_request_irq(struct msm_dp *dp_display);
 bool dp_display_check_video_test(struct msm_dp *dp_display);
drivers/gpu/drm/msm/dp/dp_drm.c +35 -4
index 275370f211159..6bf3b224ccaef 100644
@@ -7,11 +7,19 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_bridge_connector.h>
+#include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
+#include <drm/display/drm_hdcp_helper.h>
 
 #include "msm_drv.h"
 #include "msm_kms.h"
 #include "dp_drm.h"
+#include "dp_hdcp.h"
+
+struct msm_dp *msm_dp_from_bridge(struct drm_bridge *bridge)
+{
+	return to_dp_bridge(bridge)->dp_display;
+}
 
 /**
  * dp_bridge_detect - callback to determine if connector is connected
@@ -37,8 +45,8 @@ static int dp_bridge_atomic_check(struct drm_bridge *bridge,
 			    struct drm_connector_state *conn_state)
 {
 	struct msm_dp *dp;
-
-	dp = to_dp_bridge(bridge)->dp_display;
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(bridge);
+	dp = dp_bridge->dp_display;
 
 	drm_dbg_dp(dp->drm_dev, "is_connected = %s\n",
 		(dp->is_connected) ? "true" : "false");
@@ -54,8 +62,11 @@ static int dp_bridge_atomic_check(struct drm_bridge *bridge,
 	 * disabled by the hardware and thus all access to it should be forbidden.
 	 * After that this piece of code can be removed.
 	 */
-	if (bridge->ops & DRM_BRIDGE_OP_HPD)
-		return (dp->is_connected) ? 0 : -ENOTCONN;
+	if (bridge->ops & DRM_BRIDGE_OP_HPD && !dp->is_connected)
+		return -ENOTCONN;
+
+	dp_bridge->hdcp_transition =
+		drm_hdcp_has_changed(conn_state->connector, conn_state->state);
 
 	return 0;
 }
@@ -107,6 +118,25 @@ static const struct drm_bridge_funcs dp_bridge_ops = {
 	.hpd_notify   = dp_bridge_hpd_notify,
 };
 
+bool dp_drm_is_bridge_msm_dp(struct drm_bridge *bridge)
+{
+	return bridge->funcs == &dp_bridge_ops;
+}
+
+void dp_drm_atomic_commit(struct drm_bridge *bridge,
+			  struct drm_atomic_state *state)
+{
+	struct msm_dp_bridge *dp_bridge = to_dp_bridge(bridge);
+	struct msm_dp *dp_disp;
+
+	if (!dp_bridge->hdcp_transition)
+		return;
+
+	dp_disp = msm_dp_from_bridge(bridge);
+
+	dp_display_hdcp_commit(dp_disp, state);
+}
+
 struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
 			struct drm_encoder *encoder)
 {
@@ -119,6 +149,7 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
 		return ERR_PTR(-ENOMEM);
 
 	dp_bridge->dp_display = dp_display;
+	dp_bridge->hdcp_transition = false;
 
 	bridge = &dp_bridge->bridge;
 	bridge->funcs = &dp_bridge_ops;
drivers/gpu/drm/msm/dp/dp_drm.h +7 -0
index 250f7c66201f2..9b3f81cf2e139 100644
@@ -15,11 +15,18 @@
 struct msm_dp_bridge {
 	struct drm_bridge bridge;
 	struct msm_dp *dp_display;
+	bool hdcp_transition;
 };
 
 #define to_dp_bridge(x)     container_of((x), struct msm_dp_bridge, bridge)
 
+struct msm_dp *msm_dp_from_bridge(struct drm_bridge *bridge);
+
 struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder);
+bool dp_drm_is_bridge_msm_dp(struct drm_bridge *bridge);
+void dp_drm_atomic_commit(struct drm_bridge *bridge,
+			  struct drm_atomic_state *state);
+
 struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
 			struct drm_encoder *encoder);
 
drivers/gpu/drm/msm/dp/dp_hdcp.c new file +397 -0

397 lines not on this page · raw message

drivers/gpu/drm/msm/dp/dp_hdcp.h new file +33 -0
new file mode 100644
index 0000000000000..4586e45d14d26
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2021 Google, Inc.
+ *
+ * Authors:
+ * Sean Paul <[email protected]>
+ */
+
+#ifndef DP_HDCP_H_
+#define DP_HDCP_H_
+
+#include <drm/display/drm_hdcp_helper.h>
+#include <drm/drm_bridge.h>
+
+#include "dp_catalog.h"
+
+#define DP_HDCP_KEY_LEN 7
+#define DP_HDCP_NUM_KEYS 40
+
+struct dp_hdcp;
+struct dp_parser;
+struct drm_atomic_state;
+struct drm_dp_aux;
+
+struct dp_hdcp *dp_hdcp_get(struct dp_parser *parser, struct drm_dp_aux *aux);
+void dp_hdcp_put(struct dp_hdcp *hdcp);
+
+int dp_hdcp_attach(struct dp_hdcp *hdcp, struct drm_connector *connector,
+		   struct drm_bridge *bridgem, struct dp_catalog *catalog);
+int dp_hdcp_ingest_key(struct dp_hdcp *hdcp, const u8 *raw_key, int raw_len);
+void dp_hdcp_commit(struct dp_hdcp *hdcp, struct drm_atomic_state *state);
+
+#endif /* _DP_HDCP_H_ */
drivers/gpu/drm/msm/dp/dp_parser.c +14 -0
index 7032dcc8842b3..1f9bb001aebc3 100644
@@ -39,6 +39,8 @@ static int dp_parser_ctrl_res(struct dp_parser *parser)
 	struct platform_device *pdev = parser->pdev;
 	struct dp_io *io = &parser->io;
 	struct dss_io_data *dss = &io->dp_controller;
+	void __iomem *dss_hdcp_key;
+	size_t dss_hdcp_key_len;
 
 	dss->ahb.base = dp_ioremap(pdev, 0, &dss->ahb.len);
 	if (IS_ERR(dss->ahb.base))
@@ -84,6 +86,18 @@ static int dp_parser_ctrl_res(struct dp_parser *parser)
 		}
 	}
 
+	dss_hdcp_key = dp_ioremap(pdev, 5, &dss_hdcp_key_len);
+	if (!IS_ERR(dss_hdcp_key)) {
+		dss->hdcp_key.base = dss_hdcp_key;
+		dss->hdcp_key.len = dss_hdcp_key_len;
+		dss->hdcp_tz.base = dp_ioremap(pdev, 6, &dss->hdcp_tz.len);
+		if (IS_ERR(dss->hdcp_tz.base)) {
+			DRM_ERROR("unable to remap hdcp_tz region: %pe\n",
+				  dss->hdcp_tz.base);
+			return PTR_ERR(dss->hdcp_tz.base);
+		}
+	}
+
 	io->phy = devm_phy_get(&pdev->dev, "dp");
 	if (IS_ERR(io->phy))
 		return PTR_ERR(io->phy);
drivers/gpu/drm/msm/dp/dp_parser.h +4 -0
index 1f068626d445e..8b5dd3deb85ff 100644
@@ -35,6 +35,8 @@ struct dss_io_data {
 	struct dss_io_region aux;
 	struct dss_io_region link;
 	struct dss_io_region p0;
+	struct dss_io_region hdcp_key;
+	struct dss_io_region hdcp_tz;
 };
 
 static inline const char *dp_parser_pm_name(enum dp_pm_type module)
@@ -69,6 +71,8 @@ struct dp_display_data {
  * struct dp_ctrl_resource - controller's IO related data
  *
  * @dp_controller: Display Port controller mapped memory address
+ * @hdcp_key: mapped memory for HDCP key ingestion
+ * @hdcp_tz: mapped memory for HDCP TZ interaction
  * @phy_io: phy's mapped memory address
  */
 struct dp_io {
drivers/gpu/drm/msm/dp/dp_reg.h +29 -1
index 268602803d9a3..d2ac60ce7ae50 100644
@@ -10,7 +10,8 @@
 #define REG_DP_HW_VERSION			(0x00000000)
 
 #define REG_DP_SW_RESET				(0x00000010)
-#define DP_SW_RESET				(0x00000001)
+#define DP_SW_RESET 				BIT(0)
+#define DP_HDCP_SW_RESET 			BIT(1)
 
 #define REG_DP_PHY_CTRL				(0x00000014)
 #define DP_PHY_CTRL_SW_RESET_PLL		(0x00000001)
@@ -283,19 +284,46 @@
 /* DP HDCP 1.3 registers */
 #define DP_HDCP_CTRL                                   (0x0A0)
 #define DP_HDCP_STATUS                                 (0x0A4)
+#define DP_HDCP_KEY_STATUS 	    					   GENMASK(18, 16)
+#define DP_HDCP_KEY_STATUS_NO_KEYS 					   0
+#define DP_HDCP_KEY_STATUS_NOT_CHECKED 				   1
+#define DP_HDCP_KEY_STATUS_CHECKING 				   2
+#define DP_HDCP_KEY_STATUS_VALID 					   3
+#define DP_HDCP_KEY_STATUS_INVALID_AKSV 			   4
+#define DP_HDCP_KEY_STATUS_BAD_CHECKSUM 			   5
+#define DP_HDCP_KEY_STATUS_PROD_AKSV 				   6
+#define DP_HDCP_KEY_STATUS_RESV 					   7
+#define DP_HDCP_R0_READY 							   BIT(14)
+#define DP_HDCP_SHA_V_MATCH 						   BIT(13)
+#define DP_HDCP_RI_MATCH 							   BIT(12)
+#define DP_HDCP_AN_MSB_READY 						   BIT(9)
+#define DP_HDCP_AN_LSB_READY 						   BIT(8)
+#define DP_HDCP_AN_READY_MASK 						   (DP_HDCP_AN_MSB_READY | DP_HDCP_AN_LSB_READY)
+#define DP_HDCP_AUTH_FAIL_INFO 						   GENMASK(7, 4)
+#define DP_HDCP_AUTH_FAIL_INVALID_AKSV 				   3
+#define DP_HDCP_AUTH_FAIL_INVALID_BKSV 				   4
+#define DP_HDCP_AUTH_FAIL_RI_MISMATCH 				   5
+#define DP_HDCP_AUTH_FAIL 							   BIT(2)
+#define DP_HDCP_AUTH_SUCCESS 						   BIT(0)
 #define DP_HDCP_SW_UPPER_AKSV                          (0x098)
 #define DP_HDCP_SW_LOWER_AKSV                          (0x09C)
 #define DP_HDCP_ENTROPY_CTRL0                          (0x350)
 #define DP_HDCP_ENTROPY_CTRL1                          (0x35C)
 #define DP_HDCP_SHA_STATUS                             (0x0C8)
+#define DP_HDCP_SHA_COMP_DONE 						   BIT(4)
+#define DP_HDCP_SHA_DONE 							   BIT(0)
 #define DP_HDCP_RCVPORT_DATA2_0                        (0x0B0)
 #define DP_HDCP_RCVPORT_DATA3                          (0x0A4)
 #define DP_HDCP_RCVPORT_DATA4                          (0x0A8)
 #define DP_HDCP_RCVPORT_DATA5                          (0x0C0)
 #define DP_HDCP_RCVPORT_DATA6                          (0x0C4)
+#define DP_HDCP_RCVPORT_DATA7 						   (0x0C8)
 
 #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_CTRL           (0x024)
+#define DP_HDCP_SHA_CTRL_RESET 						   BIT(0)
 #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_DATA           (0x028)
+#define DP_HDCP_SHA_DATA_MASK 						   GENMASK(23, 16)
+#define DP_HDCP_SHA_DATA_DONE 						   BIT(0)
 #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA0      (0x004)
 #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA1      (0x008)
 #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA7      (0x00C)
drivers/gpu/drm/msm/msm_atomic.c +19 -0
index 1686fbb611fd7..2168188d6379c 100644
@@ -5,8 +5,11 @@
  */
 
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_device.h>
 #include <drm/drm_vblank.h>
 
+#include "dp/dp_drm.h"
 #include "msm_atomic_trace.h"
 #include "msm_drv.h"
 #include "msm_gem.h"
@@ -179,6 +182,20 @@ static unsigned get_crtc_mask(struct drm_atomic_state *state)
 	return mask;
 }
 
+static void msm_atomic_commit_connectors(struct drm_atomic_state *state)
+{
+	struct drm_device *dev = state->dev;
+	struct msm_drm_private *priv = dev->dev_private;
+	int i;
+
+	for (i = 0; i < priv->num_bridges; ++i) {
+		struct drm_bridge *bridge = priv->bridges[i];
+		if (dp_drm_is_bridge_msm_dp(bridge)) {
+			dp_drm_atomic_commit(bridge, state);
+		}
+	}
+}
+
 void msm_atomic_commit_tail(struct drm_atomic_state *state)
 {
 	struct drm_device *dev = state->dev;
@@ -215,6 +232,8 @@ void msm_atomic_commit_tail(struct drm_atomic_state *state)
 	drm_atomic_helper_commit_planes(dev, state, 0);
 	drm_atomic_helper_commit_modeset_enables(dev, state);
 
+	msm_atomic_commit_connectors(state);
+
 	if (async) {
 		struct msm_pending_timer *timer =
 			&kms->pending_timers[drm_crtc_index(async_crtc)];
Signature · 1 line
2.40.0.348.gf938b09366-goog
#19 lore raw

Re: [Intel-gfx] [PATCH v8 10/10] drm/msm: Implement HDCP 1.x using the new drm HDCP helpers

I love your patch! Yet something to improve:

Hi Mark,

I love your patch! Yet something to improve:

[auto build test ERROR on drm-intel/for-linux-next-fixes]
[also build test ERROR on linus/master v6.3-rc4]
[cannot apply to drm-misc/drm-misc-next drm-intel/for-linux-next drm/drm-next next-20230331] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mark-Yacoub/drm-hdcp-Add-drm_hdcp_atomic_check/20230401-061425
base:   git://anongit.freedesktop.org/drm-intel for-linux-next-fixes
patch link:    https://lore.kernel.org/r/20230331221213.1691997-11-markyacoub%40google.com
patch subject: [Intel-gfx] [PATCH v8 10/10] drm/msm: Implement HDCP 1.x using the new drm HDCP helpers
config: arc-randconfig-r043-20230329 (https://download.01.org/0day-ci/archive/20230401/[email protected]/config)
compiler: arc-elf-gcc (GCC) 12.1.0

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/697c762c590d862f4f6ed4a8cac97ac2de815f73
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Mark-Yacoub/drm-hdcp-Add-drm_hdcp_atomic_check/20230401-061425 git checkout 697c762c590d862f4f6ed4a8cac97ac2de815f73
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arc olddefconfig COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

arc-elf-ld: drivers/gpu/drm/msm/msm_atomic.o: in function `msm_atomic_commit_tail':

>> drivers/gpu/drm/msm/msm_atomic.c:193: undefined reference to `dp_drm_is_bridge_msm_dp'
>> arc-elf-ld: drivers/gpu/drm/msm/msm_atomic.c:193: undefined reference to `dp_drm_is_bridge_msm_dp'
>> arc-elf-ld: drivers/gpu/drm/msm/msm_atomic.c:194: undefined reference to `dp_drm_atomic_commit'
>> arc-elf-ld: drivers/gpu/drm/msm/msm_atomic.c:194: undefined reference to `dp_drm_atomic_commit'
   arc-elf-ld: drivers/gpu/drm/msm/dp/dp_debug.o: in function `dp_hdcp_key_write':
>> drivers/gpu/drm/msm/dp/dp_debug.c:219: undefined reference to `dp_hdcp_ingest_key'
>> arc-elf-ld: drivers/gpu/drm/msm/dp/dp_debug.c:219: undefined reference to `dp_hdcp_ingest_key'

vim +193 drivers/gpu/drm/msm/msm_atomic.c

184
185 static void msm_atomic_commit_connectors(struct drm_atomic_state *state)
186 {
187 struct drm_device *dev = state->dev;
188 struct msm_drm_private *priv = dev->dev_private;
189 int i;
190
191 for (i = 0; i < priv->num_bridges; ++i) {
192 struct drm_bridge *bridge = priv->bridges[i];

 > 193			if (dp_drm_is_bridge_msm_dp(bridge)) {
 > 194				dp_drm_atomic_commit(bridge, state);

195 }
196 }
197 }
198

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
#20 lore raw

[Intel-gfx] ✗ Fi.CI.BUILD: failure for drm/hdcp: Pull HDCP auth/exchange/check into helpers (rev8)

In reply to Mark Yacoub
== Series Details ==

== Series Details ==

Series: drm/hdcp: Pull HDCP auth/exchange/check into helpers (rev8)
URL : https://patchwork.freedesktop.org/series/94712/
State : failure

== Summary ==

Error: patch https://patchwork.freedesktop.org/api/1.0/series/94712/revisions/8/mbox/ not applied
Applying: drm/hdcp: Add drm_hdcp_atomic_check()
Applying: drm/hdcp: Avoid changing crtc state in hdcp atomic check
Applying: drm/hdcp: Update property value on content type and user changes
Applying: drm/hdcp: Expand HDCP helper library for enable/disable/check
Applying: drm/i915/hdcp: Consolidate HDCP setup/state cache
Using index info to reconstruct a base tree...
M drivers/gpu/drm/i915/display/intel_hdcp.c
Falling back to patching base and 3-way merge...
Auto-merging drivers/gpu/drm/i915/display/intel_hdcp.c
CONFLICT (content): Merge conflict in drivers/gpu/drm/i915/display/intel_hdcp.c
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0005 drm/i915/hdcp: Consolidate HDCP setup/state cache
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Build failed, no error log produced