diff mbox

[v6,14/35] drm/i915: Implement HDCP2.2 link integrity check

Message ID 1531538117-1606-15-git-send-email-ramalingam.c@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ramalingam C July 14, 2018, 3:14 a.m. UTC
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.

v2:
  Rebased.
v3:
  No Changes.
v4:
  enum check_link_response is used check the link status [Uma]
v5:
  Rebased as part of patch reordering.
v6:
  Required members of intel_hdcp is defined [Sean Paul]

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

Comments

Shankar, Uma Aug. 1, 2018, 10:45 a.m. UTC | #1
>-----Original Message-----
>From: C, Ramalingam
>Sent: Saturday, July 14, 2018 8:45 AM
>To: intel-gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org;
>daniel@ffwll.ch; seanpaul@chromium.org; Winkler, Tomas
><tomas.winkler@intel.com>; Usyskin, Alexander <alexander.usyskin@intel.com>;
>Shankar, Uma <uma.shankar@intel.com>
>Cc: Sharma, Shashank <shashank.sharma@intel.com>; C, Ramalingam
><ramalingam.c@intel.com>
>Subject: [PATCH v6 14/35] drm/i915: Implement HDCP2.2 link integrity check
>
>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.
>
>v2:
>  Rebased.
>v3:
>  No Changes.
>v4:
>  enum check_link_response is used check the link status [Uma]
>v5:
>  Rebased as part of patch reordering.
>v6:
>  Required members of intel_hdcp is defined [Sean Paul]
>
>Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
>---
> drivers/gpu/drm/i915/intel_drv.h  |  5 +++  drivers/gpu/drm/i915/intel_hdcp.c |
>84 ++++++++++++++++++++++++++++++++++++++-
> include/drm/drm_hdcp.h            |  8 ++++
> 3 files changed, 96 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>index ed429a6fb9ab..ce4679a0b530 100644
>--- a/drivers/gpu/drm/i915/intel_drv.h
>+++ b/drivers/gpu/drm/i915/intel_drv.h
>@@ -400,6 +400,9 @@ struct intel_hdcp_shim {
> 	 */
> 	int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
> 				  void *buf, size_t size);
>+
>+	/* HDCP2.2 Link Integrity Check */
>+	int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
> };
>
> struct intel_hdcp {
>@@ -442,6 +445,8 @@ struct intel_hdcp {
> 	 * over re-Auth has to be triggered.
> 	 */
> 	uint32_t seq_num_m;
>+
>+	struct delayed_work hdcp2_check_work;
> };
>
> struct intel_connector {
>diff --git a/drivers/gpu/drm/i915/intel_hdcp.c
>b/drivers/gpu/drm/i915/intel_hdcp.c
>index a08d80c710d7..0a14d8757346 100644
>--- a/drivers/gpu/drm/i915/intel_hdcp.c
>+++ b/drivers/gpu/drm/i915/intel_hdcp.c
>@@ -25,6 +25,8 @@
> static int intel_hdcp2_init(struct intel_connector *connector);  static int
>_intel_hdcp2_enable(struct intel_connector *connector);  static int
>_intel_hdcp2_disable(struct intel_connector *connector);
>+static void intel_hdcp2_check_work(struct work_struct *work); static
>+int intel_hdcp2_check_link(struct intel_connector *connector);
> static
> int intel_hdcp_read_valid_bksv(struct intel_digital_port *intel_dig_port,
> 			       const struct intel_hdcp_shim *shim, u8 *bksv); @@ -
>823,6 +825,7 @@ int intel_hdcp_init(struct intel_connector *connector,
> 	mutex_init(&hdcp->mutex);
> 	INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
> 	INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
>+	INIT_DELAYED_WORK(&hdcp->hdcp2_check_work,
>intel_hdcp2_check_work);
>
> 	if (hdcp2_supported)
> 		intel_hdcp2_init(connector);
>@@ -844,8 +847,12 @@ int intel_hdcp_enable(struct intel_connector
>*connector)
> 	 * 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.
> 	 */
>-	if (intel_hdcp2_capable(connector))
>+	if (intel_hdcp2_capable(connector)) {
> 		ret = _intel_hdcp2_enable(connector);
>+		if (!ret)
>+			schedule_delayed_work(&hdcp->hdcp2_check_work,
>+					      DRM_HDCP2_CHECK_PERIOD_MS);
>+	}
>
> 	/* When HDCP2.2 fails, HDCP1.4 will be attempted */
> 	if (ret && intel_hdcp_capable(connector)) { @@ -1724,3 +1731,78 @@
>static int intel_hdcp2_init(struct intel_connector *connector)
> exit:
> 	return ret;
> }
>+
>+static int intel_hdcp2_check_link(struct intel_connector *connector) {
>+	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
>+	struct intel_hdcp *hdcp = &connector->hdcp;
>+	enum port port = connector->encoder->port;
>+	int ret = 0;
>+
>+	if (!hdcp->shim)
>+		return -ENOENT;
>+
>+	mutex_lock(&hdcp->mutex);
>+
>+	if (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->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>+		schedule_work(&hdcp->prop_work);
>+		goto out;
>+	}
>+
>+	ret = hdcp->shim->check_2_2_link(intel_dig_port);
>+	if (ret == DRM_HDCP_LINK_PROTECTED) {
>+		if (hdcp->value !=
>DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
>+			hdcp->value =
>DRM_MODE_CONTENT_PROTECTION_ENABLED;
>+			schedule_work(&hdcp->prop_work);
>+		}
>+		goto out;
>+	}
>+
>+	DRM_ERROR("[%s:%d] HDCP2.2 link failed, retrying auth\n",
>+		  connector->base.name, connector->base.base.id);
>+
>+	ret = _intel_hdcp2_disable(connector);
>+	if (ret) {
>+		DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
>+			  connector->base.name, connector->base.base.id, ret);
>+
>+		hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>+		schedule_work(&hdcp->prop_work);
>+		goto out;
>+	}
>+
>+	ret = _intel_hdcp2_enable(connector);
>+	if (ret) {
>+		DRM_ERROR("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
>+			  connector->base.name, connector->base.base.id, ret);
>+
>+		hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>+		schedule_work(&hdcp->prop_work);
>+		goto out;
>+	}
>+
>+out:
>+	mutex_unlock(&hdcp->mutex);
>+
>+	return ret;
>+}
>+
>+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);
>+	struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
>+
>+	if (!intel_hdcp2_check_link(connector))
>+		schedule_delayed_work(&hdcp->hdcp2_check_work,
>+				      DRM_HDCP2_CHECK_PERIOD_MS);
>+}
>diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index
>c01cdb1d848d..c4aff185076d 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

