[Intel-gfx] [PATCH v7 12/17] drm/i915: Factor out HDCP shim functions from dp for use by dp_mst

C, Ramalingam ramalingam.c at intel.com
Thu Jul 9 10:51:48 UTC 2020


> -----Original Message-----
> From: Sean Paul <sean at poorly.run>
> Sent: Tuesday, June 23, 2020 9:29 PM
> To: dri-devel at lists.freedesktop.org; intel-gfx at lists.freedesktop.org
> Cc: Li, Juston <juston.li at intel.com>; C, Ramalingam
> <ramalingam.c at intel.com>; ville.syrjala at linux.intel.com;
> jani.nikula at linux.intel.com; joonas.lahtinen at linux.intel.com; Vivi, Rodrigo
> <rodrigo.vivi at intel.com>; daniel.vetter at ffwll.ch; Sean Paul
> <seanpaul at chromium.org>
> Subject: [PATCH v7 12/17] drm/i915: Factor out HDCP shim functions from dp
> for use by dp_mst
> 
> From: Sean Paul <seanpaul at chromium.org>
> 
> These functions are all the same for dp and dp_mst, so move them into a
> dedicated file for both sst and mst to use.
> 
> Signed-off-by: Sean Paul <seanpaul at chromium.org>
Reviewed-by: Ramalingam C <ramalingam.c at intel.com>

