Skip to content

Commit

Permalink
drm/i915: Implement the HDCP2.2 support for HDMI
Browse files Browse the repository at this point in the history
Implements the HDMI adaptation specific HDCP2.2 operations.

Basically these are DDC read and write for authenticating through
HDCP2.2 messages.

v2: Rebased.
v3:
  No more special handling of Gmbus burst read for AKE_SEND_CERT.
  Style fixed with few naming. [Uma]
  %s/PARING/PAIRING
v4:
  msg_sz is initialized at definition.
  Lookup table is defined for HDMI HDCP2.2 msgs [Daniel].
v5: Rebased.
v6:
  Make a function as inline [Uma]
  %s/uintxx_t/uxx
v7:
  Errors due to sinks are reported as DEBUG logs.
  Adjust to the new mei interface.
v8:
  ARRAY_SIZE for the # of array members [Jon & Daniel].
  hdcp adaptation is added as a const in the hdcp_shim [Daniel]

Signed-off-by: Ramalingam C <ramalingam.c@intel.com>
Reviewed-by: Uma Shankar <uma.shankar@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/1550338640-17470-15-git-send-email-ramalingam.c@intel.com
  • Loading branch information
Ramalingam C authored and Daniel Vetter committed Feb 20, 2019
1 parent 238d3a9 commit 2d4254e
Showing 1 changed file with 189 additions and 0 deletions.
189 changes: 189 additions & 0 deletions drivers/gpu/drm/i915/intel_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,190 @@ bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
return true;
}

static struct hdcp2_hdmi_msg_data {
u8 msg_id;
u32 timeout;
u32 timeout2;
} hdcp2_msg_data[] = {
{HDCP_2_2_AKE_INIT, 0, 0},
{HDCP_2_2_AKE_SEND_CERT, HDCP_2_2_CERT_TIMEOUT_MS, 0},
{HDCP_2_2_AKE_NO_STORED_KM, 0, 0},
{HDCP_2_2_AKE_STORED_KM, 0, 0},
{HDCP_2_2_AKE_SEND_HPRIME, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS},
{HDCP_2_2_AKE_SEND_PAIRING_INFO, HDCP_2_2_PAIRING_TIMEOUT_MS,
0},
{HDCP_2_2_LC_INIT, 0, 0},
{HDCP_2_2_LC_SEND_LPRIME, HDCP_2_2_HDMI_LPRIME_TIMEOUT_MS, 0},
{HDCP_2_2_SKE_SEND_EKS, 0, 0},
{HDCP_2_2_REP_SEND_RECVID_LIST,
HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0},
{HDCP_2_2_REP_SEND_ACK, 0, 0},
{HDCP_2_2_REP_STREAM_MANAGE, 0, 0},
{HDCP_2_2_REP_STREAM_READY, HDCP_2_2_STREAM_READY_TIMEOUT_MS,
0},
};

static
int intel_hdmi_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
uint8_t *rx_status)
{
return intel_hdmi_hdcp_read(intel_dig_port,
HDCP_2_2_HDMI_REG_RXSTATUS_OFFSET,
rx_status,
HDCP_2_2_HDMI_RXSTATUS_LEN);
}

static int get_hdcp2_msg_timeout(u8 msg_id, bool is_paired)
{
int i;

for (i = 0; i < ARRAY_SIZE(hdcp2_msg_data); i++)
if (hdcp2_msg_data[i].msg_id == msg_id &&
(msg_id != HDCP_2_2_AKE_SEND_HPRIME || is_paired))
return hdcp2_msg_data[i].timeout;
else if (hdcp2_msg_data[i].msg_id == msg_id)
return hdcp2_msg_data[i].timeout2;

return -EINVAL;
}

static inline
int hdcp2_detect_msg_availability(struct intel_digital_port *intel_digital_port,
u8 msg_id, bool *msg_ready,
ssize_t *msg_sz)
{
u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN];
int ret;

ret = intel_hdmi_hdcp2_read_rx_status(intel_digital_port, rx_status);
if (ret < 0) {
DRM_DEBUG_KMS("rx_status read failed. Err %d\n", ret);
return ret;
}

*msg_sz = ((HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(rx_status[1]) << 8) |
rx_status[0]);

