|
@@ -36,7 +36,9 @@
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
|
#include <drm/drm_crtc.h>
|
|
|
#include <drm/drm_crtc_helper.h>
|
|
|
+#include <drm/drm_dp_helper.h>
|
|
|
#include <drm/drm_edid.h>
|
|
|
+#include <drm/drm_hdcp.h>
|
|
|
#include "intel_drv.h"
|
|
|
#include <drm/i915_drm.h>
|
|
|
#include "i915_drv.h"
|
|
@@ -1025,10 +1027,29 @@ static uint32_t skl_get_aux_send_ctl(struct intel_dp *intel_dp,
|
|
|
DP_AUX_CH_CTL_SYNC_PULSE_SKL(32);
|
|
|
}
|
|
|
|
|
|
+static uint32_t intel_dp_get_aux_send_ctl(struct intel_dp *intel_dp,
|
|
|
+ bool has_aux_irq,
|
|
|
+ int send_bytes,
|
|
|
+ uint32_t aux_clock_divider,
|
|
|
+ bool aksv_write)
|
|
|
+{
|
|
|
+ uint32_t val = 0;
|
|
|
+
|
|
|
+ if (aksv_write) {
|
|
|
+ send_bytes += 5;
|
|
|
+ val |= DP_AUX_CH_CTL_AUX_AKSV_SELECT;
|
|
|
+ }
|
|
|
+
|
|
|
+ return val | intel_dp->get_aux_send_ctl(intel_dp,
|
|
|
+ has_aux_irq,
|
|
|
+ send_bytes,
|
|
|
+ aux_clock_divider);
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
const uint8_t *send, int send_bytes,
|
|
|
- uint8_t *recv, int recv_size)
|
|
|
+ uint8_t *recv, int recv_size, bool aksv_write)
|
|
|
{
|
|
|
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
|
|
struct drm_i915_private *dev_priv =
|
|
@@ -1088,10 +1109,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
|
|
|
}
|
|
|
|
|
|
while ((aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, clock++))) {
|
|
|
- u32 send_ctl = intel_dp->get_aux_send_ctl(intel_dp,
|
|
|
- has_aux_irq,
|
|
|
- send_bytes,
|
|
|
- aux_clock_divider);
|
|
|
+ u32 send_ctl = intel_dp_get_aux_send_ctl(intel_dp,
|
|
|
+ has_aux_irq,
|
|
|
+ send_bytes,
|
|
|
+ aux_clock_divider,
|
|
|
+ aksv_write);
|
|
|
|
|
|
/* Must try at least 3 times according to DP spec */
|
|
|
for (try = 0; try < 5; try++) {
|
|
@@ -1228,7 +1250,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
|
|
if (msg->buffer)
|
|
|
memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
|
|
|
|
|
|
- ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
|
|
|
+ ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize,
|
|
|
+ false);
|
|
|
if (ret > 0) {
|
|
|
msg->reply = rxbuf[0] >> 4;
|
|
|
|
|
@@ -1250,7 +1273,8 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
|
|
if (WARN_ON(rxsize > 20))
|
|
|
return -E2BIG;
|
|
|
|
|
|
- ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
|
|
|
+ ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize,
|
|
|
+ false);
|
|
|
if (ret > 0) {
|
|
|
msg->reply = rxbuf[0] >> 4;
|
|
|
/*
|
|
@@ -4985,6 +5009,203 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
|
|
|
pps_unlock(intel_dp);
|
|
|
}
|
|
|
|
|
|
+static
|
|
|
+int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
|
|
|
+ u8 *an)
|
|
|
+{
|
|
|
+ struct intel_dp *intel_dp = enc_to_intel_dp(&intel_dig_port->base.base);
|
|
|
+ uint8_t txbuf[4], rxbuf[2], reply = 0;
|
|
|
+ ssize_t dpcd_ret;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Output An first, that's easy */
|
|
|
+ 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_ERROR("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 in
|
|
|
+ * order to get it on the wire, we need to create the AUX header as if
|
|
|
+ * we were writing the data, and then tickle the hardware to output the
|
|
|
+ * data once the header is sent out.
|
|
|
+ */
|
|
|
+ txbuf[0] = (DP_AUX_NATIVE_WRITE << 4) |
|
|
|
+ ((DP_AUX_HDCP_AKSV >> 16) & 0xf);
|
|
|
+ txbuf[1] = (DP_AUX_HDCP_AKSV >> 8) & 0xff;
|
|
|
+ txbuf[2] = DP_AUX_HDCP_AKSV & 0xff;
|
|
|
+ txbuf[3] = DRM_HDCP_KSV_LEN - 1;
|
|
|
+
|
|
|
+ ret = intel_dp_aux_ch(intel_dp, txbuf, sizeof(txbuf), rxbuf,
|
|
|
+ sizeof(rxbuf), true);
|
|
|
+ if (ret < 0) {
|
|
|
+ DRM_ERROR("Write Aksv over DP/AUX failed (%d)\n", ret);
|
|
|
+ return ret;
|
|
|
+ } else if (ret == 0) {
|
|
|
+ DRM_ERROR("Aksv write over DP/AUX was empty\n");
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ reply = (rxbuf[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK;
|
|
|
+ return reply == DP_AUX_NATIVE_REPLY_ACK ? 0 : -EIO;
|
|
|
+}
|
|
|
+
|
|
|
+static int intel_dp_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
|
|
|
+ u8 *bksv)
|
|
|
+{
|
|
|
+ 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_ERROR("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)
|
|
|
+{
|
|
|
+ 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_ERROR("Read bstatus 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 = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BCAPS,
|
|
|
+ &bcaps, 1);
|
|
|
+ if (ret != 1) {
|
|
|
+ DRM_ERROR("Read bcaps from DP/AUX failed (%zd)\n", ret);
|
|
|
+ return ret >= 0 ? -EIO : 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)
|
|
|
+{
|
|
|
+ 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_ERROR("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)
|
|
|
+{
|
|
|
+ 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_ERROR("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)
|
|
|
+{
|
|
|
+ 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_ERROR("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)
|
|
|
+{
|
|
|
+ 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_ERROR("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,
|
|
|
+ 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)
|
|
|
+{
|
|
|
+ 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_ERROR("Read bstatus from DP/AUX failed (%zd)\n", ret);
|
|
|
+ return ret >= 0 ? -EIO : ret;
|
|
|
+ }
|
|
|
+ return !(bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ));
|
|
|
+}
|
|
|
+
|
|
|
+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,
|
|
|
+};
|
|
|
+
|
|
|
static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp));
|
|
@@ -5150,6 +5371,9 @@ err:
|
|
|
drm_modeset_acquire_fini(&ctx);
|
|
|
WARN(iret, "Acquiring modeset locks failed with %i\n", iret);
|
|
|
|
|
|
+ /* Short pulse can signify loss of hdcp authentication */
|
|
|
+ intel_hdcp_check_link(intel_dp->attached_connector);
|
|
|
+
|
|
|
if (!handled) {
|
|
|
intel_dp->detect_done = false;
|
|
|
goto put_power;
|
|
@@ -6128,6 +6352,12 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
|
|
|
|
|
intel_dp_add_properties(intel_dp, connector);
|
|
|
|
|
|
+ if (INTEL_GEN(dev_priv) >= 9 && !intel_dp_is_edp(intel_dp)) {
|
|
|
+ int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
|
|
|
+ if (ret)
|
|
|
+ DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
|
|
|
+ }
|
|
|
+
|
|
|
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
|
|
|
* 0xd. Failure to do so will result in spurious interrupts being
|
|
|
* generated on the port when a cable is not attached.
|