> Link: https://patchwork.freedesktop.org/patch/msgid/20191203173638.94919-
> 11-sean at poorly.run #v1
> Link:
> https://patchwork.freedesktop.org/patch/msgid/20191212190230.188505-12-
> sean at poorly.run #v2
> Link:
> https://patchwork.freedesktop.org/patch/msgid/20200117193103.156821-12-
> sean at poorly.run #v3
> Link:
> https://patchwork.freedesktop.org/patch/msgid/20200218220242.107265-12-
> sean at poorly.run #v4
> Link:
> https://patchwork.freedesktop.org/patch/msgid/20200305201236.152307-12-
> sean at poorly.run #v5
> Link: https://patchwork.freedesktop.org/patch/msgid/20200429195502.39919-
> 12-sean at poorly.run #v6
> 
> Changes in v2:
> -None
> Changes in v3:
> -Created intel_dp_hdcp.c for the shared functions to live (Ville) Changes in v4:
> -Rebased on new drm logging change
> Changes in v5:
> -None
> Changes in v6:
> -None
> Changes in v7:
> -Rebased patch
> ---
>  drivers/gpu/drm/i915/Makefile                |   1 +
>  drivers/gpu/drm/i915/display/intel_dp.c      | 606 +-----------------
>  drivers/gpu/drm/i915/display/intel_dp.h      |   3 +
>  drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 636 +++++++++++++++++++
>  4 files changed, 641 insertions(+), 605 deletions(-)  create mode 100644
> drivers/gpu/drm/i915/display/intel_dp_hdcp.c
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 41a27fd5dbc7..cba4ddb95ab1 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -233,6 +233,7 @@ i915-y += \
>  	display/intel_ddi.o \
>  	display/intel_dp.o \
>  	display/intel_dp_aux_backlight.o \
> +	display/intel_dp_hdcp.o \
>  	display/intel_dp_link_training.o \
>  	display/intel_dp_mst.o \
>  	display/intel_dsi.o \
> diff --git a/drivers/gpu/drm/i915/display/intel_dp.c
> b/drivers/gpu/drm/i915/display/intel_dp.c
> index d98e45a09c28..78ce5e41d559 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp.c
> +++ b/drivers/gpu/drm/i915/display/intel_dp.c
> @@ -38,7 +38,6 @@
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_dp_helper.h>
>  #include <drm/drm_edid.h>
> -#include <drm/drm_hdcp.h>
>  #include <drm/drm_probe_helper.h>
> 
>  #include "i915_debugfs.h"
> @@ -6396,609 +6395,6 @@ void intel_dp_encoder_suspend(struct
> intel_encoder *intel_encoder)
>  		edp_panel_vdd_off_sync(intel_dp);
>  }
> 
> -static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int timeout)
> -{
> -	long ret;
> -
> -#define C (hdcp->cp_irq_count_cached != atomic_read(&hdcp->cp_irq_count))
> -	ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
> -					       msecs_to_jiffies(timeout));
> -
> -	if (!ret)
> -		DRM_DEBUG_KMS("Timedout at waiting for CP_IRQ\n");
> -}
> -
> -static
> -int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
> -				u8 *an)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	u8 aksv[DRM_HDCP_KSV_LEN] = {};
> -	ssize_t dpcd_ret;
> -
> -	dpcd_ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
> DP_AUX_HDCP_AN,
> -				     an, 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",
> -			    dpcd_ret);
> -		return dpcd_ret >= 0 ? -EIO : dpcd_ret;
> -	}
> -
> -	/*
> -	 * Since Aksv is Oh-So-Secret, we can't access it in software. So we
> -	 * send an empty buffer of the correct length through the DP helpers. On
> -	 * the other side, in the transfer hook, we'll generate a flag based on
> -	 * the destination address which will tickle the hardware to output the
> -	 * Aksv on our behalf after the header is sent.
> -	 */
> -	dpcd_ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
> DP_AUX_HDCP_AKSV,
> -				     aksv, DRM_HDCP_KSV_LEN);
> -	if (dpcd_ret != DRM_HDCP_KSV_LEN) {
> -		drm_dbg_kms(&i915->drm,
> -			    "Failed to write Aksv over DP/AUX (%zd)\n",
> -			    dpcd_ret);
> -		return dpcd_ret >= 0 ? -EIO : dpcd_ret;
> -	}
> -	return 0;
> -}
> -
> -static int intel_dp_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
> -				   u8 *bksv)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	ssize_t ret;
> -
> -	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> -				      u8 *bstatus)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_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(&intel_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 *intel_dig_port,
> -			     u8 *bcaps)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	ssize_t ret;
> -
> -	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> -				   bool *repeater_present)
> -{
> -	ssize_t ret;
> -	u8 bcaps;
> -
> -	ret = intel_dp_hdcp_read_bcaps(intel_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 *intel_dig_port,
> -				u8 *ri_prime)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	ssize_t ret;
> -
> -	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> DP_AUX_HDCP_RI_PRIME,
> -			       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",
> -			    ret);
> -		return ret >= 0 ? -EIO : ret;
> -	}
> -	return 0;
> -}
> -
> -static
> -int intel_dp_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
> -				 bool *ksv_ready)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	ssize_t ret;
> -	u8 bstatus;
> -
> -	ret = drm_dp_dpcd_read(&intel_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 ret >= 0 ? -EIO : ret;
> -	}
> -	*ksv_ready = bstatus & DP_BSTATUS_READY;
> -	return 0;
> -}
> -
> -static
> -int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
> -				int num_downstream, u8 *ksv_fifo)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_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;
> -		ret = drm_dp_dpcd_read(&intel_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",
> -				    i, ret);
> -			return ret >= 0 ? -EIO : ret;
> -		}
> -	}
> -	return 0;
> -}
> -
> -static
> -int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
> -				    int i, u32 *part)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	ssize_t ret;
> -
> -	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
> -		return -EINVAL;
> -
> -	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> -				    enum transcoder cpu_transcoder,
> -				    bool enable)
> -{
> -	/* Not used for single stream DisplayPort setups */
> -	return 0;
> -}
> -
> -static
> -bool intel_dp_hdcp_check_link(struct intel_digital_port *intel_dig_port) -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	ssize_t ret;
> -	u8 bstatus;
> -
> -	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> -			  bool *hdcp_capable)
> -{
> -	ssize_t ret;
> -	u8 bcaps;
> -
> -	ret = intel_dp_hdcp_read_bcaps(intel_dig_port, &bcaps);
> -	if (ret)
> -		return ret;
> -
> -	*hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE;
> -	return 0;
> -}
> -
> -struct hdcp2_dp_errata_stream_type {
> -	u8	msg_id;
> -	u8	stream_type;
> -} __packed;
> -
> -struct hdcp2_dp_msg_data {
> -	u8 msg_id;
> -	u32 offset;
> -	bool msg_detectable;
> -	u32 timeout;
> -	u32 timeout2; /* Added for non_paired situation */
> -};
> -
> -static const struct hdcp2_dp_msg_data hdcp2_dp_msg_data[] = {
> -	{ HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0 },
> -	{ HDCP_2_2_AKE_SEND_CERT,
> DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
> -	  false, HDCP_2_2_CERT_TIMEOUT_MS, 0 },
> -	{ HDCP_2_2_AKE_NO_STORED_KM,
> DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
> -	  false, 0, 0 },
> -	{ HDCP_2_2_AKE_STORED_KM,
> DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
> -	  false, 0, 0 },
> -	{ HDCP_2_2_AKE_SEND_HPRIME,
> DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
> -	  true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
> -	  HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS },
> -	{ HDCP_2_2_AKE_SEND_PAIRING_INFO,
> -	  DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
> -	  HDCP_2_2_PAIRING_TIMEOUT_MS, 0 },
> -	{ HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0 },
> -	{ HDCP_2_2_LC_SEND_LPRIME,
> DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
> -	  false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0 },
> -	{ HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET,
> false,
> -	  0, 0 },
> -	{ HDCP_2_2_REP_SEND_RECVID_LIST,
> -	  DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
> -	  HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0 },
> -	{ HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET,
> false,
> -	  0, 0 },
> -	{ HDCP_2_2_REP_STREAM_MANAGE,
> -	  DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
> -	  0, 0 },
> -	{ HDCP_2_2_REP_STREAM_READY,
> DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
> -	  false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0 },
> -/* local define to shovel this through the write_2_2 interface */
> -#define HDCP_2_2_ERRATA_DP_STREAM_TYPE	50
> -	{ HDCP_2_2_ERRATA_DP_STREAM_TYPE,
> -	  DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
> -	  0, 0 },
> -};
> -
> -static int
> -intel_dp_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
> -			      u8 *rx_status)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	ssize_t ret;
> -
> -	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> -			       DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
> -			       HDCP_2_2_DP_RXSTATUS_LEN);
> -	if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
> -		drm_dbg_kms(&i915->drm,
> -			    "Read bstatus from DP/AUX failed (%zd)\n", ret);
> -		return ret >= 0 ? -EIO : ret;
> -	}
> -
> -	return 0;
> -}
> -
> -static
> -int hdcp2_detect_msg_availability(struct intel_digital_port *intel_dig_port,
> -				  u8 msg_id, bool *msg_ready)
> -{
> -	u8 rx_status;
> -	int ret;
> -
> -	*msg_ready = false;
> -	ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
> -	if (ret < 0)
> -		return ret;
> -
> -	switch (msg_id) {
> -	case HDCP_2_2_AKE_SEND_HPRIME:
> -		if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
> -			*msg_ready = true;
> -		break;
> -	case HDCP_2_2_AKE_SEND_PAIRING_INFO:
> -		if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
> -			*msg_ready = true;
> -		break;
> -	case HDCP_2_2_REP_SEND_RECVID_LIST:
> -		if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
> -			*msg_ready = true;
> -		break;
> -	default:
> -		DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
> -		return -EINVAL;
> -	}
> -
> -	return 0;
> -}
> -
> -static ssize_t
> -intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
> -			    const struct hdcp2_dp_msg_data *hdcp2_msg_data)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	struct intel_dp *dp = &intel_dig_port->dp;
> -	struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
> -	u8 msg_id = hdcp2_msg_data->msg_id;
> -	int ret, timeout;
> -	bool msg_ready = false;
> -
> -	if (msg_id == HDCP_2_2_AKE_SEND_HPRIME && !hdcp->is_paired)
> -		timeout = hdcp2_msg_data->timeout2;
> -	else
> -		timeout = hdcp2_msg_data->timeout;
> -
> -	/*
> -	 * There is no way to detect the CERT, LPRIME and STREAM_READY
> -	 * availability. So Wait for timeout and read the msg.
> -	 */
> -	if (!hdcp2_msg_data->msg_detectable) {
> -		mdelay(timeout);
> -		ret = 0;
> -	} else {
> -		/*
> -		 * As we want to check the msg availability at timeout, Ignoring
> -		 * the timeout at wait for CP_IRQ.
> -		 */
> -		intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
> -		ret = hdcp2_detect_msg_availability(intel_dig_port,
> -						    msg_id, &msg_ready);
> -		if (!msg_ready)
> -			ret = -ETIMEDOUT;
> -	}
> -
> -	if (ret)
> -		drm_dbg_kms(&i915->drm,
> -			    "msg_id %d, ret %d, timeout(mSec): %d\n",
> -			    hdcp2_msg_data->msg_id, ret, timeout);
> -
> -	return ret;
> -}
> -
> -static const struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id) -
> {
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(hdcp2_dp_msg_data); i++)
> -		if (hdcp2_dp_msg_data[i].msg_id == msg_id)
> -			return &hdcp2_dp_msg_data[i];
> -
> -	return NULL;
> -}
> -
> -static
> -int intel_dp_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
> -			     void *buf, size_t size)
> -{
> -	struct intel_dp *dp = &intel_dig_port->dp;
> -	struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
> -	unsigned int offset;
> -	u8 *byte = buf;
> -	ssize_t ret, bytes_to_write, len;
> -	const struct hdcp2_dp_msg_data *hdcp2_msg_data;
> -
> -	hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
> -	if (!hdcp2_msg_data)
> -		return -EINVAL;
> -
> -	offset = hdcp2_msg_data->offset;
> -
> -	/* No msg_id in DP HDCP2.2 msgs */
> -	bytes_to_write = size - 1;
> -	byte++;
> -
> -	hdcp->cp_irq_count_cached = atomic_read(&hdcp->cp_irq_count);
> -
> -	while (bytes_to_write) {
> -		len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
> -				DP_AUX_MAX_PAYLOAD_BYTES :
> bytes_to_write;
> -
> -		ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
> -					offset, (void *)byte, len);
> -		if (ret < 0)
> -			return ret;
> -
> -		bytes_to_write -= ret;
> -		byte += ret;
> -		offset += ret;
> -	}
> -
> -	return size;
> -}
> -
> -static
> -ssize_t get_receiver_id_list_size(struct intel_digital_port *intel_dig_port) -{
> -	u8 rx_info[HDCP_2_2_RXINFO_LEN];
> -	u32 dev_cnt;
> -	ssize_t ret;
> -
> -	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> -			       DP_HDCP_2_2_REG_RXINFO_OFFSET,
> -			       (void *)rx_info, HDCP_2_2_RXINFO_LEN);
> -	if (ret != HDCP_2_2_RXINFO_LEN)
> -		return ret >= 0 ? -EIO : ret;
> -
> -	dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
> -		   HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
> -
> -	if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
> -		dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
> -
> -	ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
> -		HDCP_2_2_RECEIVER_IDS_MAX_LEN +
> -		(dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
> -
> -	return ret;
> -}
> -
> -static
> -int intel_dp_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
> -			    u8 msg_id, void *buf, size_t size)
> -{
> -	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> -	unsigned int offset;
> -	u8 *byte = buf;
> -	ssize_t ret, bytes_to_recv, len;
> -	const struct hdcp2_dp_msg_data *hdcp2_msg_data;
> -
> -	hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
> -	if (!hdcp2_msg_data)
> -		return -EINVAL;
> -	offset = hdcp2_msg_data->offset;
> -
> -	ret = intel_dp_hdcp2_wait_for_msg(intel_dig_port, hdcp2_msg_data);
> -	if (ret < 0)
> -		return ret;
> -
> -	if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
> -		ret = get_receiver_id_list_size(intel_dig_port);
> -		if (ret < 0)
> -			return ret;
> -
> -		size = ret;
> -	}
> -	bytes_to_recv = size - 1;
> -
> -	/* DP adaptation msgs has no msg_id */
> -	byte++;
> -
> -	while (bytes_to_recv) {
> -		len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
> -		      DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
> -
> -		ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, offset,
> -				       (void *)byte, len);
> -		if (ret < 0) {
> -			drm_dbg_kms(&i915->drm, "msg_id %d, ret %zd\n",
> -				    msg_id, ret);
> -			return ret;
> -		}
> -
> -		bytes_to_recv -= ret;
> -		byte += ret;
> -		offset += ret;
> -	}
> -	byte = buf;
> -	*byte = msg_id;
> -
> -	return size;
> -}
> -
> -static
> -int intel_dp_hdcp2_config_stream_type(struct intel_digital_port
> *intel_dig_port,
> -				      bool is_repeater, u8 content_type)
> -{
> -	int ret;
> -	struct hdcp2_dp_errata_stream_type stream_type_msg;
> -
> -	if (is_repeater)
> -		return 0;
> -
> -	/*
> -	 * Errata for DP: As Stream type is used for encryption, Receiver
> -	 * should be communicated with stream type for the decryption of the
> -	 * content.
> -	 * Repeater will be communicated with stream type as a part of it's
> -	 * auth later in time.
> -	 */
> -	stream_type_msg.msg_id = HDCP_2_2_ERRATA_DP_STREAM_TYPE;
> -	stream_type_msg.stream_type = content_type;
> -
> -	ret =  intel_dp_hdcp2_write_msg(intel_dig_port, &stream_type_msg,
> -					sizeof(stream_type_msg));
> -
> -	return ret < 0 ? ret : 0;
> -
> -}
> -
> -static
> -int intel_dp_hdcp2_check_link(struct intel_digital_port *intel_dig_port) -{
> -	u8 rx_status;
> -	int ret;
> -
> -	ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
> -	if (ret)
> -		return ret;
> -
> -	if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
> -		ret = HDCP_REAUTH_REQUEST;
> -	else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
> -		ret = HDCP_LINK_INTEGRITY_FAILURE;
> -	else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
> -		ret = HDCP_TOPOLOGY_CHANGE;
> -
> -	return ret;
> -}
> -
> -static
> -int intel_dp_hdcp2_capable(struct intel_digital_port *intel_dig_port,
> -			   bool *capable)
> -{
> -	u8 rx_caps[3];
> -	int ret;
> -
> -	*capable = false;
> -	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> -			       DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
> -			       rx_caps, HDCP_2_2_RXCAPS_LEN);
> -	if (ret != HDCP_2_2_RXCAPS_LEN)
> -		return ret >= 0 ? -EIO : ret;
> -
> -	if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
> -	    HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
> -		*capable = true;
> -
> -	return 0;
> -}
> -
> -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,
> -};
> -
>  static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)  {
>  	struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); @@ -8232,7
> +7628,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
>  	intel_dp_add_properties(intel_dp, connector);
> 
>  	if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
> -		int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
> +		int ret = intel_dp_init_hdcp(intel_dig_port, intel_connector);
>  		if (ret)
>  			drm_dbg_kms(&dev_priv->drm,
>  				    "HDCP init failed, skipping.\n"); diff --git
> a/drivers/gpu/drm/i915/display/intel_dp.h
> b/drivers/gpu/drm/i915/display/intel_dp.h
> index 6352c7e97e3b..794f25573254 100644
> --- a/drivers/gpu/drm/i915/display/intel_dp.h
> +++ b/drivers/gpu/drm/i915/display/intel_dp.h
> @@ -134,4 +134,7 @@ void intel_ddi_update_pipe(struct intel_atomic_state
> *state,
>  			   const struct intel_crtc_state *crtc_state,
>  			   const struct drm_connector_state *conn_state);
> 
> +int intel_dp_init_hdcp(struct intel_digital_port *intel_dig_port,
> +		       struct intel_connector *intel_connector);
> +
>  #endif /* __INTEL_DP_H__ */
> diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
> b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
> new file mode 100644
> index 000000000000..0e06a1066d61
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
> @@ -0,0 +1,636 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright (C) 2020 Google, Inc.
> + *
> + * Authors:
> + * Sean Paul <seanpaul at chromium.org>
> + */
> +
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_hdcp.h>
> +#include <drm/drm_print.h>
> +
> +#include "intel_display_types.h"
> +#include "intel_dp.h"
> +#include "intel_hdcp.h"
> +
> +static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int
> +timeout) {
> +	long ret;
> +
> +#define C (hdcp->cp_irq_count_cached != atomic_read(&hdcp->cp_irq_count))
> +	ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
> +					       msecs_to_jiffies(timeout));
> +
> +	if (!ret)
> +		DRM_DEBUG_KMS("Timedout at waiting for CP_IRQ\n"); }
> +
> +static
> +int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
> +				u8 *an)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	u8 aksv[DRM_HDCP_KSV_LEN] = {};
> +	ssize_t dpcd_ret;
> +
> +	dpcd_ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
> DP_AUX_HDCP_AN,
> +				     an, 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",
> +			    dpcd_ret);
> +		return dpcd_ret >= 0 ? -EIO : dpcd_ret;
> +	}
> +
> +	/*
> +	 * Since Aksv is Oh-So-Secret, we can't access it in software. So we
> +	 * send an empty buffer of the correct length through the DP helpers. On
> +	 * the other side, in the transfer hook, we'll generate a flag based on
> +	 * the destination address which will tickle the hardware to output the
> +	 * Aksv on our behalf after the header is sent.
> +	 */
> +	dpcd_ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
> DP_AUX_HDCP_AKSV,
> +				     aksv, DRM_HDCP_KSV_LEN);
> +	if (dpcd_ret != DRM_HDCP_KSV_LEN) {
> +		drm_dbg_kms(&i915->drm,
> +			    "Failed to write Aksv over DP/AUX (%zd)\n",
> +			    dpcd_ret);
> +		return dpcd_ret >= 0 ? -EIO : dpcd_ret;
> +	}
> +	return 0;
> +}
> +
> +static int intel_dp_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
> +				   u8 *bksv)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	ssize_t ret;
> +
> +	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> +				      u8 *bstatus)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_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(&intel_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 *intel_dig_port,
> +			     u8 *bcaps)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	ssize_t ret;
> +
> +	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> +				   bool *repeater_present)
> +{
> +	ssize_t ret;
> +	u8 bcaps;
> +
> +	ret = intel_dp_hdcp_read_bcaps(intel_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 *intel_dig_port,
> +				u8 *ri_prime)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	ssize_t ret;
> +
> +	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> DP_AUX_HDCP_RI_PRIME,
> +			       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",
> +			    ret);
> +		return ret >= 0 ? -EIO : ret;
> +	}
> +	return 0;
> +}
> +
> +static
> +int intel_dp_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
> +				 bool *ksv_ready)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	ssize_t ret;
> +	u8 bstatus;
> +
> +	ret = drm_dp_dpcd_read(&intel_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 ret >= 0 ? -EIO : ret;
> +	}
> +	*ksv_ready = bstatus & DP_BSTATUS_READY;
> +	return 0;
> +}
> +
> +static
> +int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
> +				int num_downstream, u8 *ksv_fifo)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_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;
> +		ret = drm_dp_dpcd_read(&intel_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",
> +				    i, ret);
> +			return ret >= 0 ? -EIO : ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static
> +int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
> +				    int i, u32 *part)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	ssize_t ret;
> +
> +	if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
> +		return -EINVAL;
> +
> +	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> +				    enum transcoder cpu_transcoder,
> +				    bool enable)
> +{
> +	/* Not used for single stream DisplayPort setups */
> +	return 0;
> +}
> +
> +static
> +bool intel_dp_hdcp_check_link(struct intel_digital_port
> +*intel_dig_port) {
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	ssize_t ret;
> +	u8 bstatus;
> +
> +	ret = drm_dp_dpcd_read(&intel_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 *intel_dig_port,
> +			  bool *hdcp_capable)
> +{
> +	ssize_t ret;
> +	u8 bcaps;
> +
> +	ret = intel_dp_hdcp_read_bcaps(intel_dig_port, &bcaps);
> +	if (ret)
> +		return ret;
> +
> +	*hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE;
> +	return 0;
> +}
> +
> +struct hdcp2_dp_errata_stream_type {
> +	u8	msg_id;
> +	u8	stream_type;
> +} __packed;
> +
> +struct hdcp2_dp_msg_data {
> +	u8 msg_id;
> +	u32 offset;
> +	bool msg_detectable;
> +	u32 timeout;
> +	u32 timeout2; /* Added for non_paired situation */ };
> +
> +static const struct hdcp2_dp_msg_data hdcp2_dp_msg_data[] = {
> +	{ HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0 },
> +	{ HDCP_2_2_AKE_SEND_CERT,
> DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
> +	  false, HDCP_2_2_CERT_TIMEOUT_MS, 0 },
> +	{ HDCP_2_2_AKE_NO_STORED_KM,
> DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
> +	  false, 0, 0 },
> +	{ HDCP_2_2_AKE_STORED_KM,
> DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
> +	  false, 0, 0 },
> +	{ HDCP_2_2_AKE_SEND_HPRIME,
> DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
> +	  true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
> +	  HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS },
> +	{ HDCP_2_2_AKE_SEND_PAIRING_INFO,
> +	  DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
> +	  HDCP_2_2_PAIRING_TIMEOUT_MS, 0 },
> +	{ HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0 },
> +	{ HDCP_2_2_LC_SEND_LPRIME,
> DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
> +	  false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0 },
> +	{ HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET,
> false,
> +	  0, 0 },
> +	{ HDCP_2_2_REP_SEND_RECVID_LIST,
> +	  DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
> +	  HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0 },
> +	{ HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET,
> false,
> +	  0, 0 },
> +	{ HDCP_2_2_REP_STREAM_MANAGE,
> +	  DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
> +	  0, 0 },
> +	{ HDCP_2_2_REP_STREAM_READY,
> DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
> +	  false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0 },
> +/* local define to shovel this through the write_2_2 interface */
> +#define HDCP_2_2_ERRATA_DP_STREAM_TYPE	50
> +	{ HDCP_2_2_ERRATA_DP_STREAM_TYPE,
> +	  DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
> +	  0, 0 },
> +};
> +
> +static int
> +intel_dp_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
> +			      u8 *rx_status)
> +{
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	ssize_t ret;
> +
> +	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> +			       DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
> +			       HDCP_2_2_DP_RXSTATUS_LEN);
> +	if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
> +		drm_dbg_kms(&i915->drm,
> +			    "Read bstatus from DP/AUX failed (%zd)\n", ret);
> +		return ret >= 0 ? -EIO : ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static
> +int hdcp2_detect_msg_availability(struct intel_digital_port *intel_dig_port,
> +				  u8 msg_id, bool *msg_ready)
> +{
> +	u8 rx_status;
> +	int ret;
> +
> +	*msg_ready = false;
> +	ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
> +	if (ret < 0)
> +		return ret;
> +
> +	switch (msg_id) {
> +	case HDCP_2_2_AKE_SEND_HPRIME:
> +		if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
> +			*msg_ready = true;
> +		break;
> +	case HDCP_2_2_AKE_SEND_PAIRING_INFO:
> +		if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
> +			*msg_ready = true;
> +		break;
> +	case HDCP_2_2_REP_SEND_RECVID_LIST:
> +		if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
> +			*msg_ready = true;
> +		break;
> +	default:
> +		DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static ssize_t
> +intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
> +			    const struct hdcp2_dp_msg_data *hdcp2_msg_data)
> {
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	struct intel_dp *dp = &intel_dig_port->dp;
> +	struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
> +	u8 msg_id = hdcp2_msg_data->msg_id;
> +	int ret, timeout;
> +	bool msg_ready = false;
> +
> +	if (msg_id == HDCP_2_2_AKE_SEND_HPRIME && !hdcp->is_paired)
> +		timeout = hdcp2_msg_data->timeout2;
> +	else
> +		timeout = hdcp2_msg_data->timeout;
> +
> +	/*
> +	 * There is no way to detect the CERT, LPRIME and STREAM_READY
> +	 * availability. So Wait for timeout and read the msg.
> +	 */
> +	if (!hdcp2_msg_data->msg_detectable) {
> +		mdelay(timeout);
> +		ret = 0;
> +	} else {
> +		/*
> +		 * As we want to check the msg availability at timeout, Ignoring
> +		 * the timeout at wait for CP_IRQ.
> +		 */
> +		intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
> +		ret = hdcp2_detect_msg_availability(intel_dig_port,
> +						    msg_id, &msg_ready);
> +		if (!msg_ready)
> +			ret = -ETIMEDOUT;
> +	}
> +
> +	if (ret)
> +		drm_dbg_kms(&i915->drm,
> +			    "msg_id %d, ret %d, timeout(mSec): %d\n",
> +			    hdcp2_msg_data->msg_id, ret, timeout);
> +
> +	return ret;
> +}
> +
> +static const struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hdcp2_dp_msg_data); i++)
> +		if (hdcp2_dp_msg_data[i].msg_id == msg_id)
> +			return &hdcp2_dp_msg_data[i];
> +
> +	return NULL;
> +}
> +
> +static
> +int intel_dp_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
> +			     void *buf, size_t size)
> +{
> +	struct intel_dp *dp = &intel_dig_port->dp;
> +	struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
> +	unsigned int offset;
> +	u8 *byte = buf;
> +	ssize_t ret, bytes_to_write, len;
> +	const struct hdcp2_dp_msg_data *hdcp2_msg_data;
> +
> +	hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
> +	if (!hdcp2_msg_data)
> +		return -EINVAL;
> +
> +	offset = hdcp2_msg_data->offset;
> +
> +	/* No msg_id in DP HDCP2.2 msgs */
> +	bytes_to_write = size - 1;
> +	byte++;
> +
> +	hdcp->cp_irq_count_cached = atomic_read(&hdcp->cp_irq_count);
> +
> +	while (bytes_to_write) {
> +		len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
> +				DP_AUX_MAX_PAYLOAD_BYTES :
> bytes_to_write;
> +
> +		ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
> +					offset, (void *)byte, len);
> +		if (ret < 0)
> +			return ret;
> +
> +		bytes_to_write -= ret;
> +		byte += ret;
> +		offset += ret;
> +	}
> +
> +	return size;
> +}
> +
> +static
> +ssize_t get_receiver_id_list_size(struct intel_digital_port
> +*intel_dig_port) {
> +	u8 rx_info[HDCP_2_2_RXINFO_LEN];
> +	u32 dev_cnt;
> +	ssize_t ret;
> +
> +	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> +			       DP_HDCP_2_2_REG_RXINFO_OFFSET,
> +			       (void *)rx_info, HDCP_2_2_RXINFO_LEN);
> +	if (ret != HDCP_2_2_RXINFO_LEN)
> +		return ret >= 0 ? -EIO : ret;
> +
> +	dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
> +		   HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
> +
> +	if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
> +		dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
> +
> +	ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
> +		HDCP_2_2_RECEIVER_IDS_MAX_LEN +
> +		(dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
> +
> +	return ret;
> +}
> +
> +static
> +int intel_dp_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
> +			    u8 msg_id, void *buf, size_t size) {
> +	struct drm_i915_private *i915 = to_i915(intel_dig_port-
> >base.base.dev);
> +	unsigned int offset;
> +	u8 *byte = buf;
> +	ssize_t ret, bytes_to_recv, len;
> +	const struct hdcp2_dp_msg_data *hdcp2_msg_data;
> +
> +	hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
> +	if (!hdcp2_msg_data)
> +		return -EINVAL;
> +	offset = hdcp2_msg_data->offset;
> +
> +	ret = intel_dp_hdcp2_wait_for_msg(intel_dig_port, hdcp2_msg_data);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
> +		ret = get_receiver_id_list_size(intel_dig_port);
> +		if (ret < 0)
> +			return ret;
> +
> +		size = ret;
> +	}
> +	bytes_to_recv = size - 1;
> +
> +	/* DP adaptation msgs has no msg_id */
> +	byte++;
> +
> +	while (bytes_to_recv) {
> +		len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
> +		      DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
> +
> +		ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, offset,
> +				       (void *)byte, len);
> +		if (ret < 0) {
> +			drm_dbg_kms(&i915->drm, "msg_id %d, ret %zd\n",
> +				    msg_id, ret);
> +			return ret;
> +		}
> +
> +		bytes_to_recv -= ret;
> +		byte += ret;
> +		offset += ret;
> +	}
> +	byte = buf;
> +	*byte = msg_id;
> +
> +	return size;
> +}
> +
> +static
> +int intel_dp_hdcp2_config_stream_type(struct intel_digital_port
> *intel_dig_port,
> +				      bool is_repeater, u8 content_type) {
> +	int ret;
> +	struct hdcp2_dp_errata_stream_type stream_type_msg;
> +
> +	if (is_repeater)
> +		return 0;
> +
> +	/*
> +	 * Errata for DP: As Stream type is used for encryption, Receiver
> +	 * should be communicated with stream type for the decryption of the
> +	 * content.
> +	 * Repeater will be communicated with stream type as a part of it's
> +	 * auth later in time.
> +	 */
> +	stream_type_msg.msg_id = HDCP_2_2_ERRATA_DP_STREAM_TYPE;
> +	stream_type_msg.stream_type = content_type;
> +
> +	ret =  intel_dp_hdcp2_write_msg(intel_dig_port, &stream_type_msg,
> +					sizeof(stream_type_msg));
> +
> +	return ret < 0 ? ret : 0;
> +
> +}
> +
> +static
> +int intel_dp_hdcp2_check_link(struct intel_digital_port
> +*intel_dig_port) {
> +	u8 rx_status;
> +	int ret;
> +
> +	ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
> +	if (ret)
> +		return ret;
> +
> +	if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
> +		ret = HDCP_REAUTH_REQUEST;
> +	else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
> +		ret = HDCP_LINK_INTEGRITY_FAILURE;
> +	else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
> +		ret = HDCP_TOPOLOGY_CHANGE;
> +
> +	return ret;
> +}
> +
> +static
> +int intel_dp_hdcp2_capable(struct intel_digital_port *intel_dig_port,
> +			   bool *capable)
> +{
> +	u8 rx_caps[3];
> +	int ret;
> +
> +	*capable = false;
> +	ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
> +			       DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
> +			       rx_caps, HDCP_2_2_RXCAPS_LEN);
> +	if (ret != HDCP_2_2_RXCAPS_LEN)
> +		return ret >= 0 ? -EIO : ret;
> +
> +	if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
> +	    HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
> +		*capable = true;
> +
> +	return 0;
> +}
> +
> +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,
> +};
> +
> +int intel_dp_init_hdcp(struct intel_digital_port *intel_dig_port,
> +		       struct intel_connector *intel_connector) {
> +	struct drm_device *dev = intel_connector->base.dev;
> +	struct drm_i915_private *dev_priv = to_i915(dev);
> +	struct intel_encoder *intel_encoder = &intel_dig_port->base;
> +	enum port port = intel_encoder->port;
> +	struct intel_dp *intel_dp = &intel_dig_port->dp;
> +
> +	if (!is_hdcp_supported(dev_priv, port))
> +		return 0;
> +
> +	if (!intel_dp_is_edp(intel_dp))
> +		return intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
> +
> +	return 0;
> +}
> --
> Sean Paul, Software Engineer, Google / Chromium OS



More information about the Intel-gfx mailing list