I feel we can optimize this frequency of link integrity check. As per spec, we should
check once every 1 sec, but our frequency here seems to be just double of it.

Other than that looks ok to me.
Reviewed-by: Uma Shankar <uma.shankar@intel.com>

>+
>+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
>--
>2.7.4
Ramalingam C Aug. 30, 2018, 6:57 a.m. UTC | #2
On Wednesday 01 August 2018 04:15 PM, Shankar, Uma wrote:
>
>> -----Original Message-----
>> From: C, Ramalingam
>> Sent: Saturday, July 14, 2018 8:45 AM
>> To: intel-gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org;
>> daniel@ffwll.ch; seanpaul@chromium.org; Winkler, Tomas
>> <tomas.winkler@intel.com>; Usyskin, Alexander <alexander.usyskin@intel.com>;
>> Shankar, Uma <uma.shankar@intel.com>
>> Cc: Sharma, Shashank <shashank.sharma@intel.com>; C, Ramalingam
>> <ramalingam.c@intel.com>
>> Subject: [PATCH v6 14/35] drm/i915: Implement HDCP2.2 link integrity check
>>
>> 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.
>>
>> v2:
>>   Rebased.
>> v3:
>>   No Changes.
>> v4:
>>   enum check_link_response is used check the link status [Uma]
>> v5:
>>   Rebased as part of patch reordering.
>> v6:
>>   Required members of intel_hdcp is defined [Sean Paul]
>>
>> Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
>> ---
>> drivers/gpu/drm/i915/intel_drv.h  |  5 +++  drivers/gpu/drm/i915/intel_hdcp.c |
>> 84 ++++++++++++++++++++++++++++++++++++++-
>> include/drm/drm_hdcp.h            |  8 ++++
>> 3 files changed, 96 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>> index ed429a6fb9ab..ce4679a0b530 100644
>> --- a/drivers/gpu/drm/i915/intel_drv.h
>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>> @@ -400,6 +400,9 @@ struct intel_hdcp_shim {
>> 	 */
>> 	int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
>> 				  void *buf, size_t size);
>> +
>> +	/* HDCP2.2 Link Integrity Check */
>> +	int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
>> };
>>
>> struct intel_hdcp {
>> @@ -442,6 +445,8 @@ struct intel_hdcp {
>> 	 * over re-Auth has to be triggered.
>> 	 */
>> 	uint32_t seq_num_m;
>> +
>> +	struct delayed_work hdcp2_check_work;
>> };
>>
>> struct intel_connector {
>> diff --git a/drivers/gpu/drm/i915/intel_hdcp.c
>> b/drivers/gpu/drm/i915/intel_hdcp.c
>> index a08d80c710d7..0a14d8757346 100644
>> --- a/drivers/gpu/drm/i915/intel_hdcp.c
>> +++ b/drivers/gpu/drm/i915/intel_hdcp.c
>> @@ -25,6 +25,8 @@
>> static int intel_hdcp2_init(struct intel_connector *connector);  static int
>> _intel_hdcp2_enable(struct intel_connector *connector);  static int
>> _intel_hdcp2_disable(struct intel_connector *connector);
>> +static void intel_hdcp2_check_work(struct work_struct *work); static
>> +int intel_hdcp2_check_link(struct intel_connector *connector);
>> static
>> int intel_hdcp_read_valid_bksv(struct intel_digital_port *intel_dig_port,
>> 			       const struct intel_hdcp_shim *shim, u8 *bksv); @@ -
>> 823,6 +825,7 @@ int intel_hdcp_init(struct intel_connector *connector,
>> 	mutex_init(&hdcp->mutex);
>> 	INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
>> 	INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
>> +	INIT_DELAYED_WORK(&hdcp->hdcp2_check_work,
>> intel_hdcp2_check_work);
>>
>> 	if (hdcp2_supported)
>> 		intel_hdcp2_init(connector);
>> @@ -844,8 +847,12 @@ int intel_hdcp_enable(struct intel_connector
>> *connector)
>> 	 * 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.
>> 	 */
>> -	if (intel_hdcp2_capable(connector))
>> +	if (intel_hdcp2_capable(connector)) {
>> 		ret = _intel_hdcp2_enable(connector);
>> +		if (!ret)
>> +			schedule_delayed_work(&hdcp->hdcp2_check_work,
>> +					      DRM_HDCP2_CHECK_PERIOD_MS);
>> +	}
>>
>> 	/* When HDCP2.2 fails, HDCP1.4 will be attempted */
>> 	if (ret && intel_hdcp_capable(connector)) { @@ -1724,3 +1731,78 @@
>> static int intel_hdcp2_init(struct intel_connector *connector)
>> exit:
>> 	return ret;
>> }
>> +
>> +static int intel_hdcp2_check_link(struct intel_connector *connector) {
>> +	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
>> +	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
>> +	struct intel_hdcp *hdcp = &connector->hdcp;
>> +	enum port port = connector->encoder->port;
>> +	int ret = 0;
>> +
>> +	if (!hdcp->shim)
>> +		return -ENOENT;
>> +
>> +	mutex_lock(&hdcp->mutex);
>> +
>> +	if (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->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> +		schedule_work(&hdcp->prop_work);
>> +		goto out;
>> +	}
>> +
>> +	ret = hdcp->shim->check_2_2_link(intel_dig_port);
>> +	if (ret == DRM_HDCP_LINK_PROTECTED) {
>> +		if (hdcp->value !=
>> DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
>> +			hdcp->value =
>> DRM_MODE_CONTENT_PROTECTION_ENABLED;
>> +			schedule_work(&hdcp->prop_work);
>> +		}
>> +		goto out;
>> +	}
>> +
>> +	DRM_ERROR("[%s:%d] HDCP2.2 link failed, retrying auth\n",
>> +		  connector->base.name, connector->base.base.id);
>> +
>> +	ret = _intel_hdcp2_disable(connector);
>> +	if (ret) {
>> +		DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
>> +			  connector->base.name, connector->base.base.id, ret);
>> +
>> +		hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> +		schedule_work(&hdcp->prop_work);
>> +		goto out;
>> +	}
>> +
>> +	ret = _intel_hdcp2_enable(connector);
>> +	if (ret) {
>> +		DRM_ERROR("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
>> +			  connector->base.name, connector->base.base.id, ret);
>> +
>> +		hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
>> +		schedule_work(&hdcp->prop_work);
>> +		goto out;
>> +	}
>> +
>> +out:
>> +	mutex_unlock(&hdcp->mutex);
>> +
>> +	return ret;
>> +}
>> +
>> +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);
>> +	struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
>> +
>> +	if (!intel_hdcp2_check_link(connector))
>> +		schedule_delayed_work(&hdcp->hdcp2_check_work,
>> +				      DRM_HDCP2_CHECK_PERIOD_MS);
>> +}
>> diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index
>> c01cdb1d848d..c4aff185076d 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
> I feel we can optimize this frequency of link integrity check. As per spec, we should
> check once every 1 sec, but our frequency here seems to be just double of it.
Uma,

