[PATCH 32/43] drm/i915: Implement HDCP2.2 link integrity check

Ramalingam C
2018-02-14T14:21:02Z

Implements the link integrity check once in 500mSec.

Once encryption is enabled, an ongoing Link Integrity Check is
performed by the HDCP Receiver to check that cipher synchronization
is maintained between the HDCP Transmitter and the HDCP Receiver.

On the detection of synchronization lost, the HDCP Receiver must assert
the corresponding bits of the RxStatus register. The Transmitter polls
the RxStatus register and it may initiate re-authentication.

Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
---
drivers/gpu/drm/i915/intel_hdcp.c | 80 ++++++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 8 ++++
2 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 8bf30513713a..076a0b0d2ae9 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -24,6 +24,8 @@

static int _intel_hdcp2_enable(struct intel_hdcp *hdcp);
static int _intel_hdcp2_disable(struct intel_hdcp *hdcp);
+static void intel_hdcp2_check_work(struct work_struct *work);
+static int intel_hdcp2_check_link(struct intel_hdcp *hdcp);

static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
@@ -1453,6 +1455,82 @@ static int _intel_hdcp2_enable(struct intel_hdcp *hdcp)

hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
schedule_work(&hdcp->hdcp_prop_work);
-
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
return 0;
}
+
+static void intel_hdcp2_check_work(struct work_struct *work)
+{
+ struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
+ struct intel_hdcp,
+ hdcp2_check_work);
+
+ if (!intel_hdcp2_check_link(hdcp))
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+}
+
+static int intel_hdcp2_check_link(struct intel_hdcp *hdcp)
+{
+ struct intel_digital_port *intel_dig_port =
+ conn_to_dig_port(hdcp->connector);
+ struct drm_i915_private *dev_priv = to_i915(hdcp->connector->base.dev);
+ enum port port = hdcp->connector->encoder->port;
+ int ret = 0;
+
+ if (!hdcp->hdcp_shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->hdcp_mutex);
+
+ if (hdcp->hdcp_value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) {
+ DRM_ERROR("HDCP check failed: link is not encrypted, %x\n",
+ I915_READ(HDCP2_STATUS_DDI(port)));
+ ret = -ENXIO;
+ hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->hdcp_prop_work);
+ goto out;
+ }
+
+ ret = hdcp->hdcp2_shim->check_link(intel_dig_port);
+ if (!ret) {
+ if (hdcp->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->hdcp_prop_work);
+ }
+ goto out;
+ }
+
+ DRM_INFO("[%s:%d] HDCP2.2 link failed, retrying authentication\n",
+ hdcp->connector->base.name, hdcp->connector->base.base.id);
+
+ ret = _intel_hdcp2_disable(hdcp);
+ if (ret) {
+ DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
+ hdcp->connector->base.name,
+ hdcp->connector->base.base.id, ret);
+
+ hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->hdcp_prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp2_enable(hdcp);
+ if (ret) {
+ DRM_ERROR("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
+ hdcp->connector->base.name,
+ hdcp->connector->base.base.id, ret);
+
+ hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->hdcp_prop_work);
+ goto out;
+ }
+
+out:
+ mutex_unlock(&hdcp->hdcp_mutex);
+ return ret;
+}
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index c5f36b1a004b..8c1fca59ea35 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -11,6 +11,14 @@

/* 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
+
+enum check_link_response {
+ DRM_HDCP_LINK_PROTECTED = 0,
+ DRM_HDCP_TOPOLOGY_CHANGE,
+ DRM_HDCP_LINK_INTEGRITY_FAILURE,
+ DRM_HDCP_REAUTH_REQUEST
+};

/* Shared lengths/masks between HDMI/DVI/DisplayPort */
#define DRM_HDCP_AN_LEN 8