Merge tag 'drm-intel-next-2018-02-21' of git://anongit.freedesktop.org/drm/drm-intel...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / i915 / intel_dp.c
index 1868f73f730c9c151e050befff3e5f0506b9ed8d..f20b25f98e5adf90bd7c0f3e6bb93493fbc5b32f 100644 (file)
@@ -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"
@@ -1060,10 +1062,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 =
@@ -1123,10 +1144,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++) {
@@ -1263,7 +1285,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;
 
@@ -1285,7 +1308,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;
                        /*
@@ -5025,6 +5049,236 @@ 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_read_bcaps(struct intel_digital_port *intel_dig_port,
+                            u8 *bcaps)
+{
+       ssize_t ret;
+
+       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;
+       }
+
+       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)
+{
+       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 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;
+}
+
+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,
+};
+
 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));
@@ -5190,6 +5444,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;
@@ -6179,6 +6436,12 @@ 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);
+               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.