/* * 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 #include "dm_services.h" #include "dm_helpers.h" #include "include/hdcp_msg_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; struct i2c_payload i2c_payloads[] = { { true, 0, 1, 0 }, /* actual hdcp payload, will be filled later, zeroed for now*/ { 0 } }; if (message_info->msg_id == HDCP_MESSAGE_ID_INVALID) { DC_LOG_ERROR("%s: Invalid message_info msg_id - %d\n", __func__, message_info->msg_id); return false; } offset = hdcp_i2c_offsets[message_info->msg_id]; i2c_payloads[0].data = &offset; 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) { if (message_info->msg_id == HDCP_MESSAGE_ID_INVALID) { DC_LOG_ERROR("%s: Invalid message_info msg_id - %d\n", __func__, message_info->msg_id); return false; } 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; }