if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST)
*msg_ready = (HDCP_2_2_HDMI_RXSTATUS_READY(rx_status[1]) &&
*msg_sz);
else
*msg_ready = *msg_sz;

return 0;
}

static ssize_t
intel_hdmi_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
u8 msg_id, bool paired)
{
bool msg_ready = false;
int timeout, ret;
ssize_t msg_sz = 0;

timeout = get_hdcp2_msg_timeout(msg_id, paired);
if (timeout < 0)
return timeout;

ret = __wait_for(ret = hdcp2_detect_msg_availability(intel_dig_port,
msg_id, &msg_ready,
&msg_sz),
!ret && msg_ready && msg_sz, timeout * 1000,
1000, 5 * 1000);
if (ret)
DRM_DEBUG_KMS("msg_id: %d, ret: %d, timeout: %d\n",
msg_id, ret, timeout);

return ret ? ret : msg_sz;
}

static
int intel_hdmi_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
void *buf, size_t size)
{
unsigned int offset;

offset = HDCP_2_2_HDMI_REG_WR_MSG_OFFSET;
return intel_hdmi_hdcp_write(intel_dig_port, offset, buf, size);
}

static
int intel_hdmi_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
u8 msg_id, void *buf, size_t size)
{
struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
struct intel_hdcp *hdcp = &hdmi->attached_connector->hdcp;
unsigned int offset;
ssize_t ret;

ret = intel_hdmi_hdcp2_wait_for_msg(intel_dig_port, msg_id,
hdcp->is_paired);
if (ret < 0)
return ret;

/*
* Available msg size should be equal to or lesser than the
* available buffer.
*/
if (ret > size) {
DRM_DEBUG_KMS("msg_sz(%zd) is more than exp size(%zu)\n",
ret, size);
return -1;
}

offset = HDCP_2_2_HDMI_REG_RD_MSG_OFFSET;
ret = intel_hdmi_hdcp_read(intel_dig_port, offset, buf, ret);
if (ret)
DRM_DEBUG_KMS("Failed to read msg_id: %d(%zd)\n", msg_id, ret);

return ret;
}

static
int intel_hdmi_hdcp2_check_link(struct intel_digital_port *intel_dig_port)
{
u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN];
int ret;

ret = intel_hdmi_hdcp2_read_rx_status(intel_dig_port, rx_status);
if (ret)
return ret;

/*
* Re-auth request and Link Integrity Failures are represented by
* same bit. i.e reauth_req.
*/
if (HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(rx_status[1]))
ret = HDCP_REAUTH_REQUEST;
else if (HDCP_2_2_HDMI_RXSTATUS_READY(rx_status[1]))
ret = HDCP_TOPOLOGY_CHANGE;

return ret;
}

static
int intel_hdmi_hdcp2_capable(struct intel_digital_port *intel_dig_port,
bool *capable)
{
u8 hdcp2_version;
int ret;

*capable = false;
ret = intel_hdmi_hdcp_read(intel_dig_port, HDCP_2_2_HDMI_REG_VER_OFFSET,
&hdcp2_version, sizeof(hdcp2_version));
if (!ret && hdcp2_version & HDCP_2_2_HDMI_SUPPORT_MASK)
*capable = true;

return ret;
}

static inline
enum hdcp_wired_protocol intel_hdmi_hdcp2_protocol(void)
{
return HDCP_PROTOCOL_HDMI;
}

static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
.write_an_aksv = intel_hdmi_hdcp_write_an_aksv,
.read_bksv = intel_hdmi_hdcp_read_bksv,
Expand All @@ -1140,6 +1324,11 @@ static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
.read_v_prime_part = intel_hdmi_hdcp_read_v_prime_part,
.toggle_signalling = intel_hdmi_hdcp_toggle_signalling,
.check_link = intel_hdmi_hdcp_check_link,
.write_2_2_msg = intel_hdmi_hdcp2_write_msg,
.read_2_2_msg = intel_hdmi_hdcp2_read_msg,
.check_2_2_link = intel_hdmi_hdcp2_check_link,
.hdcp_2_2_capable = intel_hdmi_hdcp2_capable,
.protocol = HDCP_PROTOCOL_HDMI,
};

static void intel_hdmi_prepare(struct intel_encoder *encoder,
Expand Down

0 comments on commit 2d4254e

Please sign in to comment.