diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3860-drm-amd-display-Add-HDCP-module.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3860-drm-amd-display-Add-HDCP-module.patch | 3268 |
1 files changed, 3268 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3860-drm-amd-display-Add-HDCP-module.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3860-drm-amd-display-Add-HDCP-module.patch new file mode 100644 index 00000000..64b7c305 --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.19.8/3860-drm-amd-display-Add-HDCP-module.patch @@ -0,0 +1,3268 @@ +From 534836296531de1667b952c884571e7939299587 Mon Sep 17 00:00:00 2001 +From: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com> +Date: Tue, 6 Aug 2019 17:52:01 -0400 +Subject: [PATCH 3860/4256] drm/amd/display: Add HDCP module + +This module manages HDCP for amdgpu driver. The module behaves as a +state machine which handles the different authentication states of HDCP + +The module is divided into 3 major components ++--------+ +| Hdcp.c | ++--------+ +Manages the state machine, sends the events to be executed and communicates +with the dm + ++-----------+ +|Execution.c| ++-----------+ +This executes events based on the current state. Also generates +execution results as transition inputs + ++------------+ +|Transition.c| ++------------+ +Decides the next state based on the input and makes requests to +hdcp.c to handle. + +-------------+ + ------> | Execution.c | ------ + | +-------------+ | + | V ++----+ +--------+ +--------------+ +| DM | -----> | Hdcp.c | <------------ | Transition.c | ++----+ <----- +--------+ +--------------+ + +v2: Drop unused function definitions + +Signed-off-by: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com> +Signed-off-by: Wenjing Liu <Wenjing.Liu@amd.com> +Reviewed-by: Wenjing Liu <Wenjing.Liu@amd.com> +Acked-by: Harry Wentland <harry.wentland@amd.com> +Signed-off-by: Alex Deucher <alexander.deucher@amd.com> +--- + drivers/gpu/drm/amd/display/Makefile | 7 + + drivers/gpu/drm/amd/display/dc/Makefile | 4 + + drivers/gpu/drm/amd/display/dc/hdcp/Makefile | 28 + + .../gpu/drm/amd/display/dc/hdcp/hdcp_msg.c | 326 +++++++++++ + .../gpu/drm/amd/display/include/hdcp_types.h | 96 ++++ + .../gpu/drm/amd/display/modules/hdcp/Makefile | 32 ++ + .../gpu/drm/amd/display/modules/hdcp/hdcp.c | 426 ++++++++++++++ + .../gpu/drm/amd/display/modules/hdcp/hdcp.h | 442 +++++++++++++++ + .../display/modules/hdcp/hdcp1_execution.c | 531 ++++++++++++++++++ + .../display/modules/hdcp/hdcp1_transition.c | 307 ++++++++++ + .../drm/amd/display/modules/hdcp/hdcp_ddc.c | 305 ++++++++++ + .../drm/amd/display/modules/hdcp/hdcp_log.c | 163 ++++++ + .../drm/amd/display/modules/hdcp/hdcp_log.h | 139 +++++ + .../drm/amd/display/modules/inc/mod_hdcp.h | 289 ++++++++++ + 14 files changed, 3095 insertions(+) + create mode 100644 drivers/gpu/drm/amd/display/dc/hdcp/Makefile + create mode 100644 drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c + create mode 100644 drivers/gpu/drm/amd/display/include/hdcp_types.h + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/Makefile + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_transition.c + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c + create mode 100644 drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h + create mode 100644 drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h + +diff --git a/drivers/gpu/drm/amd/display/Makefile b/drivers/gpu/drm/amd/display/Makefile +index 496cee000f10..36b3d6a5d04d 100644 +--- a/drivers/gpu/drm/amd/display/Makefile ++++ b/drivers/gpu/drm/amd/display/Makefile +@@ -34,12 +34,19 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/freesync + subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/color + subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/info_packet + subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/power ++ifdef CONFIG_DRM_AMD_DC_HDCP ++subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/modules/hdcp ++endif + + #TODO: remove when Timing Sync feature is complete + subdir-ccflags-y += -DBUILD_FEATURE_TIMING_SYNC=0 + + DAL_LIBS = amdgpu_dm dc modules/freesync modules/color modules/info_packet modules/power + ++ifdef CONFIG_DRM_AMD_DC_HDCP ++DAL_LIBS += modules/hdcp ++endif ++ + AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/,$(DAL_LIBS))) + + include $(AMD_DAL) +diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile +index 627982cb15d2..a160512a2f04 100644 +--- a/drivers/gpu/drm/amd/display/dc/Makefile ++++ b/drivers/gpu/drm/amd/display/dc/Makefile +@@ -48,6 +48,10 @@ DC_LIBS += dce110 + DC_LIBS += dce100 + DC_LIBS += dce80 + ++ifdef CONFIG_DRM_AMD_DC_HDCP ++DC_LIBS += hdcp ++endif ++ + AMD_DC = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/dc/,$(DC_LIBS))) + + include $(AMD_DC) +diff --git a/drivers/gpu/drm/amd/display/dc/hdcp/Makefile b/drivers/gpu/drm/amd/display/dc/hdcp/Makefile +new file mode 100644 +index 000000000000..4170b6eb9ec0 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dc/hdcp/Makefile +@@ -0,0 +1,28 @@ ++# 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. ++# ++# Makefile for the 'hdcp' sub-component of DAL. ++# ++ ++HDCP_MSG = hdcp_msg.o ++ ++AMD_DAL_HDCP_MSG = $(addprefix $(AMDDALPATH)/dc/hdcp/,$(HDCP_MSG)) ++ ++AMD_DISPLAY_FILES += $(AMD_DAL_HDCP_MSG) +diff --git a/drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c b/drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c +new file mode 100644 +index 000000000000..cf6ef387e5d2 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c +@@ -0,0 +1,326 @@ ++/* ++ * 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/i2caux_interface.h" ++#include "include/signal_types.h" ++#include "core_types.h" ++#include "dc_link_ddc.h" ++#include "link_hwss.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_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_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 ++}; ++ ++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); ++ ++ if (buff) ++ 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_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 ++}; ++ +diff --git a/drivers/gpu/drm/amd/display/include/hdcp_types.h b/drivers/gpu/drm/amd/display/include/hdcp_types.h +new file mode 100644 +index 000000000000..f31e6befc8d6 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/include/hdcp_types.h +@@ -0,0 +1,96 @@ ++/* ++* 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 ++* ++*/ ++ ++#ifndef __DC_HDCP_TYPES_H__ ++#define __DC_HDCP_TYPES_H__ ++ ++enum hdcp_message_id { ++ HDCP_MESSAGE_ID_INVALID = -1, ++ ++ /* HDCP 1.4 */ ++ ++ HDCP_MESSAGE_ID_READ_BKSV = 0, ++ /* HDMI is called Ri', DP is called R0' */ ++ HDCP_MESSAGE_ID_READ_RI_R0, ++ HDCP_MESSAGE_ID_READ_PJ, ++ HDCP_MESSAGE_ID_WRITE_AKSV, ++ HDCP_MESSAGE_ID_WRITE_AINFO, ++ HDCP_MESSAGE_ID_WRITE_AN, ++ HDCP_MESSAGE_ID_READ_VH_X, ++ HDCP_MESSAGE_ID_READ_VH_0, ++ HDCP_MESSAGE_ID_READ_VH_1, ++ HDCP_MESSAGE_ID_READ_VH_2, ++ HDCP_MESSAGE_ID_READ_VH_3, ++ HDCP_MESSAGE_ID_READ_VH_4, ++ HDCP_MESSAGE_ID_READ_BCAPS, ++ HDCP_MESSAGE_ID_READ_BSTATUS, ++ HDCP_MESSAGE_ID_READ_KSV_FIFO, ++ HDCP_MESSAGE_ID_READ_BINFO, ++ ++ /* HDCP 2.2 */ ++ ++ HDCP_MESSAGE_ID_HDCP2VERSION, ++ HDCP_MESSAGE_ID_RX_CAPS, ++ HDCP_MESSAGE_ID_WRITE_AKE_INIT, ++ HDCP_MESSAGE_ID_READ_AKE_SEND_CERT, ++ HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM, ++ HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM, ++ HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME, ++ HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO, ++ HDCP_MESSAGE_ID_WRITE_LC_INIT, ++ HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME, ++ HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS, ++ HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST, ++ HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK, ++ HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE, ++ HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY, ++ HDCP_MESSAGE_ID_READ_RXSTATUS, ++ HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE, ++ ++ HDCP_MESSAGE_ID_MAX ++}; ++ ++enum hdcp_version { ++ HDCP_Unknown = 0, ++ HDCP_VERSION_14, ++ HDCP_VERSION_22, ++}; ++ ++enum hdcp_link { ++ HDCP_LINK_PRIMARY, ++ HDCP_LINK_SECONDARY ++}; ++ ++struct hdcp_protection_message { ++ enum hdcp_version version; ++ /* relevant only for DVI */ ++ enum hdcp_link link; ++ enum hdcp_message_id msg_id; ++ uint32_t length; ++ uint8_t max_retries; ++ uint8_t *data; ++}; ++ ++#endif +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/Makefile b/drivers/gpu/drm/amd/display/modules/hdcp/Makefile +new file mode 100644 +index 000000000000..1c3c6d47973a +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/Makefile +@@ -0,0 +1,32 @@ ++# ++# 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. ++# ++# ++# Makefile for the 'hdcp' sub-module of DAL. ++# ++ ++HDCP = hdcp_ddc.o hdcp_log.o hdcp_psp.o hdcp.o \ ++ hdcp1_execution.o hdcp1_transition.o ++ ++AMD_DAL_HDCP = $(addprefix $(AMDDALPATH)/modules/hdcp/,$(HDCP)) ++#$(info ************ DAL-HDCP_MAKEFILE ************) ++ ++AMD_DISPLAY_FILES += $(AMD_DAL_HDCP) +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c +new file mode 100644 +index 000000000000..d7ac445dec6f +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c +@@ -0,0 +1,426 @@ ++/* ++ * 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" ++ ++static void push_error_status(struct mod_hdcp *hdcp, ++ enum mod_hdcp_status status) ++{ ++ struct mod_hdcp_trace *trace = &hdcp->connection.trace; ++ ++ if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) { ++ trace->errors[trace->error_count].status = status; ++ trace->errors[trace->error_count].state_id = hdcp->state.id; ++ trace->error_count++; ++ HDCP_ERROR_TRACE(hdcp, status); ++ } ++ ++ hdcp->connection.hdcp1_retry_count++; ++} ++ ++static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp) ++{ ++ int i, display_enabled = 0; ++ ++ /* if all displays on the link are disabled, hdcp is not desired */ ++ for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { ++ if (hdcp->connection.displays[i].state != MOD_HDCP_DISPLAY_INACTIVE && ++ !hdcp->connection.displays[i].adjust.disable) { ++ display_enabled = 1; ++ break; ++ } ++ } ++ ++ return (hdcp->connection.hdcp1_retry_count < MAX_NUM_OF_ATTEMPTS) && ++ display_enabled && !hdcp->connection.link.adjust.hdcp1.disable; ++} ++ ++static enum mod_hdcp_status execution(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ union mod_hdcp_transition_input *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (is_in_initialized_state(hdcp)) { ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ /* initialize transition input */ ++ memset(input, 0, sizeof(union mod_hdcp_transition_input)); ++ } else if (is_in_cp_not_desired_state(hdcp)) { ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ /* update topology event if hdcp is not desired */ ++ status = mod_hdcp_add_display_topology(hdcp); ++ } else if (is_in_hdcp1_states(hdcp)) { ++ status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1); ++ } else if (is_in_hdcp1_dp_states(hdcp)) { ++ status = mod_hdcp_hdcp1_dp_execution(hdcp, ++ event_ctx, &input->hdcp1); ++ } ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status transition(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ union mod_hdcp_transition_input *input, ++ struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->unexpected_event) ++ goto out; ++ ++ if (is_in_initialized_state(hdcp)) { ++ if (is_dp_hdcp(hdcp)) ++ if (is_cp_desired_hdcp1(hdcp)) { ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE); ++ } else { ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); ++ } ++ else if (is_hdmi_dvi_sl_hdcp(hdcp)) ++ if (is_cp_desired_hdcp1(hdcp)) { ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, H1_A0_WAIT_FOR_ACTIVE_RX); ++ } else { ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); ++ } ++ else { ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); ++ } ++ } else if (is_in_cp_not_desired_state(hdcp)) { ++ increment_stay_counter(hdcp); ++ } else if (is_in_hdcp1_states(hdcp)) { ++ status = mod_hdcp_hdcp1_transition(hdcp, ++ event_ctx, &input->hdcp1, output); ++ } else if (is_in_hdcp1_dp_states(hdcp)) { ++ status = mod_hdcp_hdcp1_dp_transition(hdcp, ++ event_ctx, &input->hdcp1, output); ++ } else { ++ status = MOD_HDCP_STATUS_INVALID_STATE; ++ } ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp, ++ struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (is_hdcp1(hdcp)) { ++ if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) ++ mod_hdcp_hdcp1_destroy_session(hdcp); ++ ++ if (hdcp->auth.trans_input.hdcp1.add_topology == PASS) { ++ status = mod_hdcp_remove_display_topology(hdcp); ++ if (status != MOD_HDCP_STATUS_SUCCESS) { ++ output->callback_needed = 0; ++ output->watchdog_timer_needed = 0; ++ goto out; ++ } ++ } ++ HDCP_TOP_RESET_AUTH_TRACE(hdcp); ++ memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); ++ memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); ++ set_state_id(hdcp, output, HDCP_INITIALIZED); ++ } else if (is_in_cp_not_desired_state(hdcp)) { ++ status = mod_hdcp_remove_display_topology(hdcp); ++ if (status != MOD_HDCP_STATUS_SUCCESS) { ++ output->callback_needed = 0; ++ output->watchdog_timer_needed = 0; ++ goto out; ++ } ++ HDCP_TOP_RESET_AUTH_TRACE(hdcp); ++ memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); ++ memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); ++ set_state_id(hdcp, output, HDCP_INITIALIZED); ++ } ++ ++out: ++ /* stop callback and watchdog requests from previous authentication*/ ++ output->watchdog_timer_stop = 1; ++ output->callback_stop = 1; ++ return status; ++} ++ ++static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp, ++ struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ memset(output, 0, sizeof(struct mod_hdcp_output)); ++ ++ status = reset_authentication(hdcp, output); ++ if (status != MOD_HDCP_STATUS_SUCCESS) ++ goto out; ++ ++ if (current_state(hdcp) != HDCP_UNINITIALIZED) { ++ HDCP_TOP_RESET_CONN_TRACE(hdcp); ++ set_state_id(hdcp, output, HDCP_UNINITIALIZED); ++ } ++ memset(&hdcp->connection, 0, sizeof(hdcp->connection)); ++out: ++ return status; ++} ++ ++/* ++ * Implementation of functions in mod_hdcp.h ++ */ ++size_t mod_hdcp_get_memory_size(void) ++{ ++ return sizeof(struct mod_hdcp); ++} ++ ++enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp, ++ struct mod_hdcp_config *config) ++{ ++ struct mod_hdcp_output output; ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ memset(hdcp, 0, sizeof(struct mod_hdcp)); ++ memset(&output, 0, sizeof(output)); ++ hdcp->config = *config; ++ HDCP_TOP_INTERFACE_TRACE(hdcp); ++ status = reset_connection(hdcp, &output); ++ if (status != MOD_HDCP_STATUS_SUCCESS) ++ push_error_status(hdcp, status); ++ return status; ++} ++ ++enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ struct mod_hdcp_output output; ++ ++ HDCP_TOP_INTERFACE_TRACE(hdcp); ++ memset(&output, 0, sizeof(output)); ++ status = reset_connection(hdcp, &output); ++ if (status == MOD_HDCP_STATUS_SUCCESS) ++ memset(hdcp, 0, sizeof(struct mod_hdcp)); ++ else ++ push_error_status(hdcp, status); ++ return status; ++} ++ ++enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp, ++ struct mod_hdcp_link *link, struct mod_hdcp_display *display, ++ struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ struct mod_hdcp_display *display_container = NULL; ++ ++ HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index); ++ memset(output, 0, sizeof(struct mod_hdcp_output)); ++ ++ /* skip inactive display */ ++ if (display->state != MOD_HDCP_DISPLAY_ACTIVE) { ++ status = MOD_HDCP_STATUS_SUCCESS; ++ goto out; ++ } ++ ++ /* check existing display container */ ++ if (get_active_display_at_index(hdcp, display->index)) { ++ status = MOD_HDCP_STATUS_SUCCESS; ++ goto out; ++ } ++ ++ /* find an empty display container */ ++ display_container = get_empty_display_container(hdcp); ++ if (!display_container) { ++ status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND; ++ goto out; ++ } ++ ++ /* reset existing authentication status */ ++ status = reset_authentication(hdcp, output); ++ if (status != MOD_HDCP_STATUS_SUCCESS) ++ goto out; ++ ++ /* add display to connection */ ++ hdcp->connection.link = *link; ++ *display_container = *display; ++ ++ /* reset retry counters */ ++ reset_retry_counts(hdcp); ++ ++ /* reset error trace */ ++ memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); ++ ++ /* request authentication */ ++ if (current_state(hdcp) != HDCP_INITIALIZED) ++ set_state_id(hdcp, output, HDCP_INITIALIZED); ++ callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output); ++out: ++ if (status != MOD_HDCP_STATUS_SUCCESS) ++ push_error_status(hdcp, status); ++ ++ return status; ++} ++ ++enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp, ++ uint8_t index, struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ struct mod_hdcp_display *display = NULL; ++ ++ HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index); ++ memset(output, 0, sizeof(struct mod_hdcp_output)); ++ ++ /* find display in connection */ ++ display = get_active_display_at_index(hdcp, index); ++ if (!display) { ++ status = MOD_HDCP_STATUS_SUCCESS; ++ goto out; ++ } ++ ++ /* stop current authentication */ ++ status = reset_authentication(hdcp, output); ++ if (status != MOD_HDCP_STATUS_SUCCESS) ++ goto out; ++ ++ /* remove display */ ++ display->state = MOD_HDCP_DISPLAY_INACTIVE; ++ ++ /* clear retry counters */ ++ reset_retry_counts(hdcp); ++ ++ /* reset error trace */ ++ memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); ++ ++ /* request authentication for remaining displays*/ ++ if (get_active_display_count(hdcp) > 0) ++ callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, ++ output); ++out: ++ if (status != MOD_HDCP_STATUS_SUCCESS) ++ push_error_status(hdcp, status); ++ return status; ++} ++ ++enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp, ++ uint8_t index, struct mod_hdcp_display_query *query) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ struct mod_hdcp_display *display = NULL; ++ ++ /* find display in connection */ ++ display = get_active_display_at_index(hdcp, index); ++ if (!display) { ++ status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND; ++ goto out; ++ } ++ ++ /* populate query */ ++ query->link = &hdcp->connection.link; ++ query->display = display; ++ query->trace = &hdcp->connection.trace; ++ query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ ++ mod_hdcp_hdcp1_get_link_encryption_status(hdcp, &query->encryption_status); ++ ++out: ++ return status; ++} ++ ++enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp, ++ struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ HDCP_TOP_INTERFACE_TRACE(hdcp); ++ status = reset_connection(hdcp, output); ++ if (status != MOD_HDCP_STATUS_SUCCESS) ++ push_error_status(hdcp, status); ++ ++ return status; ++} ++ ++enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp, ++ enum mod_hdcp_event event, struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status exec_status, trans_status, reset_status, status; ++ struct mod_hdcp_event_context event_ctx; ++ ++ HDCP_EVENT_TRACE(hdcp, event); ++ memset(output, 0, sizeof(struct mod_hdcp_output)); ++ memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context)); ++ event_ctx.event = event; ++ ++ /* execute and transition */ ++ exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input); ++ trans_status = transition( ++ hdcp, &event_ctx, &hdcp->auth.trans_input, output); ++ if (trans_status == MOD_HDCP_STATUS_SUCCESS) { ++ status = MOD_HDCP_STATUS_SUCCESS; ++ } else if (exec_status == MOD_HDCP_STATUS_SUCCESS) { ++ status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE; ++ push_error_status(hdcp, status); ++ } else { ++ status = exec_status; ++ push_error_status(hdcp, status); ++ } ++ ++ /* reset authentication if needed */ ++ if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) { ++ HDCP_FULL_DDC_TRACE(hdcp); ++ reset_status = reset_authentication(hdcp, output); ++ if (reset_status != MOD_HDCP_STATUS_SUCCESS) ++ push_error_status(hdcp, reset_status); ++ } ++ return status; ++} ++ ++enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode( ++ enum signal_type signal) ++{ ++ enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF; ++ ++ switch (signal) { ++ case SIGNAL_TYPE_DVI_SINGLE_LINK: ++ case SIGNAL_TYPE_HDMI_TYPE_A: ++ mode = MOD_HDCP_MODE_DEFAULT; ++ break; ++ case SIGNAL_TYPE_EDP: ++ case SIGNAL_TYPE_DISPLAY_PORT: ++ mode = MOD_HDCP_MODE_DP; ++ break; ++ case SIGNAL_TYPE_DISPLAY_PORT_MST: ++ mode = MOD_HDCP_MODE_DP_MST; ++ break; ++ default: ++ break; ++ }; ++ ++ return mode; ++} +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h +new file mode 100644 +index 000000000000..402bb7999093 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h +@@ -0,0 +1,442 @@ ++/* ++ * 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 ++ * ++ */ ++ ++#ifndef HDCP_H_ ++#define HDCP_H_ ++ ++#include "mod_hdcp.h" ++#include "hdcp_log.h" ++ ++#define BCAPS_READY_MASK 0x20 ++#define BCAPS_REPEATER_MASK 0x40 ++#define BSTATUS_DEVICE_COUNT_MASK 0X007F ++#define BSTATUS_MAX_DEVS_EXCEEDED_MASK 0x0080 ++#define BSTATUS_MAX_CASCADE_EXCEEDED_MASK 0x0800 ++#define BCAPS_HDCP_CAPABLE_MASK_DP 0x01 ++#define BCAPS_REPEATER_MASK_DP 0x02 ++#define BSTATUS_READY_MASK_DP 0x01 ++#define BSTATUS_R0_P_AVAILABLE_MASK_DP 0x02 ++#define BSTATUS_LINK_INTEGRITY_FAILURE_MASK_DP 0x04 ++#define BSTATUS_REAUTH_REQUEST_MASK_DP 0x08 ++#define BINFO_DEVICE_COUNT_MASK_DP 0X007F ++#define BINFO_MAX_DEVS_EXCEEDED_MASK_DP 0x0080 ++#define BINFO_MAX_CASCADE_EXCEEDED_MASK_DP 0x0800 ++ ++#define RXSTATUS_MSG_SIZE_MASK 0x03FF ++#define RXSTATUS_READY_MASK 0x0400 ++#define RXSTATUS_REAUTH_REQUEST_MASK 0x0800 ++#define RXIDLIST_DEVICE_COUNT_LOWER_MASK 0xf0 ++#define RXIDLIST_DEVICE_COUNT_UPPER_MASK 0x01 ++#define RXCAPS_BYTE0_HDCP_CAPABLE_MASK_DP 0x02 ++#define RXSTATUS_READY_MASK_DP 0x0001 ++#define RXSTATUS_H_P_AVAILABLE_MASK_DP 0x0002 ++#define RXSTATUS_PAIRING_AVAILABLE_MASK_DP 0x0004 ++#define RXSTATUS_REAUTH_REQUEST_MASK_DP 0x0008 ++#define RXSTATUS_LINK_INTEGRITY_FAILURE_MASK_DP 0x0010 ++ ++enum mod_hdcp_trans_input_result { ++ UNKNOWN = 0, ++ PASS, ++ FAIL ++}; ++ ++struct mod_hdcp_transition_input_hdcp1 { ++ uint8_t bksv_read; ++ uint8_t bksv_validation; ++ uint8_t add_topology; ++ uint8_t create_session; ++ uint8_t an_write; ++ uint8_t aksv_write; ++ uint8_t ainfo_write; ++ uint8_t bcaps_read; ++ uint8_t r0p_read; ++ uint8_t rx_validation; ++ uint8_t encryption; ++ uint8_t link_maintenance; ++ uint8_t ready_check; ++ uint8_t bstatus_read; ++ uint8_t max_cascade_check; ++ uint8_t max_devs_check; ++ uint8_t device_count_check; ++ uint8_t ksvlist_read; ++ uint8_t vp_read; ++ uint8_t ksvlist_vp_validation; ++ ++ uint8_t hdcp_capable_dp; ++ uint8_t binfo_read_dp; ++ uint8_t r0p_available_dp; ++ uint8_t link_integiry_check; ++ uint8_t reauth_request_check; ++ uint8_t stream_encryption_dp; ++}; ++ ++union mod_hdcp_transition_input { ++ struct mod_hdcp_transition_input_hdcp1 hdcp1; ++}; ++ ++struct mod_hdcp_message_hdcp1 { ++ uint8_t an[8]; ++ uint8_t aksv[5]; ++ uint8_t ainfo; ++ uint8_t bksv[5]; ++ uint16_t r0p; ++ uint8_t bcaps; ++ uint16_t bstatus; ++ uint8_t ksvlist[635]; ++ uint16_t ksvlist_size; ++ uint8_t vp[20]; ++ ++ uint16_t binfo_dp; ++}; ++ ++union mod_hdcp_message { ++ struct mod_hdcp_message_hdcp1 hdcp1; ++}; ++ ++struct mod_hdcp_auth_counters { ++ uint8_t stream_management_retry_count; ++}; ++ ++/* contains values per connection */ ++struct mod_hdcp_connection { ++ struct mod_hdcp_link link; ++ struct mod_hdcp_display displays[MAX_NUM_OF_DISPLAYS]; ++ uint8_t is_repeater; ++ uint8_t is_km_stored; ++ struct mod_hdcp_trace trace; ++ uint8_t hdcp1_retry_count; ++}; ++ ++/* contains values per authentication cycle */ ++struct mod_hdcp_authentication { ++ uint32_t id; ++ union mod_hdcp_message msg; ++ union mod_hdcp_transition_input trans_input; ++ struct mod_hdcp_auth_counters count; ++}; ++ ++/* contains values per state change */ ++struct mod_hdcp_state { ++ uint8_t id; ++ uint32_t stay_count; ++}; ++ ++/* per event in a state */ ++struct mod_hdcp_event_context { ++ enum mod_hdcp_event event; ++ uint8_t rx_id_list_ready; ++ uint8_t unexpected_event; ++}; ++ ++struct mod_hdcp { ++ /* per link */ ++ struct mod_hdcp_config config; ++ /* per connection */ ++ struct mod_hdcp_connection connection; ++ /* per authentication attempt */ ++ struct mod_hdcp_authentication auth; ++ /* per state in an authentication */ ++ struct mod_hdcp_state state; ++ /* reserved memory buffer */ ++ uint8_t buf[2025]; ++}; ++ ++enum mod_hdcp_initial_state_id { ++ HDCP_UNINITIALIZED = 0x0, ++ HDCP_INITIAL_STATE_START = HDCP_UNINITIALIZED, ++ HDCP_INITIALIZED, ++ HDCP_CP_NOT_DESIRED, ++ HDCP_INITIAL_STATE_END = HDCP_CP_NOT_DESIRED ++}; ++ ++enum mod_hdcp_hdcp1_state_id { ++ HDCP1_STATE_START = HDCP_INITIAL_STATE_END, ++ H1_A0_WAIT_FOR_ACTIVE_RX, ++ H1_A1_EXCHANGE_KSVS, ++ H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER, ++ H1_A45_AUTHENICATED, ++ H1_A8_WAIT_FOR_READY, ++ H1_A9_READ_KSV_LIST, ++ HDCP1_STATE_END = H1_A9_READ_KSV_LIST ++}; ++ ++enum mod_hdcp_hdcp1_dp_state_id { ++ HDCP1_DP_STATE_START = HDCP1_STATE_END, ++ D1_A0_DETERMINE_RX_HDCP_CAPABLE, ++ D1_A1_EXCHANGE_KSVS, ++ D1_A23_WAIT_FOR_R0_PRIME, ++ D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER, ++ D1_A4_AUTHENICATED, ++ D1_A6_WAIT_FOR_READY, ++ D1_A7_READ_KSV_LIST, ++ HDCP1_DP_STATE_END = D1_A7_READ_KSV_LIST, ++}; ++ ++/* hdcp1 executions and transitions */ ++typedef enum mod_hdcp_status (*mod_hdcp_action)(struct mod_hdcp *hdcp); ++uint8_t mod_hdcp_execute_and_set( ++ mod_hdcp_action func, uint8_t *flag, ++ enum mod_hdcp_status *status, struct mod_hdcp *hdcp, char *str); ++enum mod_hdcp_status mod_hdcp_hdcp1_execution(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input); ++enum mod_hdcp_status mod_hdcp_hdcp1_dp_execution(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input); ++enum mod_hdcp_status mod_hdcp_hdcp1_transition(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input, ++ struct mod_hdcp_output *output); ++enum mod_hdcp_status mod_hdcp_hdcp1_dp_transition(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input, ++ struct mod_hdcp_output *output); ++ ++/* log functions */ ++void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size, ++ uint8_t *buf, uint32_t buf_size); ++/* TODO: add adjustment log */ ++ ++/* psp functions */ ++enum mod_hdcp_status mod_hdcp_add_display_topology( ++ struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_remove_display_topology( ++ struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_create_session(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_destroy_session(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_validate_rx(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_enable_encryption(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_validate_ksvlist_vp(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_enable_dp_stream_encryption( ++ struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_link_maintenance(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_hdcp1_get_link_encryption_status(struct mod_hdcp *hdcp, ++ enum mod_hdcp_encryption_status *encryption_status); ++/* ddc functions */ ++enum mod_hdcp_status mod_hdcp_read_bksv(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_bcaps(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_bstatus(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_r0p(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_ksvlist(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_vp(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_binfo(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_aksv(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_ainfo(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_an(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_rxcaps(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_rxstatus(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_ake_cert(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_h_prime(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_pairing_info(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_l_prime(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_read_stream_ready(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_ake_init(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_no_stored_km(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_stored_km(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_lc_init(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_eks(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_repeater_auth_ack(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_stream_manage(struct mod_hdcp *hdcp); ++enum mod_hdcp_status mod_hdcp_write_content_type(struct mod_hdcp *hdcp); ++ ++/* hdcp version helpers */ ++static inline uint8_t is_dp_hdcp(struct mod_hdcp *hdcp) ++{ ++ return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP || ++ hdcp->connection.link.mode == MOD_HDCP_MODE_DP_MST); ++} ++ ++static inline uint8_t is_dp_mst_hdcp(struct mod_hdcp *hdcp) ++{ ++ return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP_MST); ++} ++ ++static inline uint8_t is_hdmi_dvi_sl_hdcp(struct mod_hdcp *hdcp) ++{ ++ return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT); ++} ++ ++/* hdcp state helpers */ ++static inline uint8_t current_state(struct mod_hdcp *hdcp) ++{ ++ return hdcp->state.id; ++} ++ ++static inline void set_state_id(struct mod_hdcp *hdcp, ++ struct mod_hdcp_output *output, uint8_t id) ++{ ++ memset(&hdcp->state, 0, sizeof(hdcp->state)); ++ hdcp->state.id = id; ++ /* callback timer should be reset per state */ ++ output->callback_stop = 1; ++ output->watchdog_timer_stop = 1; ++ HDCP_NEXT_STATE_TRACE(hdcp, id, output); ++} ++ ++static inline uint8_t is_in_hdcp1_states(struct mod_hdcp *hdcp) ++{ ++ return (current_state(hdcp) > HDCP1_STATE_START && ++ current_state(hdcp) <= HDCP1_STATE_END); ++} ++ ++static inline uint8_t is_in_hdcp1_dp_states(struct mod_hdcp *hdcp) ++{ ++ return (current_state(hdcp) > HDCP1_DP_STATE_START && ++ current_state(hdcp) <= HDCP1_DP_STATE_END); ++} ++ ++static inline uint8_t is_hdcp1(struct mod_hdcp *hdcp) ++{ ++ return (is_in_hdcp1_states(hdcp) || is_in_hdcp1_dp_states(hdcp)); ++} ++ ++static inline uint8_t is_in_cp_not_desired_state(struct mod_hdcp *hdcp) ++{ ++ return current_state(hdcp) == HDCP_CP_NOT_DESIRED; ++} ++ ++static inline uint8_t is_in_initialized_state(struct mod_hdcp *hdcp) ++{ ++ return current_state(hdcp) == HDCP_INITIALIZED; ++} ++ ++/* transition operation helpers */ ++static inline void increment_stay_counter(struct mod_hdcp *hdcp) ++{ ++ hdcp->state.stay_count++; ++} ++ ++static inline void fail_and_restart_in_ms(uint16_t time, ++ enum mod_hdcp_status *status, ++ struct mod_hdcp_output *output) ++{ ++ output->callback_needed = 1; ++ output->callback_delay = time; ++ output->watchdog_timer_needed = 0; ++ output->watchdog_timer_delay = 0; ++ *status = MOD_HDCP_STATUS_RESET_NEEDED; ++} ++ ++static inline void callback_in_ms(uint16_t time, struct mod_hdcp_output *output) ++{ ++ output->callback_needed = 1; ++ output->callback_delay = time; ++} ++ ++static inline void set_watchdog_in_ms(struct mod_hdcp *hdcp, uint16_t time, ++ struct mod_hdcp_output *output) ++{ ++ output->watchdog_timer_needed = 1; ++ output->watchdog_timer_delay = time; ++} ++ ++/* connection topology helpers */ ++static inline uint8_t is_display_active(struct mod_hdcp_display *display) ++{ ++ return display->state >= MOD_HDCP_DISPLAY_ACTIVE; ++} ++ ++static inline uint8_t is_display_added(struct mod_hdcp_display *display) ++{ ++ return display->state >= MOD_HDCP_DISPLAY_ACTIVE_AND_ADDED; ++} ++ ++static inline uint8_t is_display_encryption_enabled(struct mod_hdcp_display *display) ++{ ++ return display->state >= MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED; ++} ++ ++static inline uint8_t get_active_display_count(struct mod_hdcp *hdcp) ++{ ++ uint8_t added_count = 0; ++ uint8_t i; ++ ++ for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) ++ if (is_display_active(&hdcp->connection.displays[i])) ++ added_count++; ++ return added_count; ++} ++ ++static inline uint8_t get_added_display_count(struct mod_hdcp *hdcp) ++{ ++ uint8_t added_count = 0; ++ uint8_t i; ++ ++ for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) ++ if (is_display_added(&hdcp->connection.displays[i])) ++ added_count++; ++ return added_count; ++} ++ ++static inline struct mod_hdcp_display *get_first_added_display( ++ struct mod_hdcp *hdcp) ++{ ++ uint8_t i; ++ struct mod_hdcp_display *display = NULL; ++ ++ for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) ++ if (is_display_added(&hdcp->connection.displays[i])) { ++ display = &hdcp->connection.displays[i]; ++ break; ++ } ++ return display; ++} ++ ++static inline struct mod_hdcp_display *get_active_display_at_index( ++ struct mod_hdcp *hdcp, uint8_t index) ++{ ++ uint8_t i; ++ struct mod_hdcp_display *display = NULL; ++ ++ for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) ++ if (hdcp->connection.displays[i].index == index && ++ is_display_active(&hdcp->connection.displays[i])) { ++ display = &hdcp->connection.displays[i]; ++ break; ++ } ++ return display; ++} ++ ++static inline struct mod_hdcp_display *get_empty_display_container( ++ struct mod_hdcp *hdcp) ++{ ++ uint8_t i; ++ struct mod_hdcp_display *display = NULL; ++ ++ for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) ++ if (!is_display_active(&hdcp->connection.displays[i])) { ++ display = &hdcp->connection.displays[i]; ++ break; ++ } ++ return display; ++} ++ ++static inline void reset_retry_counts(struct mod_hdcp *hdcp) ++{ ++ hdcp->connection.hdcp1_retry_count = 0; ++} ++ ++#endif /* HDCP_H_ */ +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c +new file mode 100644 +index 000000000000..9e7302eac299 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c +@@ -0,0 +1,531 @@ ++/* ++ * 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" ++ ++static inline enum mod_hdcp_status validate_bksv(struct mod_hdcp *hdcp) ++{ ++ uint64_t n = *(uint64_t *)hdcp->auth.msg.hdcp1.bksv; ++ uint8_t count = 0; ++ ++ while (n) { ++ count++; ++ n &= (n - 1); ++ } ++ return (count == 20) ? MOD_HDCP_STATUS_SUCCESS : ++ MOD_HDCP_STATUS_HDCP1_INVALID_BKSV; ++} ++ ++static inline enum mod_hdcp_status check_ksv_ready(struct mod_hdcp *hdcp) ++{ ++ if (is_dp_hdcp(hdcp)) ++ return (hdcp->auth.msg.hdcp1.bstatus & BSTATUS_READY_MASK_DP) ? ++ MOD_HDCP_STATUS_SUCCESS : ++ MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY; ++ return (hdcp->auth.msg.hdcp1.bcaps & BCAPS_READY_MASK) ? ++ MOD_HDCP_STATUS_SUCCESS : ++ MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY; ++} ++ ++static inline enum mod_hdcp_status check_hdcp_capable_dp(struct mod_hdcp *hdcp) ++{ ++ return (hdcp->auth.msg.hdcp1.bcaps & BCAPS_HDCP_CAPABLE_MASK_DP) ? ++ MOD_HDCP_STATUS_SUCCESS : ++ MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE; ++} ++ ++static inline enum mod_hdcp_status check_r0p_available_dp(struct mod_hdcp *hdcp) ++{ ++ enum mod_hdcp_status status; ++ if (is_dp_hdcp(hdcp)) { ++ status = (hdcp->auth.msg.hdcp1.bstatus & ++ BSTATUS_R0_P_AVAILABLE_MASK_DP) ? ++ MOD_HDCP_STATUS_SUCCESS : ++ MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING; ++ } else { ++ status = MOD_HDCP_STATUS_INVALID_OPERATION; ++ } ++ return status; ++} ++ ++static inline enum mod_hdcp_status check_link_integrity_dp( ++ struct mod_hdcp *hdcp) ++{ ++ return (hdcp->auth.msg.hdcp1.bstatus & ++ BSTATUS_LINK_INTEGRITY_FAILURE_MASK_DP) ? ++ MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE : ++ MOD_HDCP_STATUS_SUCCESS; ++} ++ ++static inline enum mod_hdcp_status check_no_reauthentication_request_dp( ++ struct mod_hdcp *hdcp) ++{ ++ return (hdcp->auth.msg.hdcp1.bstatus & BSTATUS_REAUTH_REQUEST_MASK_DP) ? ++ MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED : ++ MOD_HDCP_STATUS_SUCCESS; ++} ++ ++static inline enum mod_hdcp_status check_no_max_cascade(struct mod_hdcp *hdcp) ++{ ++ enum mod_hdcp_status status; ++ ++ if (is_dp_hdcp(hdcp)) ++ status = (hdcp->auth.msg.hdcp1.binfo_dp & ++ BINFO_MAX_CASCADE_EXCEEDED_MASK_DP) ? ++ MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE : ++ MOD_HDCP_STATUS_SUCCESS; ++ else ++ status = (hdcp->auth.msg.hdcp1.bstatus & ++ BSTATUS_MAX_CASCADE_EXCEEDED_MASK) ? ++ MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE : ++ MOD_HDCP_STATUS_SUCCESS; ++ return status; ++} ++ ++static inline enum mod_hdcp_status check_no_max_devs(struct mod_hdcp *hdcp) ++{ ++ enum mod_hdcp_status status; ++ ++ if (is_dp_hdcp(hdcp)) ++ status = (hdcp->auth.msg.hdcp1.binfo_dp & ++ BINFO_MAX_DEVS_EXCEEDED_MASK_DP) ? ++ MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE : ++ MOD_HDCP_STATUS_SUCCESS; ++ else ++ status = (hdcp->auth.msg.hdcp1.bstatus & ++ BSTATUS_MAX_DEVS_EXCEEDED_MASK) ? ++ MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE : ++ MOD_HDCP_STATUS_SUCCESS; ++ return status; ++} ++ ++static inline uint8_t get_device_count(struct mod_hdcp *hdcp) ++{ ++ return is_dp_hdcp(hdcp) ? ++ (hdcp->auth.msg.hdcp1.binfo_dp & BINFO_DEVICE_COUNT_MASK_DP) : ++ (hdcp->auth.msg.hdcp1.bstatus & BSTATUS_DEVICE_COUNT_MASK); ++} ++ ++static inline enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp) ++{ ++ /* device count must be greater than or equal to tracked hdcp displays */ ++ return (get_device_count(hdcp) < get_added_display_count(hdcp)) ? ++ MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE : ++ MOD_HDCP_STATUS_SUCCESS; ++} ++ ++static enum mod_hdcp_status wait_for_active_rx(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bksv, ++ &input->bksv_read, &status, ++ hdcp, "bksv_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps, ++ &input->bcaps_read, &status, ++ hdcp, "bcaps_read")) ++ goto out; ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status exchange_ksvs(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (!mod_hdcp_execute_and_set(mod_hdcp_add_display_topology, ++ &input->add_topology, &status, ++ hdcp, "add_topology")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_create_session, ++ &input->create_session, &status, ++ hdcp, "create_session")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_write_an, ++ &input->an_write, &status, ++ hdcp, "an_write")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_write_aksv, ++ &input->aksv_write, &status, ++ hdcp, "aksv_write")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bksv, ++ &input->bksv_read, &status, ++ hdcp, "bksv_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(validate_bksv, ++ &input->bksv_validation, &status, ++ hdcp, "bksv_validation")) ++ goto out; ++ if (hdcp->auth.msg.hdcp1.ainfo) { ++ if (!mod_hdcp_execute_and_set(mod_hdcp_write_ainfo, ++ &input->ainfo_write, &status, ++ hdcp, "ainfo_write")) ++ goto out; ++ } ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status computations_validate_rx_test_for_repeater( ++ struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_r0p, ++ &input->r0p_read, &status, ++ hdcp, "r0p_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_validate_rx, ++ &input->rx_validation, &status, ++ hdcp, "rx_validation")) ++ goto out; ++ if (hdcp->connection.is_repeater) { ++ if (!hdcp->connection.link.adjust.hdcp1.postpone_encryption) ++ if (!mod_hdcp_execute_and_set( ++ mod_hdcp_hdcp1_enable_encryption, ++ &input->encryption, &status, ++ hdcp, "encryption")) ++ goto out; ++ } else { ++ if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_enable_encryption, ++ &input->encryption, &status, ++ hdcp, "encryption")) ++ goto out; ++ if (is_dp_mst_hdcp(hdcp)) ++ if (!mod_hdcp_execute_and_set( ++ mod_hdcp_hdcp1_enable_dp_stream_encryption, ++ &input->stream_encryption_dp, &status, ++ hdcp, "stream_encryption_dp")) ++ goto out; ++ } ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status authenticated(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_link_maintenance, ++ &input->link_maintenance, &status, ++ hdcp, "link_maintenance")) ++ goto out; ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status wait_for_ready(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && ++ event_ctx->event != MOD_HDCP_EVENT_CPIRQ && ++ event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (is_dp_hdcp(hdcp)) { ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus, ++ &input->bstatus_read, &status, ++ hdcp, "bstatus_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_link_integrity_dp, ++ &input->link_integiry_check, &status, ++ hdcp, "link_integiry_check")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_no_reauthentication_request_dp, ++ &input->reauth_request_check, &status, ++ hdcp, "reauth_request_check")) ++ goto out; ++ } else { ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps, ++ &input->bcaps_read, &status, ++ hdcp, "bcaps_read")) ++ goto out; ++ } ++ if (!mod_hdcp_execute_and_set(check_ksv_ready, ++ &input->ready_check, &status, ++ hdcp, "ready_check")) ++ goto out; ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status read_ksv_list(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ uint8_t device_count; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (is_dp_hdcp(hdcp)) { ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_binfo, ++ &input->binfo_read_dp, &status, ++ hdcp, "binfo_read_dp")) ++ goto out; ++ } else { ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus, ++ &input->bstatus_read, &status, ++ hdcp, "bstatus_read")) ++ goto out; ++ } ++ if (!mod_hdcp_execute_and_set(check_no_max_cascade, ++ &input->max_cascade_check, &status, ++ hdcp, "max_cascade_check")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_no_max_devs, ++ &input->max_devs_check, &status, ++ hdcp, "max_devs_check")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_device_count, ++ &input->device_count_check, &status, ++ hdcp, "device_count_check")) ++ goto out; ++ device_count = get_device_count(hdcp); ++ hdcp->auth.msg.hdcp1.ksvlist_size = device_count*5; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_ksvlist, ++ &input->ksvlist_read, &status, ++ hdcp, "ksvlist_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_vp, ++ &input->vp_read, &status, ++ hdcp, "vp_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_validate_ksvlist_vp, ++ &input->ksvlist_vp_validation, &status, ++ hdcp, "ksvlist_vp_validation")) ++ goto out; ++ if (input->encryption != PASS) ++ if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_enable_encryption, ++ &input->encryption, &status, ++ hdcp, "encryption")) ++ goto out; ++ if (is_dp_mst_hdcp(hdcp)) ++ if (!mod_hdcp_execute_and_set( ++ mod_hdcp_hdcp1_enable_dp_stream_encryption, ++ &input->stream_encryption_dp, &status, ++ hdcp, "stream_encryption_dp")) ++ goto out; ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status determine_rx_hdcp_capable_dp(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps, ++ &input->bcaps_read, &status, ++ hdcp, "bcaps_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_hdcp_capable_dp, ++ &input->hdcp_capable_dp, &status, ++ hdcp, "hdcp_capable_dp")) ++ goto out; ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status wait_for_r0_prime_dp(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CPIRQ && ++ event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus, ++ &input->bstatus_read, &status, ++ hdcp, "bstatus_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_r0p_available_dp, ++ &input->r0p_available_dp, &status, ++ hdcp, "r0p_available_dp")) ++ goto out; ++out: ++ return status; ++} ++ ++static enum mod_hdcp_status authenticated_dp(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ if (event_ctx->event != MOD_HDCP_EVENT_CPIRQ) { ++ event_ctx->unexpected_event = 1; ++ goto out; ++ } ++ ++ if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus, ++ &input->bstatus_read, &status, ++ hdcp, "bstatus_read")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_link_integrity_dp, ++ &input->link_integiry_check, &status, ++ hdcp, "link_integiry_check")) ++ goto out; ++ if (!mod_hdcp_execute_and_set(check_no_reauthentication_request_dp, ++ &input->reauth_request_check, &status, ++ hdcp, "reauth_request_check")) ++ goto out; ++out: ++ return status; ++} ++ ++uint8_t mod_hdcp_execute_and_set( ++ mod_hdcp_action func, uint8_t *flag, ++ enum mod_hdcp_status *status, struct mod_hdcp *hdcp, char *str) ++{ ++ *status = func(hdcp); ++ if (*status == MOD_HDCP_STATUS_SUCCESS && *flag != PASS) { ++ HDCP_INPUT_PASS_TRACE(hdcp, str); ++ *flag = PASS; ++ } else if (*status != MOD_HDCP_STATUS_SUCCESS && *flag != FAIL) { ++ HDCP_INPUT_FAIL_TRACE(hdcp, str); ++ *flag = FAIL; ++ } ++ return (*status == MOD_HDCP_STATUS_SUCCESS); ++} ++ ++enum mod_hdcp_status mod_hdcp_hdcp1_execution(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ switch (current_state(hdcp)) { ++ case H1_A0_WAIT_FOR_ACTIVE_RX: ++ status = wait_for_active_rx(hdcp, event_ctx, input); ++ break; ++ case H1_A1_EXCHANGE_KSVS: ++ status = exchange_ksvs(hdcp, event_ctx, input); ++ break; ++ case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER: ++ status = computations_validate_rx_test_for_repeater(hdcp, ++ event_ctx, input); ++ break; ++ case H1_A45_AUTHENICATED: ++ status = authenticated(hdcp, event_ctx, input); ++ break; ++ case H1_A8_WAIT_FOR_READY: ++ status = wait_for_ready(hdcp, event_ctx, input); ++ break; ++ case H1_A9_READ_KSV_LIST: ++ status = read_ksv_list(hdcp, event_ctx, input); ++ break; ++ default: ++ status = MOD_HDCP_STATUS_INVALID_STATE; ++ break; ++ } ++ ++ return status; ++} ++ ++extern enum mod_hdcp_status mod_hdcp_hdcp1_dp_execution(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ ++ switch (current_state(hdcp)) { ++ case D1_A0_DETERMINE_RX_HDCP_CAPABLE: ++ status = determine_rx_hdcp_capable_dp(hdcp, event_ctx, input); ++ break; ++ case D1_A1_EXCHANGE_KSVS: ++ status = exchange_ksvs(hdcp, event_ctx, input); ++ break; ++ case D1_A23_WAIT_FOR_R0_PRIME: ++ status = wait_for_r0_prime_dp(hdcp, event_ctx, input); ++ break; ++ case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER: ++ status = computations_validate_rx_test_for_repeater( ++ hdcp, event_ctx, input); ++ break; ++ case D1_A4_AUTHENICATED: ++ status = authenticated_dp(hdcp, event_ctx, input); ++ break; ++ case D1_A6_WAIT_FOR_READY: ++ status = wait_for_ready(hdcp, event_ctx, input); ++ break; ++ case D1_A7_READ_KSV_LIST: ++ status = read_ksv_list(hdcp, event_ctx, input); ++ break; ++ default: ++ status = MOD_HDCP_STATUS_INVALID_STATE; ++ break; ++ } ++ ++ return status; ++} +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_transition.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_transition.c +new file mode 100644 +index 000000000000..1d187809b709 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_transition.c +@@ -0,0 +1,307 @@ ++/* ++ * 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" ++ ++enum mod_hdcp_status mod_hdcp_hdcp1_transition(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input, ++ struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ struct mod_hdcp_connection *conn = &hdcp->connection; ++ struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust; ++ ++ switch (current_state(hdcp)) { ++ case H1_A0_WAIT_FOR_ACTIVE_RX: ++ if (input->bksv_read != PASS || input->bcaps_read != PASS) { ++ /* 1A-04: repeatedly attempts on port access failure */ ++ callback_in_ms(500, output); ++ increment_stay_counter(hdcp); ++ break; ++ } ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, H1_A1_EXCHANGE_KSVS); ++ break; ++ case H1_A1_EXCHANGE_KSVS: ++ if (input->add_topology != PASS || ++ input->create_session != PASS) { ++ /* out of sync with psp state */ ++ adjust->hdcp1.disable = 1; ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->an_write != PASS || ++ input->aksv_write != PASS || ++ input->bksv_read != PASS || ++ input->bksv_validation != PASS || ++ input->ainfo_write == FAIL) { ++ /* 1A-05: consider invalid bksv a failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ callback_in_ms(300, output); ++ set_state_id(hdcp, output, ++ H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER); ++ break; ++ case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER: ++ if (input->bcaps_read != PASS || ++ input->r0p_read != PASS || ++ input->rx_validation != PASS || ++ (!conn->is_repeater && input->encryption != PASS)) { ++ /* 1A-06: consider invalid r0' a failure */ ++ /* 1A-08: consider bksv listed in SRM a failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ if (conn->is_repeater) { ++ callback_in_ms(0, output); ++ set_watchdog_in_ms(hdcp, 5000, output); ++ set_state_id(hdcp, output, H1_A8_WAIT_FOR_READY); ++ } else { ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, H1_A45_AUTHENICATED); ++ HDCP_FULL_DDC_TRACE(hdcp); ++ } ++ break; ++ case H1_A45_AUTHENICATED: ++ if (input->link_maintenance != PASS) { ++ /* 1A-07: consider invalid ri' a failure */ ++ /* 1A-07a: consider read ri' not returned a failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ callback_in_ms(500, output); ++ increment_stay_counter(hdcp); ++ break; ++ case H1_A8_WAIT_FOR_READY: ++ if (input->ready_check != PASS) { ++ if (event_ctx->event == ++ MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { ++ /* 1B-03: fail hdcp on ksv list READY timeout */ ++ /* prevent black screen in next attempt */ ++ adjust->hdcp1.postpone_encryption = 1; ++ fail_and_restart_in_ms(0, &status, output); ++ } else { ++ /* continue ksv list READY polling*/ ++ callback_in_ms(500, output); ++ increment_stay_counter(hdcp); ++ } ++ break; ++ } ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, H1_A9_READ_KSV_LIST); ++ break; ++ case H1_A9_READ_KSV_LIST: ++ if (input->bstatus_read != PASS || ++ input->max_cascade_check != PASS || ++ input->max_devs_check != PASS || ++ input->device_count_check != PASS || ++ input->ksvlist_read != PASS || ++ input->vp_read != PASS || ++ input->ksvlist_vp_validation != PASS || ++ input->encryption != PASS) { ++ /* 1B-06: consider MAX_CASCADE_EXCEEDED a failure */ ++ /* 1B-05: consider MAX_DEVS_EXCEEDED a failure */ ++ /* 1B-04: consider invalid v' a failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, H1_A45_AUTHENICATED); ++ HDCP_FULL_DDC_TRACE(hdcp); ++ break; ++ default: ++ status = MOD_HDCP_STATUS_INVALID_STATE; ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ ++ return status; ++} ++ ++enum mod_hdcp_status mod_hdcp_hdcp1_dp_transition(struct mod_hdcp *hdcp, ++ struct mod_hdcp_event_context *event_ctx, ++ struct mod_hdcp_transition_input_hdcp1 *input, ++ struct mod_hdcp_output *output) ++{ ++ enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; ++ struct mod_hdcp_connection *conn = &hdcp->connection; ++ struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust; ++ ++ switch (current_state(hdcp)) { ++ case D1_A0_DETERMINE_RX_HDCP_CAPABLE: ++ if (input->bcaps_read != PASS) { ++ /* 1A-04: no authentication on bcaps read failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->hdcp_capable_dp != PASS) { ++ adjust->hdcp1.disable = 1; ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, D1_A1_EXCHANGE_KSVS); ++ break; ++ case D1_A1_EXCHANGE_KSVS: ++ if (input->add_topology != PASS || ++ input->create_session != PASS) { ++ /* out of sync with psp state */ ++ adjust->hdcp1.disable = 1; ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->an_write != PASS || ++ input->aksv_write != PASS || ++ input->bksv_read != PASS || ++ input->bksv_validation != PASS || ++ input->ainfo_write == FAIL) { ++ /* 1A-05: consider invalid bksv a failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ set_watchdog_in_ms(hdcp, 100, output); ++ set_state_id(hdcp, output, D1_A23_WAIT_FOR_R0_PRIME); ++ break; ++ case D1_A23_WAIT_FOR_R0_PRIME: ++ if (input->bstatus_read != PASS) { ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->r0p_available_dp != PASS) { ++ if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) ++ fail_and_restart_in_ms(0, &status, output); ++ else ++ increment_stay_counter(hdcp); ++ break; ++ } ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER); ++ break; ++ case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER: ++ if (input->r0p_read != PASS) { ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->rx_validation != PASS) { ++ if (hdcp->state.stay_count < 2) { ++ /* allow 2 additional retries */ ++ callback_in_ms(0, output); ++ increment_stay_counter(hdcp); ++ } else { ++ /* ++ * 1A-06: consider invalid r0' a failure ++ * after 3 attempts. ++ * 1A-08: consider bksv listed in SRM a failure ++ */ ++ fail_and_restart_in_ms(0, &status, output); ++ } ++ break; ++ } else if ((!conn->is_repeater && input->encryption != PASS) || ++ (!conn->is_repeater && is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) { ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ if (conn->is_repeater) { ++ set_watchdog_in_ms(hdcp, 5000, output); ++ set_state_id(hdcp, output, D1_A6_WAIT_FOR_READY); ++ } else { ++ set_state_id(hdcp, output, D1_A4_AUTHENICATED); ++ HDCP_FULL_DDC_TRACE(hdcp); ++ } ++ break; ++ case D1_A4_AUTHENICATED: ++ if (input->link_integiry_check != PASS || ++ input->reauth_request_check != PASS) { ++ /* 1A-07: restart hdcp on a link integrity failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ break; ++ case D1_A6_WAIT_FOR_READY: ++ if (input->link_integiry_check == FAIL || ++ input->reauth_request_check == FAIL) { ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->ready_check != PASS) { ++ if (event_ctx->event == ++ MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { ++ /* 1B-04: fail hdcp on ksv list READY timeout */ ++ /* prevent black screen in next attempt */ ++ adjust->hdcp1.postpone_encryption = 1; ++ fail_and_restart_in_ms(0, &status, output); ++ } else { ++ increment_stay_counter(hdcp); ++ } ++ break; ++ } ++ callback_in_ms(0, output); ++ set_state_id(hdcp, output, D1_A7_READ_KSV_LIST); ++ break; ++ case D1_A7_READ_KSV_LIST: ++ if (input->binfo_read_dp != PASS || ++ input->max_cascade_check != PASS || ++ input->max_devs_check != PASS) { ++ /* 1B-06: consider MAX_DEVS_EXCEEDED a failure */ ++ /* 1B-07: consider MAX_CASCADE_EXCEEDED a failure */ ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->device_count_check != PASS) { ++ /* ++ * some slow dongle doesn't update ++ * device count as soon as downstream is connected. ++ * give it more time to react. ++ */ ++ adjust->hdcp1.postpone_encryption = 1; ++ fail_and_restart_in_ms(1000, &status, output); ++ break; ++ } else if (input->ksvlist_read != PASS || ++ input->vp_read != PASS) { ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } else if (input->ksvlist_vp_validation != PASS) { ++ if (hdcp->state.stay_count < 2) { ++ /* allow 2 additional retries */ ++ callback_in_ms(0, output); ++ increment_stay_counter(hdcp); ++ } else { ++ /* ++ * 1B-05: consider invalid v' a failure ++ * after 3 attempts. ++ */ ++ fail_and_restart_in_ms(0, &status, output); ++ } ++ break; ++ } else if (input->encryption != PASS || ++ (is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) { ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ set_state_id(hdcp, output, D1_A4_AUTHENICATED); ++ HDCP_FULL_DDC_TRACE(hdcp); ++ break; ++ default: ++ fail_and_restart_in_ms(0, &status, output); ++ break; ++ } ++ ++ return status; ++} +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c +new file mode 100644 +index 000000000000..e7baae059b85 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c +@@ -0,0 +1,305 @@ ++/* ++ * 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" ++ ++#define MIN(a, b) ((a) < (b) ? (a) : (b)) ++#define HDCP_I2C_ADDR 0x3a /* 0x74 >> 1*/ ++#define KSV_READ_SIZE 0xf /* 0x6803b - 0x6802c */ ++#define HDCP_MAX_AUX_TRANSACTION_SIZE 16 ++ ++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, ++ ++ 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, ++}; ++ ++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, ++}; ++ ++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 (is_dp_hdcp(hdcp)) { ++ 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 { ++ 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 (is_dp_hdcp(hdcp)) { ++ 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 { ++ 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)); ++} +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c +new file mode 100644 +index 000000000000..d868f556d180 +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c +@@ -0,0 +1,163 @@ ++/* ++ * 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" ++ ++void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size, ++ uint8_t *buf, uint32_t buf_size) ++{ ++ const uint8_t bytes_per_line = 16, ++ byte_size = 3, ++ newline_size = 1, ++ terminator_size = 1; ++ uint32_t line_count = msg_size / bytes_per_line, ++ trailing_bytes = msg_size % bytes_per_line; ++ uint32_t target_size = (byte_size * bytes_per_line + newline_size) * line_count + ++ byte_size * trailing_bytes + newline_size + terminator_size; ++ uint32_t buf_pos = 0; ++ uint32_t i = 0; ++ ++ if (buf_size >= target_size) { ++ for (i = 0; i < msg_size; i++) { ++ if (i % bytes_per_line == 0) ++ buf[buf_pos++] = '\n'; ++ sprintf(&buf[buf_pos], "%02X ", msg[i]); ++ buf_pos += byte_size; ++ } ++ buf[buf_pos++] = '\0'; ++ } ++} ++ ++char *mod_hdcp_status_to_str(int32_t status) ++{ ++ switch (status) { ++ case MOD_HDCP_STATUS_SUCCESS: ++ return "MOD_HDCP_STATUS_SUCCESS"; ++ case MOD_HDCP_STATUS_FAILURE: ++ return "MOD_HDCP_STATUS_FAILURE"; ++ case MOD_HDCP_STATUS_RESET_NEEDED: ++ return "MOD_HDCP_STATUS_RESET_NEEDED"; ++ case MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND: ++ return "MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND"; ++ case MOD_HDCP_STATUS_DISPLAY_NOT_FOUND: ++ return "MOD_HDCP_STATUS_DISPLAY_NOT_FOUND"; ++ case MOD_HDCP_STATUS_INVALID_STATE: ++ return "MOD_HDCP_STATUS_INVALID_STATE"; ++ case MOD_HDCP_STATUS_NOT_IMPLEMENTED: ++ return "MOD_HDCP_STATUS_NOT_IMPLEMENTED"; ++ case MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE: ++ return "MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE"; ++ case MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE: ++ return "MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE"; ++ case MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE: ++ return "MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE"; ++ case MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE: ++ return "MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER: ++ return "MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER"; ++ case MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE: ++ return "MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE"; ++ case MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING: ++ return "MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING"; ++ case MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY: ++ return "MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY"; ++ case MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION: ++ return "MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION"; ++ case MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED: ++ return "MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED"; ++ case MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE: ++ return "MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE"; ++ case MOD_HDCP_STATUS_HDCP1_INVALID_BKSV: ++ return "MOD_HDCP_STATUS_HDCP1_INVALID_BKSV"; ++ case MOD_HDCP_STATUS_DDC_FAILURE: ++ return "MOD_HDCP_STATUS_DDC_FAILURE"; ++ case MOD_HDCP_STATUS_INVALID_OPERATION: ++ return "MOD_HDCP_STATUS_INVALID_OPERATION"; ++ default: ++ return "MOD_HDCP_STATUS_UNKNOWN"; ++ } ++} ++ ++char *mod_hdcp_state_id_to_str(int32_t id) ++{ ++ switch (id) { ++ case HDCP_UNINITIALIZED: ++ return "HDCP_UNINITIALIZED"; ++ case HDCP_INITIALIZED: ++ return "HDCP_INITIALIZED"; ++ case HDCP_CP_NOT_DESIRED: ++ return "HDCP_CP_NOT_DESIRED"; ++ case H1_A0_WAIT_FOR_ACTIVE_RX: ++ return "H1_A0_WAIT_FOR_ACTIVE_RX"; ++ case H1_A1_EXCHANGE_KSVS: ++ return "H1_A1_EXCHANGE_KSVS"; ++ case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER: ++ return "H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER"; ++ case H1_A45_AUTHENICATED: ++ return "H1_A45_AUTHENICATED"; ++ case H1_A8_WAIT_FOR_READY: ++ return "H1_A8_WAIT_FOR_READY"; ++ case H1_A9_READ_KSV_LIST: ++ return "H1_A9_READ_KSV_LIST"; ++ case D1_A0_DETERMINE_RX_HDCP_CAPABLE: ++ return "D1_A0_DETERMINE_RX_HDCP_CAPABLE"; ++ case D1_A1_EXCHANGE_KSVS: ++ return "D1_A1_EXCHANGE_KSVS"; ++ case D1_A23_WAIT_FOR_R0_PRIME: ++ return "D1_A23_WAIT_FOR_R0_PRIME"; ++ case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER: ++ return "D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER"; ++ case D1_A4_AUTHENICATED: ++ return "D1_A4_AUTHENICATED"; ++ case D1_A6_WAIT_FOR_READY: ++ return "D1_A6_WAIT_FOR_READY"; ++ case D1_A7_READ_KSV_LIST: ++ return "D1_A7_READ_KSV_LIST"; ++ default: ++ return "UNKNOWN_STATE_ID"; ++ }; ++} ++ +diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h +new file mode 100644 +index 000000000000..2fd0e0a893ef +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h +@@ -0,0 +1,139 @@ ++/* ++ * 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 ++ * ++ */ ++ ++#ifndef MOD_HDCP_LOG_H_ ++#define MOD_HDCP_LOG_H_ ++ ++#ifdef CONFIG_DRM_AMD_DC_HDCP ++#define HDCP_LOG_ERR(hdcp, ...) DRM_ERROR(__VA_ARGS__) ++#define HDCP_LOG_VER(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__) ++#define HDCP_LOG_FSM(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__) ++#define HDCP_LOG_TOP(hdcp, ...) pr_debug("[HDCP_TOP]:"__VA_ARGS__) ++#define HDCP_LOG_DDC(hdcp, ...) pr_debug("[HDCP_DDC]:"__VA_ARGS__) ++#endif ++ ++/* default logs */ ++#define HDCP_ERROR_TRACE(hdcp, status) \ ++ HDCP_LOG_ERR(hdcp, \ ++ "[Link %d] ERROR %s IN STATE %s", \ ++ hdcp->config.index, \ ++ mod_hdcp_status_to_str(status), \ ++ mod_hdcp_state_id_to_str(hdcp->state.id)) ++#define HDCP_HDCP1_ENABLED_TRACE(hdcp, displayIndex) \ ++ HDCP_LOG_VER(hdcp, \ ++ "[Link %d] HDCP 1.4 enabled on display %d", \ ++ hdcp->config.index, displayIndex) ++/* state machine logs */ ++#define HDCP_REMOVE_DISPLAY_TRACE(hdcp, displayIndex) \ ++ HDCP_LOG_FSM(hdcp, \ ++ "[Link %d] HDCP_REMOVE_DISPLAY index %d", \ ++ hdcp->config.index, displayIndex) ++#define HDCP_INPUT_PASS_TRACE(hdcp, str) \ ++ HDCP_LOG_FSM(hdcp, \ ++ "[Link %d]\tPASS %s", \ ++ hdcp->config.index, str) ++#define HDCP_INPUT_FAIL_TRACE(hdcp, str) \ ++ HDCP_LOG_FSM(hdcp, \ ++ "[Link %d]\tFAIL %s", \ ++ hdcp->config.index, str) ++#define HDCP_NEXT_STATE_TRACE(hdcp, id, output) do { \ ++ if (output->watchdog_timer_needed) \ ++ HDCP_LOG_FSM(hdcp, \ ++ "[Link %d] > %s with %d ms watchdog", \ ++ hdcp->config.index, \ ++ mod_hdcp_state_id_to_str(id), output->watchdog_timer_delay); \ ++ else \ ++ HDCP_LOG_FSM(hdcp, \ ++ "[Link %d] > %s", hdcp->config.index, \ ++ mod_hdcp_state_id_to_str(id)); \ ++} while (0) ++#define HDCP_TIMEOUT_TRACE(hdcp) \ ++ HDCP_LOG_FSM(hdcp, "[Link %d] --> TIMEOUT", hdcp->config.index) ++#define HDCP_CPIRQ_TRACE(hdcp) \ ++ HDCP_LOG_FSM(hdcp, "[Link %d] --> CPIRQ", hdcp->config.index) ++#define HDCP_EVENT_TRACE(hdcp, event) \ ++ if (event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) \ ++ HDCP_TIMEOUT_TRACE(hdcp); \ ++ else if (event == MOD_HDCP_EVENT_CPIRQ) \ ++ HDCP_CPIRQ_TRACE(hdcp) ++/* TODO: find some way to tell if logging is off to save time */ ++#define HDCP_DDC_READ_TRACE(hdcp, msg_name, msg, msg_size) do { \ ++ mod_hdcp_dump_binary_message(msg, msg_size, hdcp->buf, \ ++ sizeof(hdcp->buf)); \ ++ HDCP_LOG_DDC(hdcp, "[Link %d] Read %s%s", hdcp->config.index, \ ++ msg_name, hdcp->buf); \ ++} while (0) ++#define HDCP_DDC_WRITE_TRACE(hdcp, msg_name, msg, msg_size) do { \ ++ mod_hdcp_dump_binary_message(msg, msg_size, hdcp->buf, \ ++ sizeof(hdcp->buf)); \ ++ HDCP_LOG_DDC(hdcp, "[Link %d] Write %s%s", \ ++ hdcp->config.index, msg_name,\ ++ hdcp->buf); \ ++} while (0) ++#define HDCP_FULL_DDC_TRACE(hdcp) do { \ ++ HDCP_DDC_READ_TRACE(hdcp, "BKSV", hdcp->auth.msg.hdcp1.bksv, \ ++ sizeof(hdcp->auth.msg.hdcp1.bksv)); \ ++ HDCP_DDC_READ_TRACE(hdcp, "BCAPS", &hdcp->auth.msg.hdcp1.bcaps, \ ++ sizeof(hdcp->auth.msg.hdcp1.bcaps)); \ ++ HDCP_DDC_WRITE_TRACE(hdcp, "AN", hdcp->auth.msg.hdcp1.an, \ ++ sizeof(hdcp->auth.msg.hdcp1.an)); \ ++ HDCP_DDC_WRITE_TRACE(hdcp, "AKSV", hdcp->auth.msg.hdcp1.aksv, \ ++ sizeof(hdcp->auth.msg.hdcp1.aksv)); \ ++ HDCP_DDC_WRITE_TRACE(hdcp, "AINFO", &hdcp->auth.msg.hdcp1.ainfo, \ ++ sizeof(hdcp->auth.msg.hdcp1.ainfo)); \ ++ HDCP_DDC_READ_TRACE(hdcp, "RI' / R0'", \ ++ (uint8_t *)&hdcp->auth.msg.hdcp1.r0p, \ ++ sizeof(hdcp->auth.msg.hdcp1.r0p)); \ ++ HDCP_DDC_READ_TRACE(hdcp, "BINFO", \ ++ (uint8_t *)&hdcp->auth.msg.hdcp1.binfo_dp, \ ++ sizeof(hdcp->auth.msg.hdcp1.binfo_dp)); \ ++ HDCP_DDC_READ_TRACE(hdcp, "KSVLIST", hdcp->auth.msg.hdcp1.ksvlist, \ ++ hdcp->auth.msg.hdcp1.ksvlist_size); \ ++ HDCP_DDC_READ_TRACE(hdcp, "V'", hdcp->auth.msg.hdcp1.vp, \ ++ sizeof(hdcp->auth.msg.hdcp1.vp)); \ ++} while (0) ++#define HDCP_TOP_ADD_DISPLAY_TRACE(hdcp, i) \ ++ HDCP_LOG_TOP(hdcp, "[Link %d]\tadd display %d", \ ++ hdcp->config.index, i) ++#define HDCP_TOP_REMOVE_DISPLAY_TRACE(hdcp, i) \ ++ HDCP_LOG_TOP(hdcp, "[Link %d]\tremove display %d", \ ++ hdcp->config.index, i) ++#define HDCP_TOP_HDCP1_DESTROY_SESSION_TRACE(hdcp) \ ++ HDCP_LOG_TOP(hdcp, "[Link %d]\tdestroy hdcp1 session", \ ++ hdcp->config.index) ++#define HDCP_TOP_RESET_AUTH_TRACE(hdcp) \ ++ HDCP_LOG_TOP(hdcp, "[Link %d]\treset authentication", hdcp->config.index) ++#define HDCP_TOP_RESET_CONN_TRACE(hdcp) \ ++ HDCP_LOG_TOP(hdcp, "[Link %d]\treset connection", hdcp->config.index) ++#define HDCP_TOP_INTERFACE_TRACE(hdcp) do { \ ++ HDCP_LOG_TOP(hdcp, "\n"); \ ++ HDCP_LOG_TOP(hdcp, "[Link %d] %s", hdcp->config.index, __func__); \ ++} while (0) ++#define HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, i) do { \ ++ HDCP_LOG_TOP(hdcp, "\n"); \ ++ HDCP_LOG_TOP(hdcp, "[Link %d] %s display %d", hdcp->config.index, __func__, i); \ ++} while (0) ++ ++#endif // MOD_HDCP_LOG_H_ +diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h +new file mode 100644 +index 000000000000..dea21702edff +--- /dev/null ++++ b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h +@@ -0,0 +1,289 @@ ++/* ++ * 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 ++ * ++ */ ++ ++#ifndef MOD_HDCP_H_ ++#define MOD_HDCP_H_ ++ ++#include "os_types.h" ++#include "signal_types.h" ++ ++/* Forward Declarations */ ++struct mod_hdcp; ++ ++#define MAX_NUM_OF_DISPLAYS 6 ++#define MAX_NUM_OF_ATTEMPTS 4 ++#define MAX_NUM_OF_ERROR_TRACE 10 ++ ++/* detailed return status */ ++enum mod_hdcp_status { ++ MOD_HDCP_STATUS_SUCCESS = 0, ++ MOD_HDCP_STATUS_FAILURE, ++ MOD_HDCP_STATUS_RESET_NEEDED, ++ MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND, ++ MOD_HDCP_STATUS_DISPLAY_NOT_FOUND, ++ MOD_HDCP_STATUS_INVALID_STATE, ++ MOD_HDCP_STATUS_NOT_IMPLEMENTED, ++ MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE, ++ MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE, ++ MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE, ++ MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER, ++ MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE, ++ MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING, ++ MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY, ++ MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION, ++ MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED, ++ MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE, ++ MOD_HDCP_STATUS_HDCP1_INVALID_BKSV, ++ MOD_HDCP_STATUS_DDC_FAILURE, /* TODO: specific errors */ ++ MOD_HDCP_STATUS_INVALID_OPERATION, ++ MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE, ++ MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING, ++ MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING, ++ MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING, ++ MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING, ++ MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY, ++ MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION, ++ MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING, ++ MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST, ++ MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE, ++ MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE, ++}; ++ ++struct mod_hdcp_displayport { ++ uint8_t rev; ++ uint8_t assr_supported; ++}; ++ ++struct mod_hdcp_hdmi { ++ uint8_t reserved; ++}; ++enum mod_hdcp_operation_mode { ++ MOD_HDCP_MODE_OFF, ++ MOD_HDCP_MODE_DEFAULT, ++ MOD_HDCP_MODE_DP, ++ MOD_HDCP_MODE_DP_MST ++}; ++ ++enum mod_hdcp_display_state { ++ MOD_HDCP_DISPLAY_INACTIVE = 0, ++ MOD_HDCP_DISPLAY_ACTIVE, ++ MOD_HDCP_DISPLAY_ACTIVE_AND_ADDED, ++ MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED ++}; ++ ++struct mod_hdcp_ddc { ++ void *handle; ++ struct { ++ bool (*read_i2c)(void *handle, ++ uint32_t address, ++ uint8_t offset, ++ uint8_t *data, ++ uint32_t size); ++ bool (*write_i2c)(void *handle, ++ uint32_t address, ++ const uint8_t *data, ++ uint32_t size); ++ bool (*read_dpcd)(void *handle, ++ uint32_t address, ++ uint8_t *data, ++ uint32_t size); ++ bool (*write_dpcd)(void *handle, ++ uint32_t address, ++ const uint8_t *data, ++ uint32_t size); ++ } funcs; ++}; ++ ++struct mod_hdcp_psp { ++ void *handle; ++ void *funcs; ++}; ++ ++struct mod_hdcp_display_adjustment { ++ uint8_t disable : 1; ++ uint8_t reserved : 7; ++}; ++ ++struct mod_hdcp_link_adjustment_hdcp1 { ++ uint8_t disable : 1; ++ uint8_t postpone_encryption : 1; ++ uint8_t reserved : 6; ++}; ++ ++struct mod_hdcp_link_adjustment_hdcp2 { ++ uint8_t disable : 1; ++ uint8_t disable_type1 : 1; ++ uint8_t force_no_stored_km : 1; ++ uint8_t increase_h_prime_timeout: 1; ++ uint8_t reserved : 4; ++}; ++ ++struct mod_hdcp_link_adjustment { ++ uint8_t auth_delay; ++ struct mod_hdcp_link_adjustment_hdcp1 hdcp1; ++ struct mod_hdcp_link_adjustment_hdcp2 hdcp2; ++}; ++ ++struct mod_hdcp_error { ++ enum mod_hdcp_status status; ++ uint8_t state_id; ++}; ++ ++struct mod_hdcp_trace { ++ struct mod_hdcp_error errors[MAX_NUM_OF_ERROR_TRACE]; ++ uint8_t error_count; ++}; ++ ++enum mod_hdcp_encryption_status { ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF = 0, ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON, ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON, ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON ++}; ++ ++/* per link events dm has to notify to hdcp module */ ++enum mod_hdcp_event { ++ MOD_HDCP_EVENT_CALLBACK = 0, ++ MOD_HDCP_EVENT_WATCHDOG_TIMEOUT, ++ MOD_HDCP_EVENT_CPIRQ ++}; ++ ++/* output flags from module requesting timer operations */ ++struct mod_hdcp_output { ++ uint8_t callback_needed; ++ uint8_t callback_stop; ++ uint8_t watchdog_timer_needed; ++ uint8_t watchdog_timer_stop; ++ uint16_t callback_delay; ++ uint16_t watchdog_timer_delay; ++}; ++ ++/* used to represent per display info */ ++struct mod_hdcp_display { ++ enum mod_hdcp_display_state state; ++ uint8_t index; ++ uint8_t controller; ++ uint8_t dig_fe; ++ union { ++ uint8_t vc_id; ++ }; ++ struct mod_hdcp_display_adjustment adjust; ++}; ++ ++/* used to represent per link info */ ++/* in case a link has multiple displays, they share the same link info */ ++struct mod_hdcp_link { ++ enum mod_hdcp_operation_mode mode; ++ uint8_t dig_be; ++ uint8_t ddc_line; ++ union { ++ struct mod_hdcp_displayport dp; ++ struct mod_hdcp_hdmi hdmi; ++ }; ++ struct mod_hdcp_link_adjustment adjust; ++}; ++ ++/* a query structure for a display's hdcp information */ ++struct mod_hdcp_display_query { ++ const struct mod_hdcp_display *display; ++ const struct mod_hdcp_link *link; ++ const struct mod_hdcp_trace *trace; ++ enum mod_hdcp_encryption_status encryption_status; ++}; ++ ++/* contains values per on external display configuration change */ ++struct mod_hdcp_config { ++ struct mod_hdcp_psp psp; ++ struct mod_hdcp_ddc ddc; ++ uint8_t index; ++}; ++ ++struct mod_hdcp; ++ ++/* dm allocates memory of mod_hdcp per dc_link on dm init based on memory size*/ ++size_t mod_hdcp_get_memory_size(void); ++ ++/* called per link on link creation */ ++enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp, ++ struct mod_hdcp_config *config); ++ ++/* called per link on link destroy */ ++enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp); ++ ++/* called per display on cp_desired set to true */ ++enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp, ++ struct mod_hdcp_link *link, struct mod_hdcp_display *display, ++ struct mod_hdcp_output *output); ++ ++/* called per display on cp_desired set to false */ ++enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp, ++ uint8_t index, struct mod_hdcp_output *output); ++ ++/* called to query hdcp information on a specific index */ ++enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp, ++ uint8_t index, struct mod_hdcp_display_query *query); ++ ++/* called per link on connectivity change */ ++enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp, ++ struct mod_hdcp_output *output); ++ ++/* called per link on events (i.e. callback, watchdog, CP_IRQ) */ ++enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp, ++ enum mod_hdcp_event event, struct mod_hdcp_output *output); ++ ++/* called to convert enum mod_hdcp_status to c string */ ++char *mod_hdcp_status_to_str(int32_t status); ++ ++/* called to convert state id to c string */ ++char *mod_hdcp_state_id_to_str(int32_t id); ++ ++/* called to convert signal type to operation mode */ ++enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode( ++ enum signal_type signal); ++#endif /* MOD_HDCP_H_ */ +-- +2.17.1 + |