mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-11 04:20:06 +00:00
[Why] HDCP locality check has strict timing requirements, currently broken due to reliance on msleep which does not guarantee accuracy. The PR moves the write-poll-read sequence into DMUB using new generic Fused IO interface, where the timing accuracy is greatly improved. New flow is enabled using DCN resource capability bit (none for now), or using a debug flag. [How] * Extended mod_hdcp_config with new function for requesting DMUB to execute a sequence of fused I2C/AUX commands and synchronously wait until an outbox reply arrives or a timeout expires. * If the timeout expires, send an abort to DMUB. * Update HDCP to use the DMUB for locality check if supported. * Add DC_HDCP_LC_FORCE_FW_ENABLE and DC_HDCP_LC_ENABLE_SW_FALLBACK. * Make the first enable new flow regardless of resource capabilities. * Make the second enable fallback to old SW flow. * Clean up makefile source file listings for easier updates. Reviewed-by: Alvin Lee <alvin.lee2@amd.com> Signed-off-by: Dominik Kaszewski <dominik.kaszewski@amd.com> Signed-off-by: Roman Li <roman.li@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
764 lines
23 KiB
C
764 lines
23 KiB
C
/*
|
|
* Copyright 2019 Advanced Micro Devices, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Authors: AMD
|
|
*
|
|
*/
|
|
|
|
#include "hdcp.h"
|
|
|
|
#ifndef MIN
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
#endif
|
|
#define HDCP_I2C_ADDR 0x3a /* 0x74 >> 1*/
|
|
#define KSV_READ_SIZE 0xf /* 0x6803b - 0x6802c */
|
|
#define HDCP_MAX_AUX_TRANSACTION_SIZE 16
|
|
|
|
#define DP_CP_IRQ (1 << 2)
|
|
|
|
enum mod_hdcp_ddc_message_id {
|
|
MOD_HDCP_MESSAGE_ID_INVALID = -1,
|
|
|
|
/* HDCP 1.4 */
|
|
|
|
MOD_HDCP_MESSAGE_ID_READ_BKSV = 0,
|
|
MOD_HDCP_MESSAGE_ID_READ_RI_R0,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_AKSV,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_AINFO,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_AN,
|
|
MOD_HDCP_MESSAGE_ID_READ_VH_X,
|
|
MOD_HDCP_MESSAGE_ID_READ_VH_0,
|
|
MOD_HDCP_MESSAGE_ID_READ_VH_1,
|
|
MOD_HDCP_MESSAGE_ID_READ_VH_2,
|
|
MOD_HDCP_MESSAGE_ID_READ_VH_3,
|
|
MOD_HDCP_MESSAGE_ID_READ_VH_4,
|
|
MOD_HDCP_MESSAGE_ID_READ_BCAPS,
|
|
MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
|
|
MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
|
|
MOD_HDCP_MESSAGE_ID_READ_BINFO,
|
|
|
|
/* HDCP 2.2 */
|
|
|
|
MOD_HDCP_MESSAGE_ID_HDCP2VERSION,
|
|
MOD_HDCP_MESSAGE_ID_RX_CAPS,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
|
|
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
|
|
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
|
|
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
|
|
MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
|
|
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
|
|
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
|
|
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
|
|
MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE,
|
|
|
|
MOD_HDCP_MESSAGE_ID_MAX
|
|
};
|
|
|
|
static const uint8_t hdcp_i2c_offsets[] = {
|
|
[MOD_HDCP_MESSAGE_ID_READ_BKSV] = 0x0,
|
|
[MOD_HDCP_MESSAGE_ID_READ_RI_R0] = 0x8,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKSV] = 0x10,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AINFO] = 0x15,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AN] = 0x18,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_X] = 0x20,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_0] = 0x20,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_1] = 0x24,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_2] = 0x28,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_3] = 0x2C,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_4] = 0x30,
|
|
[MOD_HDCP_MESSAGE_ID_READ_BCAPS] = 0x40,
|
|
[MOD_HDCP_MESSAGE_ID_READ_BSTATUS] = 0x41,
|
|
[MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x43,
|
|
[MOD_HDCP_MESSAGE_ID_READ_BINFO] = 0xFF,
|
|
[MOD_HDCP_MESSAGE_ID_HDCP2VERSION] = 0x50,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x60,
|
|
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x80,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x60,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x60,
|
|
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x80,
|
|
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x80,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x60,
|
|
[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x80,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x60,
|
|
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x80,
|
|
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2] = 0x80,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x60,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x60,
|
|
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x80,
|
|
[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x70,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x0
|
|
};
|
|
|
|
static const uint32_t hdcp_dpcd_addrs[] = {
|
|
[MOD_HDCP_MESSAGE_ID_READ_BKSV] = 0x68000,
|
|
[MOD_HDCP_MESSAGE_ID_READ_RI_R0] = 0x68005,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKSV] = 0x68007,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AINFO] = 0x6803B,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AN] = 0x6800c,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_X] = 0x68014,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_0] = 0x68014,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_1] = 0x68018,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_2] = 0x6801c,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_3] = 0x68020,
|
|
[MOD_HDCP_MESSAGE_ID_READ_VH_4] = 0x68024,
|
|
[MOD_HDCP_MESSAGE_ID_READ_BCAPS] = 0x68028,
|
|
[MOD_HDCP_MESSAGE_ID_READ_BSTATUS] = 0x68029,
|
|
[MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x6802c,
|
|
[MOD_HDCP_MESSAGE_ID_READ_BINFO] = 0x6802a,
|
|
[MOD_HDCP_MESSAGE_ID_RX_CAPS] = 0x6921d,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x69000,
|
|
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x6900b,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x69220,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x692a0,
|
|
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x692c0,
|
|
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x692e0,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x692f0,
|
|
[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x692f8,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x69318,
|
|
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x69330,
|
|
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2] = 0x69340,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x693e0,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x693f0,
|
|
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x69473,
|
|
[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x69493,
|
|
[MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x69494
|
|
};
|
|
|
|
static enum mod_hdcp_status read(struct mod_hdcp *hdcp,
|
|
enum mod_hdcp_ddc_message_id msg_id,
|
|
uint8_t *buf,
|
|
uint32_t buf_len)
|
|
{
|
|
bool success = true;
|
|
uint32_t cur_size = 0;
|
|
uint32_t data_offset = 0;
|
|
|
|
if (msg_id == MOD_HDCP_MESSAGE_ID_INVALID ||
|
|
msg_id >= MOD_HDCP_MESSAGE_ID_MAX)
|
|
return MOD_HDCP_STATUS_DDC_FAILURE;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
int num_dpcd_addrs = ARRAY_SIZE(hdcp_dpcd_addrs);
|
|
if (msg_id >= num_dpcd_addrs)
|
|
return MOD_HDCP_STATUS_DDC_FAILURE;
|
|
|
|
while (buf_len > 0) {
|
|
cur_size = MIN(buf_len, HDCP_MAX_AUX_TRANSACTION_SIZE);
|
|
success = hdcp->config.ddc.funcs.read_dpcd(hdcp->config.ddc.handle,
|
|
hdcp_dpcd_addrs[msg_id] + data_offset,
|
|
buf + data_offset,
|
|
cur_size);
|
|
|
|
if (!success)
|
|
break;
|
|
|
|
buf_len -= cur_size;
|
|
data_offset += cur_size;
|
|
}
|
|
} else {
|
|
int num_i2c_offsets = ARRAY_SIZE(hdcp_i2c_offsets);
|
|
if (msg_id >= num_i2c_offsets)
|
|
return MOD_HDCP_STATUS_DDC_FAILURE;
|
|
|
|
success = hdcp->config.ddc.funcs.read_i2c(
|
|
hdcp->config.ddc.handle,
|
|
HDCP_I2C_ADDR,
|
|
hdcp_i2c_offsets[msg_id],
|
|
buf,
|
|
(uint32_t)buf_len);
|
|
}
|
|
|
|
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
|
|
}
|
|
|
|
static enum mod_hdcp_status read_repeatedly(struct mod_hdcp *hdcp,
|
|
enum mod_hdcp_ddc_message_id msg_id,
|
|
uint8_t *buf,
|
|
uint32_t buf_len,
|
|
uint8_t read_size)
|
|
{
|
|
enum mod_hdcp_status status = MOD_HDCP_STATUS_DDC_FAILURE;
|
|
uint32_t cur_size = 0;
|
|
uint32_t data_offset = 0;
|
|
|
|
while (buf_len > 0) {
|
|
cur_size = MIN(buf_len, read_size);
|
|
status = read(hdcp, msg_id, buf + data_offset, cur_size);
|
|
|
|
if (status != MOD_HDCP_STATUS_SUCCESS)
|
|
break;
|
|
|
|
buf_len -= cur_size;
|
|
data_offset += cur_size;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static enum mod_hdcp_status write(struct mod_hdcp *hdcp,
|
|
enum mod_hdcp_ddc_message_id msg_id,
|
|
uint8_t *buf,
|
|
uint32_t buf_len)
|
|
{
|
|
bool success = true;
|
|
uint32_t cur_size = 0;
|
|
uint32_t data_offset = 0;
|
|
|
|
if (msg_id == MOD_HDCP_MESSAGE_ID_INVALID ||
|
|
msg_id >= MOD_HDCP_MESSAGE_ID_MAX)
|
|
return MOD_HDCP_STATUS_DDC_FAILURE;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
int num_dpcd_addrs = ARRAY_SIZE(hdcp_dpcd_addrs);
|
|
if (msg_id >= num_dpcd_addrs)
|
|
return MOD_HDCP_STATUS_DDC_FAILURE;
|
|
|
|
while (buf_len > 0) {
|
|
cur_size = MIN(buf_len, HDCP_MAX_AUX_TRANSACTION_SIZE);
|
|
success = hdcp->config.ddc.funcs.write_dpcd(
|
|
hdcp->config.ddc.handle,
|
|
hdcp_dpcd_addrs[msg_id] + data_offset,
|
|
buf + data_offset,
|
|
cur_size);
|
|
|
|
if (!success)
|
|
break;
|
|
|
|
buf_len -= cur_size;
|
|
data_offset += cur_size;
|
|
}
|
|
} else {
|
|
int num_i2c_offsets = ARRAY_SIZE(hdcp_i2c_offsets);
|
|
if (msg_id >= num_i2c_offsets)
|
|
return MOD_HDCP_STATUS_DDC_FAILURE;
|
|
|
|
hdcp->buf[0] = hdcp_i2c_offsets[msg_id];
|
|
memmove(&hdcp->buf[1], buf, buf_len);
|
|
success = hdcp->config.ddc.funcs.write_i2c(
|
|
hdcp->config.ddc.handle,
|
|
HDCP_I2C_ADDR,
|
|
hdcp->buf,
|
|
(uint32_t)(buf_len+1));
|
|
}
|
|
|
|
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_bksv(struct mod_hdcp *hdcp)
|
|
{
|
|
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BKSV,
|
|
hdcp->auth.msg.hdcp1.bksv,
|
|
sizeof(hdcp->auth.msg.hdcp1.bksv));
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_bcaps(struct mod_hdcp *hdcp)
|
|
{
|
|
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BCAPS,
|
|
&hdcp->auth.msg.hdcp1.bcaps,
|
|
sizeof(hdcp->auth.msg.hdcp1.bcaps));
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_bstatus(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
|
|
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
|
|
1);
|
|
else
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
|
|
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
|
|
sizeof(hdcp->auth.msg.hdcp1.bstatus));
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_r0p(struct mod_hdcp *hdcp)
|
|
{
|
|
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RI_R0,
|
|
(uint8_t *)&hdcp->auth.msg.hdcp1.r0p,
|
|
sizeof(hdcp->auth.msg.hdcp1.r0p));
|
|
}
|
|
|
|
/* special case, reading repeatedly at the same address, don't use read() */
|
|
enum mod_hdcp_status mod_hdcp_read_ksvlist(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = read_repeatedly(hdcp, MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
|
|
hdcp->auth.msg.hdcp1.ksvlist,
|
|
hdcp->auth.msg.hdcp1.ksvlist_size,
|
|
KSV_READ_SIZE);
|
|
else
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
|
|
(uint8_t *)&hdcp->auth.msg.hdcp1.ksvlist,
|
|
hdcp->auth.msg.hdcp1.ksvlist_size);
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_vp(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_0,
|
|
&hdcp->auth.msg.hdcp1.vp[0], 4);
|
|
if (status != MOD_HDCP_STATUS_SUCCESS)
|
|
goto out;
|
|
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_1,
|
|
&hdcp->auth.msg.hdcp1.vp[4], 4);
|
|
if (status != MOD_HDCP_STATUS_SUCCESS)
|
|
goto out;
|
|
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_2,
|
|
&hdcp->auth.msg.hdcp1.vp[8], 4);
|
|
if (status != MOD_HDCP_STATUS_SUCCESS)
|
|
goto out;
|
|
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_3,
|
|
&hdcp->auth.msg.hdcp1.vp[12], 4);
|
|
if (status != MOD_HDCP_STATUS_SUCCESS)
|
|
goto out;
|
|
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_4,
|
|
&hdcp->auth.msg.hdcp1.vp[16], 4);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_binfo(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BINFO,
|
|
(uint8_t *)&hdcp->auth.msg.hdcp1.binfo_dp,
|
|
sizeof(hdcp->auth.msg.hdcp1.binfo_dp));
|
|
else
|
|
status = MOD_HDCP_STATUS_INVALID_OPERATION;
|
|
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_aksv(struct mod_hdcp *hdcp)
|
|
{
|
|
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKSV,
|
|
hdcp->auth.msg.hdcp1.aksv,
|
|
sizeof(hdcp->auth.msg.hdcp1.aksv));
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_ainfo(struct mod_hdcp *hdcp)
|
|
{
|
|
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AINFO,
|
|
&hdcp->auth.msg.hdcp1.ainfo,
|
|
sizeof(hdcp->auth.msg.hdcp1.ainfo));
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_an(struct mod_hdcp *hdcp)
|
|
{
|
|
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AN,
|
|
hdcp->auth.msg.hdcp1.an,
|
|
sizeof(hdcp->auth.msg.hdcp1.an));
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_hdcp2version(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = MOD_HDCP_STATUS_INVALID_OPERATION;
|
|
else
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_HDCP2VERSION,
|
|
&hdcp->auth.msg.hdcp2.hdcp2version_hdmi,
|
|
sizeof(hdcp->auth.msg.hdcp2.hdcp2version_hdmi));
|
|
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_rxcaps(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (!is_dp_hdcp(hdcp))
|
|
status = MOD_HDCP_STATUS_INVALID_OPERATION;
|
|
else
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_RX_CAPS,
|
|
hdcp->auth.msg.hdcp2.rxcaps_dp,
|
|
sizeof(hdcp->auth.msg.hdcp2.rxcaps_dp));
|
|
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_rxstatus(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
|
|
&hdcp->auth.msg.hdcp2.rxstatus_dp,
|
|
1);
|
|
} else {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
|
|
(uint8_t *)&hdcp->auth.msg.hdcp2.rxstatus,
|
|
sizeof(hdcp->auth.msg.hdcp2.rxstatus));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_ake_cert(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
hdcp->auth.msg.hdcp2.ake_cert[0] = HDCP_2_2_AKE_SEND_CERT;
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
|
|
hdcp->auth.msg.hdcp2.ake_cert+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_cert)-1);
|
|
|
|
} else {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
|
|
hdcp->auth.msg.hdcp2.ake_cert,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_cert));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_h_prime(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
hdcp->auth.msg.hdcp2.ake_h_prime[0] = HDCP_2_2_AKE_SEND_HPRIME;
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
|
|
hdcp->auth.msg.hdcp2.ake_h_prime+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)-1);
|
|
|
|
} else {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
|
|
hdcp->auth.msg.hdcp2.ake_h_prime,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_pairing_info(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
hdcp->auth.msg.hdcp2.ake_pairing_info[0] = HDCP_2_2_AKE_SEND_PAIRING_INFO;
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
|
|
hdcp->auth.msg.hdcp2.ake_pairing_info+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info)-1);
|
|
|
|
} else {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
|
|
hdcp->auth.msg.hdcp2.ake_pairing_info,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_l_prime(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
hdcp->auth.msg.hdcp2.lc_l_prime[0] = HDCP_2_2_LC_SEND_LPRIME;
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
|
|
hdcp->auth.msg.hdcp2.lc_l_prime+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime)-1);
|
|
|
|
} else {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
|
|
hdcp->auth.msg.hdcp2.lc_l_prime,
|
|
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
uint32_t device_count = 0;
|
|
uint32_t rx_id_list_size = 0;
|
|
uint32_t bytes_read = 0;
|
|
|
|
hdcp->auth.msg.hdcp2.rx_id_list[0] = HDCP_2_2_REP_SEND_RECVID_LIST;
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
|
|
hdcp->auth.msg.hdcp2.rx_id_list+1,
|
|
HDCP_MAX_AUX_TRANSACTION_SIZE);
|
|
if (status == MOD_HDCP_STATUS_SUCCESS) {
|
|
bytes_read = HDCP_MAX_AUX_TRANSACTION_SIZE;
|
|
device_count = HDCP_2_2_DEV_COUNT_LO(hdcp->auth.msg.hdcp2.rx_id_list[2]) +
|
|
(HDCP_2_2_DEV_COUNT_HI(hdcp->auth.msg.hdcp2.rx_id_list[1]) << 4);
|
|
rx_id_list_size = MIN((21 + 5 * device_count),
|
|
(sizeof(hdcp->auth.msg.hdcp2.rx_id_list) - 1));
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2,
|
|
hdcp->auth.msg.hdcp2.rx_id_list + 1 + bytes_read,
|
|
(rx_id_list_size - 1) / HDCP_MAX_AUX_TRANSACTION_SIZE * HDCP_MAX_AUX_TRANSACTION_SIZE);
|
|
}
|
|
} else {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
|
|
hdcp->auth.msg.hdcp2.rx_id_list,
|
|
hdcp->auth.msg.hdcp2.rx_id_list_size);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_read_stream_ready(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready[0] = HDCP_2_2_REP_STREAM_READY;
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
|
|
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready)-1);
|
|
|
|
} else {
|
|
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
|
|
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready,
|
|
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready));
|
|
}
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_ake_init(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
|
|
hdcp->auth.msg.hdcp2.ake_init+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_init)-1);
|
|
else
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
|
|
hdcp->auth.msg.hdcp2.ake_init,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_init));
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_no_stored_km(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
|
|
hdcp->auth.msg.hdcp2.ake_no_stored_km+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km)-1);
|
|
else
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
|
|
hdcp->auth.msg.hdcp2.ake_no_stored_km,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km));
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_stored_km(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
|
|
hdcp->auth.msg.hdcp2.ake_stored_km+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km)-1);
|
|
else
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
|
|
hdcp->auth.msg.hdcp2.ake_stored_km,
|
|
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km));
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_lc_init(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
|
|
hdcp->auth.msg.hdcp2.lc_init+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.lc_init)-1);
|
|
else
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
|
|
hdcp->auth.msg.hdcp2.lc_init,
|
|
sizeof(hdcp->auth.msg.hdcp2.lc_init));
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_eks(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
|
|
hdcp->auth.msg.hdcp2.ske_eks+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.ske_eks)-1);
|
|
else
|
|
status = write(hdcp,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
|
|
hdcp->auth.msg.hdcp2.ske_eks,
|
|
sizeof(hdcp->auth.msg.hdcp2.ske_eks));
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_repeater_auth_ack(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
|
|
hdcp->auth.msg.hdcp2.repeater_auth_ack+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack)-1);
|
|
else
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
|
|
hdcp->auth.msg.hdcp2.repeater_auth_ack,
|
|
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack));
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_stream_manage(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
|
|
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage+1,
|
|
hdcp->auth.msg.hdcp2.stream_manage_size-1);
|
|
else
|
|
status = write(hdcp,
|
|
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
|
|
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage,
|
|
hdcp->auth.msg.hdcp2.stream_manage_size);
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_content_type(struct mod_hdcp *hdcp)
|
|
{
|
|
enum mod_hdcp_status status;
|
|
|
|
if (is_dp_hdcp(hdcp))
|
|
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE,
|
|
hdcp->auth.msg.hdcp2.content_stream_type_dp+1,
|
|
sizeof(hdcp->auth.msg.hdcp2.content_stream_type_dp)-1);
|
|
else
|
|
status = MOD_HDCP_STATUS_INVALID_OPERATION;
|
|
return status;
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_clear_cp_irq_status(struct mod_hdcp *hdcp)
|
|
{
|
|
uint8_t clear_cp_irq_bit = DP_CP_IRQ;
|
|
uint32_t size = 1;
|
|
|
|
if (is_dp_hdcp(hdcp)) {
|
|
uint32_t cp_irq_addrs = (hdcp->connection.link.dp.rev >= 0x14)
|
|
? DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0:DP_DEVICE_SERVICE_IRQ_VECTOR;
|
|
return hdcp->config.ddc.funcs.write_dpcd(hdcp->config.ddc.handle, cp_irq_addrs,
|
|
&clear_cp_irq_bit, size) ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
|
|
}
|
|
|
|
return MOD_HDCP_STATUS_INVALID_OPERATION;
|
|
}
|
|
|
|
static bool write_stall_read_lc_fw_aux(struct mod_hdcp *hdcp)
|
|
{
|
|
struct mod_hdcp_message_hdcp2 *hdcp2 = &hdcp->auth.msg.hdcp2;
|
|
|
|
struct mod_hdcp_atomic_op_aux write = {
|
|
hdcp_dpcd_addrs[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT],
|
|
hdcp2->lc_init + 1,
|
|
sizeof(hdcp2->lc_init) - 1,
|
|
};
|
|
struct mod_hdcp_atomic_op_aux stall = { 0, NULL, 0, };
|
|
struct mod_hdcp_atomic_op_aux read = {
|
|
hdcp_dpcd_addrs[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME],
|
|
hdcp2->lc_l_prime + 1,
|
|
sizeof(hdcp2->lc_l_prime) - 1,
|
|
};
|
|
|
|
hdcp2->lc_l_prime[0] = HDCP_2_2_LC_SEND_LPRIME;
|
|
|
|
return hdcp->config.ddc.funcs.atomic_write_poll_read_aux(
|
|
hdcp->config.ddc.handle,
|
|
&write,
|
|
&stall,
|
|
&read,
|
|
16 * 1000,
|
|
0
|
|
);
|
|
}
|
|
|
|
static bool write_poll_read_lc_fw_i2c(struct mod_hdcp *hdcp)
|
|
{
|
|
struct mod_hdcp_message_hdcp2 *hdcp2 = &hdcp->auth.msg.hdcp2;
|
|
uint8_t expected_rxstatus[2] = { sizeof(hdcp2->lc_l_prime) };
|
|
|
|
hdcp->buf[0] = hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT];
|
|
memmove(&hdcp->buf[1], hdcp2->lc_init, sizeof(hdcp2->lc_init));
|
|
|
|
struct mod_hdcp_atomic_op_i2c write = {
|
|
HDCP_I2C_ADDR,
|
|
0,
|
|
hdcp->buf,
|
|
sizeof(hdcp2->lc_init) + 1,
|
|
};
|
|
struct mod_hdcp_atomic_op_i2c poll = {
|
|
HDCP_I2C_ADDR,
|
|
hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS],
|
|
expected_rxstatus,
|
|
sizeof(expected_rxstatus),
|
|
};
|
|
struct mod_hdcp_atomic_op_i2c read = {
|
|
HDCP_I2C_ADDR,
|
|
hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME],
|
|
hdcp2->lc_l_prime,
|
|
sizeof(hdcp2->lc_l_prime),
|
|
};
|
|
|
|
return hdcp->config.ddc.funcs.atomic_write_poll_read_i2c(
|
|
hdcp->config.ddc.handle,
|
|
&write,
|
|
&poll,
|
|
&read,
|
|
20 * 1000,
|
|
6
|
|
);
|
|
}
|
|
|
|
enum mod_hdcp_status mod_hdcp_write_poll_read_lc_fw(struct mod_hdcp *hdcp)
|
|
{
|
|
const bool success = (is_dp_hdcp(hdcp) ? write_stall_read_lc_fw_aux : write_poll_read_lc_fw_i2c)(hdcp);
|
|
|
|
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
|
|
}
|
|
|