Spec says Link integrity check is required at least once in 1Sec. So we 
decided
to implement twice per second. If needed we could optimize it as and 
when it requires.

--Ram
>
> Other than that looks ok to me.
> Reviewed-by: Uma Shankar <uma.shankar@intel.com>
>
>> +
>> +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
>> --
>> 2.7.4
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index ed429a6fb9ab..ce4679a0b530 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -400,6 +400,9 @@  struct intel_hdcp_shim {
 	 */
 	int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
 				  void *buf, size_t size);
+
+	/* HDCP2.2 Link Integrity Check */
+	int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
 };
 
 struct intel_hdcp {
@@ -442,6 +445,8 @@  struct intel_hdcp {
 	 * over re-Auth has to be triggered.
 	 */
 	uint32_t seq_num_m;
+
+	struct delayed_work hdcp2_check_work;
 };
 
 struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index a08d80c710d7..0a14d8757346 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -25,6 +25,8 @@ 
 static int intel_hdcp2_init(struct intel_connector *connector);
 static int _intel_hdcp2_enable(struct intel_connector *connector);
 static int _intel_hdcp2_disable(struct intel_connector *connector);
+static void intel_hdcp2_check_work(struct work_struct *work);
+static int intel_hdcp2_check_link(struct intel_connector *connector);
 static
 int intel_hdcp_read_valid_bksv(struct intel_digital_port *intel_dig_port,
 			       const struct intel_hdcp_shim *shim, u8 *bksv);
@@ -823,6 +825,7 @@  int intel_hdcp_init(struct intel_connector *connector,
 	mutex_init(&hdcp->mutex);
 	INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
 	INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
+	INIT_DELAYED_WORK(&hdcp->hdcp2_check_work, intel_hdcp2_check_work);
 
 	if (hdcp2_supported)
 		intel_hdcp2_init(connector);
@@ -844,8 +847,12 @@  int intel_hdcp_enable(struct intel_connector *connector)
 	 * 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.
 	 */
-	if (intel_hdcp2_capable(connector))
+	if (intel_hdcp2_capable(connector)) {
 		ret = _intel_hdcp2_enable(connector);
+		if (!ret)
+			schedule_delayed_work(&hdcp->hdcp2_check_work,
+					      DRM_HDCP2_CHECK_PERIOD_MS);
+	}
 
 	/* When HDCP2.2 fails, HDCP1.4 will be attempted */
 	if (ret && intel_hdcp_capable(connector)) {
@@ -1724,3 +1731,78 @@  static int intel_hdcp2_init(struct intel_connector *connector)
 exit:
 	return ret;
 }
+
+static int intel_hdcp2_check_link(struct intel_connector *connector)
+{
+	struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+	struct intel_hdcp *hdcp = &connector->hdcp;
+	enum port port = connector->encoder->port;
+	int ret = 0;
+
+	if (!hdcp->shim)
+		return -ENOENT;
+
+	mutex_lock(&hdcp->mutex);
+
+	if (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->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&hdcp->prop_work);
+		goto out;
+	}
+
+	ret = hdcp->shim->check_2_2_link(intel_dig_port);
+	if (ret == DRM_HDCP_LINK_PROTECTED) {
+		if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+			hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+			schedule_work(&hdcp->prop_work);
+		}
+		goto out;
+	}
+
+	DRM_ERROR("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+		  connector->base.name, connector->base.base.id);
+
+	ret = _intel_hdcp2_disable(connector);
+	if (ret) {
+		DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
+			  connector->base.name, connector->base.base.id, ret);
+
+		hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&hdcp->prop_work);
+		goto out;
+	}
+
+	ret = _intel_hdcp2_enable(connector);
+	if (ret) {
+		DRM_ERROR("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
+			  connector->base.name, connector->base.base.id, ret);
+
+		hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		schedule_work(&hdcp->prop_work);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&hdcp->mutex);
+
+	return ret;
+}
+
+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);
+	struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+
+	if (!intel_hdcp2_check_link(connector))
+		schedule_delayed_work(&hdcp->hdcp2_check_work,
+				      DRM_HDCP2_CHECK_PERIOD_MS);
+}
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index c01cdb1d848d..c4aff185076d 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