| /* SPDX-License-Identifier: MIT */ |
| /* |
| * Copyright (C) 2020 Google, Inc. |
| * |
| * Authors: |
| * Sean Paul <seanpaul@chromium.org> |
| */ |
| |
| #include <drm/display/drm_dp_helper.h> |
| #include <drm/display/drm_dp_mst_helper.h> |
| #include <drm/display/drm_hdcp_helper.h> |
| #include <drm/drm_print.h> |
| |
| #include "i915_reg.h" |
| #include "intel_ddi.h" |
| #include "intel_de.h" |
| #include "intel_display_types.h" |
| #include "intel_dp.h" |
| #include "intel_dp_hdcp.h" |
| #include "intel_hdcp.h" |
| #include "intel_hdcp_regs.h" |
| |
| static u32 transcoder_to_stream_enc_status(enum transcoder cpu_transcoder) |
| { |
| switch (cpu_transcoder) { |
| case TRANSCODER_A: |
| return HDCP_STATUS_STREAM_A_ENC; |
| case TRANSCODER_B: |
| return HDCP_STATUS_STREAM_B_ENC; |
| case TRANSCODER_C: |
| return HDCP_STATUS_STREAM_C_ENC; |
| case TRANSCODER_D: |
| return HDCP_STATUS_STREAM_D_ENC; |
| default: |
| return 0; |
| } |
| } |
| |
| static void intel_dp_hdcp_wait_for_cp_irq(struct intel_connector *connector, |
| int timeout) |
| { |
| struct intel_hdcp *hdcp = &connector->hdcp; |
| 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_dbg_kms(connector->base.dev, |
| "Timedout at waiting for CP_IRQ\n"); |
| } |
| |
| static |
| int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *dig_port, |
| u8 *an) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| u8 aksv[DRM_HDCP_KSV_LEN] = {}; |
| ssize_t dpcd_ret; |
| |
| /* Output An first, that's easy */ |
| dpcd_ret = drm_dp_dpcd_write(&dig_port->dp.aux, DP_AUX_HDCP_AN, |
| an, DRM_HDCP_AN_LEN); |
| 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(&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 *dig_port, |
| u8 *bksv) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| |
| ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BKSV, bksv, |
| DRM_HDCP_KSV_LEN); |
| if (ret != DRM_HDCP_KSV_LEN) { |
| drm_dbg_kms(&i915->drm, |
| "Read Bksv from DP/AUX failed (%zd)\n", ret); |
| return ret >= 0 ? -EIO : ret; |
| } |
| return 0; |
| } |
| |
| static int intel_dp_hdcp_read_bstatus(struct intel_digital_port *dig_port, |
| u8 *bstatus) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| |
| /* |
| * For some reason the HDMI and DP HDCP specs call this register |
| * definition by different names. In the HDMI spec, it's called BSTATUS, |
| * but in DP it's called BINFO. |
| */ |
| ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BINFO, |
| bstatus, DRM_HDCP_BSTATUS_LEN); |
| if (ret != DRM_HDCP_BSTATUS_LEN) { |
| drm_dbg_kms(&i915->drm, |
| "Read bstatus from DP/AUX failed (%zd)\n", ret); |
| return ret >= 0 ? -EIO : ret; |
| } |
| return 0; |
| } |
| |
| static |
| int intel_dp_hdcp_read_bcaps(struct drm_dp_aux *aux, |
| struct drm_i915_private *i915, |
| u8 *bcaps) |
| { |
| ssize_t ret; |
| |
| ret = drm_dp_dpcd_read(aux, DP_AUX_HDCP_BCAPS, |
| bcaps, 1); |
| if (ret != 1) { |
| drm_dbg_kms(&i915->drm, |
| "Read bcaps from DP/AUX failed (%zd)\n", ret); |
| return ret >= 0 ? -EIO : ret; |
| } |
| |
| return 0; |
| } |
| |
| static |
| int intel_dp_hdcp_repeater_present(struct intel_digital_port *dig_port, |
| bool *repeater_present) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| u8 bcaps; |
| |
| ret = intel_dp_hdcp_read_bcaps(&dig_port->dp.aux, i915, &bcaps); |
| if (ret) |
| return ret; |
| |
| *repeater_present = bcaps & DP_BCAPS_REPEATER_PRESENT; |
| return 0; |
| } |
| |
| static |
| int intel_dp_hdcp_read_ri_prime(struct intel_digital_port *dig_port, |
| u8 *ri_prime) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| |
| ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_RI_PRIME, |
| 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 *dig_port, |
| bool *ksv_ready) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| u8 bstatus; |
| |
| ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BSTATUS, |
| &bstatus, 1); |
| if (ret != 1) { |
| drm_dbg_kms(&i915->drm, |
| "Read bstatus from DP/AUX failed (%zd)\n", ret); |
| return ret >= 0 ? -EIO : ret; |
| } |
| *ksv_ready = bstatus & DP_BSTATUS_READY; |
| return 0; |
| } |
| |
| static |
| int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *dig_port, |
| int num_downstream, u8 *ksv_fifo) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| int i; |
| |
| /* KSV list is read via 15 byte window (3 entries @ 5 bytes each) */ |
| for (i = 0; i < num_downstream; i += 3) { |
| size_t len = min(num_downstream - i, 3) * DRM_HDCP_KSV_LEN; |
| ret = drm_dp_dpcd_read(&dig_port->dp.aux, |
| DP_AUX_HDCP_KSV_FIFO, |
| ksv_fifo + i * DRM_HDCP_KSV_LEN, |
| len); |
| if (ret != len) { |
| drm_dbg_kms(&i915->drm, |
| "Read ksv[%d] from DP/AUX failed (%zd)\n", |
| i, ret); |
| return ret >= 0 ? -EIO : ret; |
| } |
| } |
| return 0; |
| } |
| |
| static |
| int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *dig_port, |
| int i, u32 *part) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| |
| if (i >= DRM_HDCP_V_PRIME_NUM_PARTS) |
| return -EINVAL; |
| |
| ret = drm_dp_dpcd_read(&dig_port->dp.aux, |
| DP_AUX_HDCP_V_PRIME(i), part, |
| DRM_HDCP_V_PRIME_PART_LEN); |
| if (ret != DRM_HDCP_V_PRIME_PART_LEN) { |
| drm_dbg_kms(&i915->drm, |
| "Read v'[%d] from DP/AUX failed (%zd)\n", i, ret); |
| return ret >= 0 ? -EIO : ret; |
| } |
| return 0; |
| } |
| |
| static |
| int intel_dp_hdcp_toggle_signalling(struct intel_digital_port *dig_port, |
| enum transcoder cpu_transcoder, |
| bool enable) |
| { |
| /* Not used for single stream DisplayPort setups */ |
| return 0; |
| } |
| |
| static |
| bool intel_dp_hdcp_check_link(struct intel_digital_port *dig_port, |
| struct intel_connector *connector) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| u8 bstatus; |
| |
| ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BSTATUS, |
| &bstatus, 1); |
| if (ret != 1) { |
| drm_dbg_kms(&i915->drm, |
| "Read bstatus from DP/AUX failed (%zd)\n", ret); |
| return false; |
| } |
| |
| return !(bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ)); |
| } |
| |
| static |
| int intel_dp_hdcp_get_capability(struct intel_digital_port *dig_port, |
| bool *hdcp_capable) |
| { |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| ssize_t ret; |
| u8 bcaps; |
| |
| ret = intel_dp_hdcp_read_bcaps(&dig_port->dp.aux, i915, &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 */ |
| /* Timeout to read entire msg */ |
| u32 msg_read_timeout; |
| }; |
| |
| 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, 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_DP_CERT_READ_TIMEOUT_MS}, |
| { HDCP_2_2_AKE_NO_STORED_KM, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET, |
| false, 0, 0, 0 }, |
| { HDCP_2_2_AKE_STORED_KM, DP_HDCP_2_2_AKE_STORED_KM_OFFSET, |
| false, 0, 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_DP_HPRIME_READ_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_DP_PAIRING_READ_TIMEOUT_MS }, |
| { HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 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, 0 }, |
| { HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, false, |
| 0, 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, 0 }, |
| { HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, false, |
| 0, 0, 0 }, |
| { HDCP_2_2_REP_STREAM_MANAGE, |
| DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false, |
| 0, 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, 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_connector *connector, |
| u8 *rx_status) |
| { |
| struct drm_i915_private *i915 = to_i915(connector->base.dev); |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_dp_aux *aux = &dig_port->dp.aux; |
| ssize_t ret; |
| |
| ret = drm_dp_dpcd_read(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_connector *connector, |
| u8 msg_id, bool *msg_ready) |
| { |
| u8 rx_status; |
| int ret; |
| |
| *msg_ready = false; |
| ret = intel_dp_hdcp2_read_rx_status(connector, &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_err(connector->base.dev, |
| "Unidentified msg_id: %d\n", msg_id); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static ssize_t |
| intel_dp_hdcp2_wait_for_msg(struct intel_connector *connector, |
| const struct hdcp2_dp_msg_data *hdcp2_msg_data) |
| { |
| struct drm_i915_private *i915 = to_i915(connector->base.dev); |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct intel_dp *dp = &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(connector, timeout); |
| ret = hdcp2_detect_msg_availability(connector, 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_connector *connector, |
| void *buf, size_t size) |
| { |
| unsigned int offset; |
| u8 *byte = buf; |
| ssize_t ret, bytes_to_write, len; |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_dp_aux *aux = &dig_port->dp.aux; |
| 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++; |
| |
| 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(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_rx_info(struct intel_connector *connector, |
| u32 *dev_cnt, u8 *byte) |
| { |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_dp_aux *aux = &dig_port->dp.aux; |
| ssize_t ret; |
| u8 *rx_info = byte; |
| |
| ret = drm_dp_dpcd_read(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; |
| |
| return ret; |
| } |
| |
| static |
| int intel_dp_hdcp2_read_msg(struct intel_connector *connector, |
| u8 msg_id, void *buf, size_t size) |
| { |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); |
| struct drm_dp_aux *aux = &dig_port->dp.aux; |
| struct intel_dp *dp = &dig_port->dp; |
| struct intel_hdcp *hdcp = &dp->attached_connector->hdcp; |
| unsigned int offset; |
| u8 *byte = buf; |
| ssize_t ret, bytes_to_recv, len; |
| const struct hdcp2_dp_msg_data *hdcp2_msg_data; |
| ktime_t msg_end = ktime_set(0, 0); |
| bool msg_expired; |
| u32 dev_cnt; |
| |
| 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(connector, hdcp2_msg_data); |
| if (ret < 0) |
| return ret; |
| |
| hdcp->cp_irq_count_cached = atomic_read(&hdcp->cp_irq_count); |
| |
| /* DP adaptation msgs has no msg_id */ |
| byte++; |
| |
| if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) { |
| ret = get_receiver_id_list_rx_info(connector, &dev_cnt, byte); |
| if (ret < 0) |
| return ret; |
| |
| byte += ret; |
| size = sizeof(struct hdcp2_rep_send_receiverid_list) - |
| HDCP_2_2_RXINFO_LEN - HDCP_2_2_RECEIVER_IDS_MAX_LEN + |
| (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN); |
| offset += HDCP_2_2_RXINFO_LEN; |
| } |
| |
| bytes_to_recv = size - 1; |
| |
| while (bytes_to_recv) { |
| len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ? |
| DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv; |
| |
| /* Entire msg read timeout since initiate of msg read */ |
| if (bytes_to_recv == size - 1 && hdcp2_msg_data->msg_read_timeout > 0) { |
| msg_end = ktime_add_ms(ktime_get_raw(), |
| hdcp2_msg_data->msg_read_timeout); |
| } |
| |
| ret = drm_dp_dpcd_read(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; |
| } |
| |
| if (hdcp2_msg_data->msg_read_timeout > 0) { |
| msg_expired = ktime_after(ktime_get_raw(), msg_end); |
| if (msg_expired) { |
| drm_dbg_kms(&i915->drm, "msg_id %d, entire msg read timeout(mSec): %d\n", |
| msg_id, hdcp2_msg_data->msg_read_timeout); |
| return -ETIMEDOUT; |
| } |
| } |
| |
| byte = buf; |
| *byte = msg_id; |
| |
| return size; |
| } |
| |
| static |
| int intel_dp_hdcp2_config_stream_type(struct intel_connector *connector, |
| 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(connector, &stream_type_msg, |
| sizeof(stream_type_msg)); |
| |
| return ret < 0 ? ret : 0; |
| |
| } |
| |
| static |
| int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port, |
| struct intel_connector *connector) |
| { |
| u8 rx_status; |
| int ret; |
| |
| ret = intel_dp_hdcp2_read_rx_status(connector, |
| &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_get_capability(struct drm_dp_aux *aux, |
| bool *capable) |
| { |
| u8 rx_caps[3]; |
| int ret, i; |
| |
| *capable = false; |
| |
| /* |
| * Some HDCP monitors act really shady by not giving the correct hdcp |
| * capability on the first rx_caps read and usually take an extra read |
| * to give the capability. We read rx_caps three times before we |
| * declare a monitor not capable of HDCP 2.2. |
| */ |
| for (i = 0; i < 3; i++) { |
| ret = drm_dp_dpcd_read(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; |
| break; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static |
| int intel_dp_hdcp2_get_capability(struct intel_connector *connector, |
| bool *capable) |
| { |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_dp_aux *aux = &dig_port->dp.aux; |
| |
| return _intel_dp_hdcp2_get_capability(aux, capable); |
| } |
| |
| static |
| int intel_dp_hdcp_get_remote_capability(struct intel_connector *connector, |
| bool *hdcp_capable, |
| bool *hdcp2_capable) |
| { |
| struct drm_i915_private *i915 = to_i915(connector->base.dev); |
| struct drm_dp_aux *aux; |
| u8 bcaps; |
| int ret; |
| |
| *hdcp_capable = false; |
| *hdcp2_capable = false; |
| if (!connector->mst_port) |
| return -EINVAL; |
| |
| aux = &connector->port->aux; |
| ret = _intel_dp_hdcp2_get_capability(aux, hdcp2_capable); |
| if (ret) |
| drm_dbg_kms(&i915->drm, |
| "HDCP2 DPCD capability read failed err: %d\n", ret); |
| |
| ret = intel_dp_hdcp_read_bcaps(aux, i915, &bcaps); |
| if (ret) |
| return ret; |
| |
| *hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE; |
| |
| 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_get_capability = intel_dp_hdcp_get_capability, |
| .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_get_capability = intel_dp_hdcp2_get_capability, |
| .protocol = HDCP_PROTOCOL_DP, |
| }; |
| |
| static int |
| intel_dp_mst_toggle_hdcp_stream_select(struct intel_connector *connector, |
| bool enable) |
| { |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_i915_private *i915 = to_i915(connector->base.dev); |
| struct intel_hdcp *hdcp = &connector->hdcp; |
| int ret; |
| |
| ret = intel_ddi_toggle_hdcp_bits(&dig_port->base, |
| hdcp->stream_transcoder, enable, |
| TRANS_DDI_HDCP_SELECT); |
| if (ret) |
| drm_err(&i915->drm, "%s HDCP stream select failed (%d)\n", |
| enable ? "Enable" : "Disable", ret); |
| return ret; |
| } |
| |
| static int |
| intel_dp_mst_hdcp_stream_encryption(struct intel_connector *connector, |
| bool enable) |
| { |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_i915_private *i915 = to_i915(connector->base.dev); |
| struct intel_hdcp *hdcp = &connector->hdcp; |
| enum port port = dig_port->base.port; |
| enum transcoder cpu_transcoder = hdcp->stream_transcoder; |
| u32 stream_enc_status; |
| int ret; |
| |
| ret = intel_dp_mst_toggle_hdcp_stream_select(connector, enable); |
| if (ret) |
| return ret; |
| |
| stream_enc_status = transcoder_to_stream_enc_status(cpu_transcoder); |
| if (!stream_enc_status) |
| return -EINVAL; |
| |
| /* Wait for encryption confirmation */ |
| if (intel_de_wait(i915, HDCP_STATUS(i915, cpu_transcoder, port), |
| stream_enc_status, enable ? stream_enc_status : 0, |
| HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { |
| drm_err(&i915->drm, "Timed out waiting for transcoder: %s stream encryption %s\n", |
| transcoder_name(cpu_transcoder), enable ? "enabled" : "disabled"); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| intel_dp_mst_hdcp2_stream_encryption(struct intel_connector *connector, |
| bool enable) |
| { |
| struct intel_digital_port *dig_port = intel_attached_dig_port(connector); |
| struct drm_i915_private *i915 = to_i915(connector->base.dev); |
| struct hdcp_port_data *data = &dig_port->hdcp_port_data; |
| struct intel_hdcp *hdcp = &connector->hdcp; |
| enum transcoder cpu_transcoder = hdcp->stream_transcoder; |
| enum pipe pipe = (enum pipe)cpu_transcoder; |
| enum port port = dig_port->base.port; |
| int ret; |
| |
| drm_WARN_ON(&i915->drm, enable && |
| !!(intel_de_read(i915, HDCP2_AUTH_STREAM(i915, cpu_transcoder, port)) |
| & AUTH_STREAM_TYPE) != data->streams[0].stream_type); |
| |
| ret = intel_dp_mst_toggle_hdcp_stream_select(connector, enable); |
| if (ret) |
| return ret; |
| |
| /* Wait for encryption confirmation */ |
| if (intel_de_wait(i915, HDCP2_STREAM_STATUS(i915, cpu_transcoder, pipe), |
| STREAM_ENCRYPTION_STATUS, |
| enable ? STREAM_ENCRYPTION_STATUS : 0, |
| HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) { |
| drm_err(&i915->drm, "Timed out waiting for transcoder: %s stream encryption %s\n", |
| transcoder_name(cpu_transcoder), enable ? "enabled" : "disabled"); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static |
| int intel_dp_mst_hdcp2_check_link(struct intel_digital_port *dig_port, |
| struct intel_connector *connector) |
| { |
| struct intel_hdcp *hdcp = &connector->hdcp; |
| int ret; |
| |
| /* |
| * We do need to do the Link Check only for the connector involved with |
| * HDCP port authentication and encryption. |
| * We can re-use the hdcp->is_repeater flag to know that the connector |
| * involved with HDCP port authentication and encryption. |
| */ |
| if (hdcp->is_repeater) { |
| ret = intel_dp_hdcp2_check_link(dig_port, connector); |
| if (ret) |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = { |
| .write_an_aksv = intel_dp_hdcp_write_an_aksv, |
| .read_bksv = intel_dp_hdcp_read_bksv, |
| .read_bstatus = intel_dp_hdcp_read_bstatus, |
| .repeater_present = intel_dp_hdcp_repeater_present, |
| .read_ri_prime = intel_dp_hdcp_read_ri_prime, |
| .read_ksv_ready = intel_dp_hdcp_read_ksv_ready, |
| .read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo, |
| .read_v_prime_part = intel_dp_hdcp_read_v_prime_part, |
| .toggle_signalling = intel_dp_hdcp_toggle_signalling, |
| .stream_encryption = intel_dp_mst_hdcp_stream_encryption, |
| .check_link = intel_dp_hdcp_check_link, |
| .hdcp_get_capability = intel_dp_hdcp_get_capability, |
| .write_2_2_msg = intel_dp_hdcp2_write_msg, |
| .read_2_2_msg = intel_dp_hdcp2_read_msg, |
| .config_stream_type = intel_dp_hdcp2_config_stream_type, |
| .stream_2_2_encryption = intel_dp_mst_hdcp2_stream_encryption, |
| .check_2_2_link = intel_dp_mst_hdcp2_check_link, |
| .hdcp_2_2_get_capability = intel_dp_hdcp2_get_capability, |
| .get_remote_hdcp_capability = intel_dp_hdcp_get_remote_capability, |
| .protocol = HDCP_PROTOCOL_DP, |
| }; |
| |
| int intel_dp_hdcp_init(struct intel_digital_port *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 = &dig_port->base; |
| enum port port = intel_encoder->port; |
| struct intel_dp *intel_dp = &dig_port->dp; |
| |
| if (!is_hdcp_supported(dev_priv, port)) |
| return 0; |
| |
| if (intel_connector->mst_port) |
| return intel_hdcp_init(intel_connector, dig_port, |
| &intel_dp_mst_hdcp_shim); |
| else if (!intel_dp_is_edp(intel_dp)) |
| return intel_hdcp_init(intel_connector, dig_port, |
| &intel_dp_hdcp_shim); |
| |
| return 0; |
| } |