[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