Message ID | 20191212190230.188505-5-sean@poorly.run (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/i915: Add support for HDCP 1.4 over MST connectors | expand |
On 2019-12-12 at 14:02:22 -0500, Sean Paul wrote: > From: Sean Paul <seanpaul@chromium.org> > > Instead of hand rolling the transfer ourselves in the hdcp hook, inspect > aux messages and add the aksv flag in the aux transfer hook. > > IIRC, this was the original implementation and folks wanted this hack to > be isolated to the hdcp code, which makes sense. > > However in testing an LG monitor on my desk, I noticed it was passing > back a DEFER reply. This wasn't handled in our hand-rolled code and HDCP > auth was failing as a result. Instead of copy/pasting all of the retry > logic and delays from drm dp helpers, let's just use the helpers and hide > the aksv select as best as we can. > > Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> > Signed-off-by: Sean Paul <seanpaul@chromium.org> > Link: https://patchwork.freedesktop.org/patch/msgid/20191203173638.94919-3-sean@poorly.run #v1 > > Changes in v2: > -Remove 'generate' in intel_dp_aux_generate_xfer_flags, make arg const (Ville) > -Bundle Aksv if statement together (Ville) > -Rename 'txbuf' to 'aksv' (Ville) LGTM Reviewed-by: Ramalingam C <ramalingam.c@intel.com> > --- > drivers/gpu/drm/i915/display/intel_dp.c | 62 ++++++++++++------------- > 1 file changed, 29 insertions(+), 33 deletions(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c > index fe31bbfd6c62..5576193b4fed 100644 > --- a/drivers/gpu/drm/i915/display/intel_dp.c > +++ b/drivers/gpu/drm/i915/display/intel_dp.c > @@ -1515,12 +1515,27 @@ intel_dp_aux_header(u8 txbuf[HEADER_SIZE], > txbuf[3] = msg->size - 1; > } > > +static u32 intel_dp_aux_xfer_flags(const struct drm_dp_aux_msg *msg) > +{ > + /* > + * If we're trying to send the HDCP Aksv, we need to set a the Aksv > + * select bit to inform the hardware to send the Aksv after our header > + * since we can't access that data from software. > + */ > + if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE && > + msg->address == DP_AUX_HDCP_AKSV) > + return DP_AUX_CH_CTL_AUX_AKSV_SELECT; > + > + return 0; > +} > + > static ssize_t > intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) > { > struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux); > u8 txbuf[20], rxbuf[20]; > size_t txsize, rxsize; > + u32 flags = intel_dp_aux_xfer_flags(msg); > int ret; > > intel_dp_aux_header(txbuf, msg); > @@ -1541,7 +1556,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) > memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size); > > ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize, > - rxbuf, rxsize, 0); > + rxbuf, rxsize, flags); > if (ret > 0) { > msg->reply = rxbuf[0] >> 4; > > @@ -1564,7 +1579,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) > return -E2BIG; > > ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize, > - rxbuf, rxsize, 0); > + rxbuf, rxsize, flags); > if (ret > 0) { > msg->reply = rxbuf[0] >> 4; > /* > @@ -5904,17 +5919,9 @@ 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); > - static const struct drm_dp_aux_msg msg = { > - .request = DP_AUX_NATIVE_WRITE, > - .address = DP_AUX_HDCP_AKSV, > - .size = DRM_HDCP_KSV_LEN, > - }; > - u8 txbuf[HEADER_SIZE + DRM_HDCP_KSV_LEN] = {}, rxbuf[2], reply = 0; > + u8 aksv[DRM_HDCP_KSV_LEN] = {}; > 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) { > @@ -5924,29 +5931,18 @@ int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port, > } > > /* > - * 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. > + * 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. > */ > - intel_dp_aux_header(txbuf, &msg); > - > - ret = intel_dp_aux_xfer(intel_dp, txbuf, HEADER_SIZE + msg.size, > - rxbuf, sizeof(rxbuf), > - DP_AUX_CH_CTL_AUX_AKSV_SELECT); > - if (ret < 0) { > - DRM_DEBUG_KMS("Write Aksv over DP/AUX failed (%d)\n", ret); > - return ret; > - } else if (ret == 0) { > - DRM_DEBUG_KMS("Aksv write over DP/AUX was empty\n"); > - return -EIO; > - } > - > - reply = (rxbuf[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK; > - if (reply != DP_AUX_NATIVE_REPLY_ACK) { > - DRM_DEBUG_KMS("Aksv write: no DP_AUX_NATIVE_REPLY_ACK %x\n", > - reply); > - return -EIO; > + 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_DEBUG_KMS("Failed to write Aksv over DP/AUX (%zd)\n", > + dpcd_ret); > + return dpcd_ret >= 0 ? -EIO : dpcd_ret; > } > return 0; > } > -- > Sean Paul, Software Engineer, Google / Chromium OS >
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index fe31bbfd6c62..5576193b4fed 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1515,12 +1515,27 @@ intel_dp_aux_header(u8 txbuf[HEADER_SIZE], txbuf[3] = msg->size - 1; } +static u32 intel_dp_aux_xfer_flags(const struct drm_dp_aux_msg *msg) +{ + /* + * If we're trying to send the HDCP Aksv, we need to set a the Aksv + * select bit to inform the hardware to send the Aksv after our header + * since we can't access that data from software. + */ + if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE && + msg->address == DP_AUX_HDCP_AKSV) + return DP_AUX_CH_CTL_AUX_AKSV_SELECT; + + return 0; +} + static ssize_t intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { struct intel_dp *intel_dp = container_of(aux, struct intel_dp, aux); u8 txbuf[20], rxbuf[20]; size_t txsize, rxsize; + u32 flags = intel_dp_aux_xfer_flags(msg); int ret; intel_dp_aux_header(txbuf, msg); @@ -1541,7 +1556,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size); ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize, - rxbuf, rxsize, 0); + rxbuf, rxsize, flags); if (ret > 0) { msg->reply = rxbuf[0] >> 4; @@ -1564,7 +1579,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) return -E2BIG; ret = intel_dp_aux_xfer(intel_dp, txbuf, txsize, - rxbuf, rxsize, 0); + rxbuf, rxsize, flags); if (ret > 0) { msg->reply = rxbuf[0] >> 4; /* @@ -5904,17 +5919,9 @@ 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); - static const struct drm_dp_aux_msg msg = { - .request = DP_AUX_NATIVE_WRITE, - .address = DP_AUX_HDCP_AKSV, - .size = DRM_HDCP_KSV_LEN, - }; - u8 txbuf[HEADER_SIZE + DRM_HDCP_KSV_LEN] = {}, rxbuf[2], reply = 0; + u8 aksv[DRM_HDCP_KSV_LEN] = {}; 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) { @@ -5924,29 +5931,18 @@ int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port, } /* - * 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. + * 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. */ - intel_dp_aux_header(txbuf, &msg); - - ret = intel_dp_aux_xfer(intel_dp, txbuf, HEADER_SIZE + msg.size, - rxbuf, sizeof(rxbuf), - DP_AUX_CH_CTL_AUX_AKSV_SELECT); - if (ret < 0) { - DRM_DEBUG_KMS("Write Aksv over DP/AUX failed (%d)\n", ret); - return ret; - } else if (ret == 0) { - DRM_DEBUG_KMS("Aksv write over DP/AUX was empty\n"); - return -EIO; - } - - reply = (rxbuf[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK; - if (reply != DP_AUX_NATIVE_REPLY_ACK) { - DRM_DEBUG_KMS("Aksv write: no DP_AUX_NATIVE_REPLY_ACK %x\n", - reply); - return -EIO; + 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_DEBUG_KMS("Failed to write Aksv over DP/AUX (%zd)\n", + dpcd_ret); + return dpcd_ret >= 0 ? -EIO : dpcd_ret; } return 0; }