415 lines
12 KiB
C
415 lines
12 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 <linux/slab.h>
|
|
|
|
#include "dm_services.h"
|
|
#include "dm_helpers.h"
|
|
#include "include/hdcp_types.h"
|
|
#include "include/signal_types.h"
|
|
#include "core_types.h"
|
|
#include "link.h"
|
|
#include "link_hwss.h"
|
|
#include "link/protocols/link_dpcd.h"
|
|
|
|
#define DC_LOGGER \
|
|
link->ctx->logger
|
|
#define HDCP14_KSV_SIZE 5
|
|
#define HDCP14_MAX_KSV_FIFO_SIZE 127*HDCP14_KSV_SIZE
|
|
|
|
static const bool hdcp_cmd_is_read[HDCP_MESSAGE_ID_MAX] = {
|
|
[HDCP_MESSAGE_ID_READ_BKSV] = true,
|
|
[HDCP_MESSAGE_ID_READ_RI_R0] = true,
|
|
[HDCP_MESSAGE_ID_READ_PJ] = true,
|
|
[HDCP_MESSAGE_ID_WRITE_AKSV] = false,
|
|
[HDCP_MESSAGE_ID_WRITE_AINFO] = false,
|
|
[HDCP_MESSAGE_ID_WRITE_AN] = false,
|
|
[HDCP_MESSAGE_ID_READ_VH_X] = true,
|
|
[HDCP_MESSAGE_ID_READ_VH_0] = true,
|
|
[HDCP_MESSAGE_ID_READ_VH_1] = true,
|
|
[HDCP_MESSAGE_ID_READ_VH_2] = true,
|
|
[HDCP_MESSAGE_ID_READ_VH_3] = true,
|
|
[HDCP_MESSAGE_ID_READ_VH_4] = true,
|
|
[HDCP_MESSAGE_ID_READ_BCAPS] = true,
|
|
[HDCP_MESSAGE_ID_READ_BSTATUS] = true,
|
|
[HDCP_MESSAGE_ID_READ_KSV_FIFO] = true,
|
|
[HDCP_MESSAGE_ID_READ_BINFO] = true,
|
|
[HDCP_MESSAGE_ID_HDCP2VERSION] = true,
|
|
[HDCP_MESSAGE_ID_RX_CAPS] = true,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_INIT] = false,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = true,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = false,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = false,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = true,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = true,
|
|
[HDCP_MESSAGE_ID_WRITE_LC_INIT] = false,
|
|
[HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = true,
|
|
[HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = false,
|
|
[HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = true,
|
|
[HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = false,
|
|
[HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = false,
|
|
[HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = true,
|
|
[HDCP_MESSAGE_ID_READ_RXSTATUS] = true,
|
|
[HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = false
|
|
};
|
|
|
|
static const uint8_t hdcp_i2c_offsets[HDCP_MESSAGE_ID_MAX] = {
|
|
[HDCP_MESSAGE_ID_READ_BKSV] = 0x0,
|
|
[HDCP_MESSAGE_ID_READ_RI_R0] = 0x8,
|
|
[HDCP_MESSAGE_ID_READ_PJ] = 0xA,
|
|
[HDCP_MESSAGE_ID_WRITE_AKSV] = 0x10,
|
|
[HDCP_MESSAGE_ID_WRITE_AINFO] = 0x15,
|
|
[HDCP_MESSAGE_ID_WRITE_AN] = 0x18,
|
|
[HDCP_MESSAGE_ID_READ_VH_X] = 0x20,
|
|
[HDCP_MESSAGE_ID_READ_VH_0] = 0x20,
|
|
[HDCP_MESSAGE_ID_READ_VH_1] = 0x24,
|
|
[HDCP_MESSAGE_ID_READ_VH_2] = 0x28,
|
|
[HDCP_MESSAGE_ID_READ_VH_3] = 0x2C,
|
|
[HDCP_MESSAGE_ID_READ_VH_4] = 0x30,
|
|
[HDCP_MESSAGE_ID_READ_BCAPS] = 0x40,
|
|
[HDCP_MESSAGE_ID_READ_BSTATUS] = 0x41,
|
|
[HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x43,
|
|
[HDCP_MESSAGE_ID_READ_BINFO] = 0xFF,
|
|
[HDCP_MESSAGE_ID_HDCP2VERSION] = 0x50,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x60,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x80,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x60,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x60,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x80,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x80,
|
|
[HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x60,
|
|
[HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x80,
|
|
[HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x60,
|
|
[HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x80,
|
|
[HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x60,
|
|
[HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x60,
|
|
[HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x80,
|
|
[HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x70,
|
|
[HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x0,
|
|
};
|
|
|
|
struct protection_properties {
|
|
bool supported;
|
|
bool (*process_transaction)(
|
|
struct dc_link *link,
|
|
struct hdcp_protection_message *message_info);
|
|
};
|
|
|
|
static const struct protection_properties non_supported_protection = {
|
|
.supported = false
|
|
};
|
|
|
|
static bool hdmi_14_process_transaction(
|
|
struct dc_link *link,
|
|
struct hdcp_protection_message *message_info)
|
|
{
|
|
uint8_t *buff = NULL;
|
|
bool result;
|
|
const uint8_t hdcp_i2c_addr_link_primary = 0x3a; /* 0x74 >> 1*/
|
|
const uint8_t hdcp_i2c_addr_link_secondary = 0x3b; /* 0x76 >> 1*/
|
|
struct i2c_command i2c_command;
|
|
uint8_t offset = hdcp_i2c_offsets[message_info->msg_id];
|
|
struct i2c_payload i2c_payloads[] = {
|
|
{ true, 0, 1, &offset },
|
|
/* actual hdcp payload, will be filled later, zeroed for now*/
|
|
{ 0 }
|
|
};
|
|
|
|
switch (message_info->link) {
|
|
case HDCP_LINK_SECONDARY:
|
|
i2c_payloads[0].address = hdcp_i2c_addr_link_secondary;
|
|
i2c_payloads[1].address = hdcp_i2c_addr_link_secondary;
|
|
break;
|
|
case HDCP_LINK_PRIMARY:
|
|
default:
|
|
i2c_payloads[0].address = hdcp_i2c_addr_link_primary;
|
|
i2c_payloads[1].address = hdcp_i2c_addr_link_primary;
|
|
break;
|
|
}
|
|
|
|
if (hdcp_cmd_is_read[message_info->msg_id]) {
|
|
i2c_payloads[1].write = false;
|
|
i2c_command.number_of_payloads = ARRAY_SIZE(i2c_payloads);
|
|
i2c_payloads[1].length = message_info->length;
|
|
i2c_payloads[1].data = message_info->data;
|
|
} else {
|
|
i2c_command.number_of_payloads = 1;
|
|
buff = kzalloc(message_info->length + 1, GFP_KERNEL);
|
|
|
|
if (!buff)
|
|
return false;
|
|
|
|
buff[0] = offset;
|
|
memmove(&buff[1], message_info->data, message_info->length);
|
|
i2c_payloads[0].length = message_info->length + 1;
|
|
i2c_payloads[0].data = buff;
|
|
}
|
|
|
|
i2c_command.payloads = i2c_payloads;
|
|
i2c_command.engine = I2C_COMMAND_ENGINE_HW;//only HW
|
|
i2c_command.speed = link->ddc->ctx->dc->caps.i2c_speed_in_khz;
|
|
|
|
result = dm_helpers_submit_i2c(
|
|
link->ctx,
|
|
link,
|
|
&i2c_command);
|
|
kfree(buff);
|
|
|
|
return result;
|
|
}
|
|
|
|
static const struct protection_properties hdmi_14_protection = {
|
|
.supported = true,
|
|
.process_transaction = hdmi_14_process_transaction
|
|
};
|
|
|
|
static const uint32_t hdcp_dpcd_addrs[HDCP_MESSAGE_ID_MAX] = {
|
|
[HDCP_MESSAGE_ID_READ_BKSV] = 0x68000,
|
|
[HDCP_MESSAGE_ID_READ_RI_R0] = 0x68005,
|
|
[HDCP_MESSAGE_ID_READ_PJ] = 0xFFFFFFFF,
|
|
[HDCP_MESSAGE_ID_WRITE_AKSV] = 0x68007,
|
|
[HDCP_MESSAGE_ID_WRITE_AINFO] = 0x6803B,
|
|
[HDCP_MESSAGE_ID_WRITE_AN] = 0x6800c,
|
|
[HDCP_MESSAGE_ID_READ_VH_X] = 0x68014,
|
|
[HDCP_MESSAGE_ID_READ_VH_0] = 0x68014,
|
|
[HDCP_MESSAGE_ID_READ_VH_1] = 0x68018,
|
|
[HDCP_MESSAGE_ID_READ_VH_2] = 0x6801c,
|
|
[HDCP_MESSAGE_ID_READ_VH_3] = 0x68020,
|
|
[HDCP_MESSAGE_ID_READ_VH_4] = 0x68024,
|
|
[HDCP_MESSAGE_ID_READ_BCAPS] = 0x68028,
|
|
[HDCP_MESSAGE_ID_READ_BSTATUS] = 0x68029,
|
|
[HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x6802c,
|
|
[HDCP_MESSAGE_ID_READ_BINFO] = 0x6802a,
|
|
[HDCP_MESSAGE_ID_RX_CAPS] = 0x6921d,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x69000,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x6900b,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x69220,
|
|
[HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x692a0,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x692c0,
|
|
[HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x692e0,
|
|
[HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x692f0,
|
|
[HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x692f8,
|
|
[HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x69318,
|
|
[HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x69330,
|
|
[HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x693e0,
|
|
[HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x693f0,
|
|
[HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x69473,
|
|
[HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x69493,
|
|
[HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x69494
|
|
};
|
|
|
|
static bool dpcd_access_helper(
|
|
struct dc_link *link,
|
|
uint32_t length,
|
|
uint8_t *data,
|
|
uint32_t dpcd_addr,
|
|
bool is_read)
|
|
{
|
|
enum dc_status status;
|
|
uint32_t cur_length = 0;
|
|
uint32_t offset = 0;
|
|
uint32_t ksv_read_size = 0x6803b - 0x6802c;
|
|
|
|
/* Read KSV, need repeatedly handle */
|
|
if (dpcd_addr == 0x6802c) {
|
|
if (length % HDCP14_KSV_SIZE) {
|
|
DC_LOG_ERROR("%s: KsvFifo Size(%d) is not a multiple of HDCP14_KSV_SIZE(%d)\n",
|
|
__func__,
|
|
length,
|
|
HDCP14_KSV_SIZE);
|
|
}
|
|
if (length > HDCP14_MAX_KSV_FIFO_SIZE) {
|
|
DC_LOG_ERROR("%s: KsvFifo Size(%d) is greater than HDCP14_MAX_KSV_FIFO_SIZE(%d)\n",
|
|
__func__,
|
|
length,
|
|
HDCP14_MAX_KSV_FIFO_SIZE);
|
|
}
|
|
|
|
DC_LOG_ERROR("%s: Reading %d Ksv(s) from KsvFifo\n",
|
|
__func__,
|
|
length / HDCP14_KSV_SIZE);
|
|
|
|
while (length > 0) {
|
|
if (length > ksv_read_size) {
|
|
status = core_link_read_dpcd(
|
|
link,
|
|
dpcd_addr + offset,
|
|
data + offset,
|
|
ksv_read_size);
|
|
|
|
data += ksv_read_size;
|
|
length -= ksv_read_size;
|
|
} else {
|
|
status = core_link_read_dpcd(
|
|
link,
|
|
dpcd_addr + offset,
|
|
data + offset,
|
|
length);
|
|
|
|
data += length;
|
|
length = 0;
|
|
}
|
|
|
|
if (status != DC_OK)
|
|
return false;
|
|
}
|
|
} else {
|
|
while (length > 0) {
|
|
if (length > DEFAULT_AUX_MAX_DATA_SIZE)
|
|
cur_length = DEFAULT_AUX_MAX_DATA_SIZE;
|
|
else
|
|
cur_length = length;
|
|
|
|
if (is_read) {
|
|
status = core_link_read_dpcd(
|
|
link,
|
|
dpcd_addr + offset,
|
|
data + offset,
|
|
cur_length);
|
|
} else {
|
|
status = core_link_write_dpcd(
|
|
link,
|
|
dpcd_addr + offset,
|
|
data + offset,
|
|
cur_length);
|
|
}
|
|
|
|
if (status != DC_OK)
|
|
return false;
|
|
|
|
length -= cur_length;
|
|
offset += cur_length;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool dp_11_process_transaction(
|
|
struct dc_link *link,
|
|
struct hdcp_protection_message *message_info)
|
|
{
|
|
return dpcd_access_helper(
|
|
link,
|
|
message_info->length,
|
|
message_info->data,
|
|
hdcp_dpcd_addrs[message_info->msg_id],
|
|
hdcp_cmd_is_read[message_info->msg_id]);
|
|
}
|
|
|
|
static const struct protection_properties dp_11_protection = {
|
|
.supported = true,
|
|
.process_transaction = dp_11_process_transaction
|
|
};
|
|
|
|
static const struct protection_properties *get_protection_properties_by_signal(
|
|
struct dc_link *link,
|
|
enum signal_type st,
|
|
enum hdcp_version version)
|
|
{
|
|
switch (version) {
|
|
case HDCP_VERSION_14:
|
|
switch (st) {
|
|
case SIGNAL_TYPE_DVI_SINGLE_LINK:
|
|
case SIGNAL_TYPE_DVI_DUAL_LINK:
|
|
case SIGNAL_TYPE_HDMI_TYPE_A:
|
|
return &hdmi_14_protection;
|
|
case SIGNAL_TYPE_DISPLAY_PORT:
|
|
if (link &&
|
|
(link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER ||
|
|
link->dpcd_caps.dongle_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER)) {
|
|
return &non_supported_protection;
|
|
}
|
|
return &dp_11_protection;
|
|
case SIGNAL_TYPE_DISPLAY_PORT_MST:
|
|
case SIGNAL_TYPE_EDP:
|
|
return &dp_11_protection;
|
|
default:
|
|
return &non_supported_protection;
|
|
}
|
|
break;
|
|
case HDCP_VERSION_22:
|
|
switch (st) {
|
|
case SIGNAL_TYPE_DVI_SINGLE_LINK:
|
|
case SIGNAL_TYPE_DVI_DUAL_LINK:
|
|
case SIGNAL_TYPE_HDMI_TYPE_A:
|
|
return &hdmi_14_protection; //todo version2.2
|
|
case SIGNAL_TYPE_DISPLAY_PORT:
|
|
case SIGNAL_TYPE_DISPLAY_PORT_MST:
|
|
case SIGNAL_TYPE_EDP:
|
|
return &dp_11_protection; //todo version2.2
|
|
default:
|
|
return &non_supported_protection;
|
|
}
|
|
break;
|
|
default:
|
|
return &non_supported_protection;
|
|
}
|
|
}
|
|
|
|
enum hdcp_message_status dc_process_hdcp_msg(
|
|
enum signal_type signal,
|
|
struct dc_link *link,
|
|
struct hdcp_protection_message *message_info)
|
|
{
|
|
enum hdcp_message_status status = HDCP_MESSAGE_FAILURE;
|
|
uint32_t i = 0;
|
|
|
|
const struct protection_properties *protection_props;
|
|
|
|
if (!message_info)
|
|
return HDCP_MESSAGE_UNSUPPORTED;
|
|
|
|
if (message_info->msg_id < HDCP_MESSAGE_ID_READ_BKSV ||
|
|
message_info->msg_id >= HDCP_MESSAGE_ID_MAX)
|
|
return HDCP_MESSAGE_UNSUPPORTED;
|
|
|
|
protection_props =
|
|
get_protection_properties_by_signal(
|
|
link,
|
|
signal,
|
|
message_info->version);
|
|
|
|
if (!protection_props->supported)
|
|
return HDCP_MESSAGE_UNSUPPORTED;
|
|
|
|
if (protection_props->process_transaction(
|
|
link,
|
|
message_info)) {
|
|
status = HDCP_MESSAGE_SUCCESS;
|
|
} else {
|
|
for (i = 0; i < message_info->max_retries; i++) {
|
|
if (protection_props->process_transaction(
|
|
link,
|
|
message_info)) {
|
|
status = HDCP_MESSAGE_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|