diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/2643-drm-amd-display-DMCU-FW-loading-from-PSP.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/2643-drm-amd-display-DMCU-FW-loading-from-PSP.patch | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/2643-drm-amd-display-DMCU-FW-loading-from-PSP.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/2643-drm-amd-display-DMCU-FW-loading-from-PSP.patch new file mode 100644 index 00000000..f82188ba --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/2643-drm-amd-display-DMCU-FW-loading-from-PSP.patch @@ -0,0 +1,434 @@ +From fb15e236c9c364538b4a7033ed4bc84cc1a45fb0 Mon Sep 17 00:00:00 2001 +From: Anthony Koo <Anthony.Koo@amd.com> +Date: Wed, 18 Oct 2017 16:14:40 -0400 +Subject: [PATCH 2643/4131] drm/amd/display: DMCU FW loading from PSP + +Update the programming sequence to allow DMCU firmware to be loaded by +PSP. This code detects whether the firmware is loaded and does a check +to verify the expected interface version and checks for correct response +from micro controller. + +Added registry key method to allow force loading of firmware from kernel +mode driver for test purposes. This is old method of firmware loading +without PSP. + +Moved some init sequences into dc/dmcu. + +Changed loading sequence to initialize IRAM after firmware completely +loaded. Firmware will now disable features that use IRAM +until initialized. + +Signed-off-by: Anthony Koo <anthony.koo@amd.com> +Reviewed-by: Tony Cheng <Tony.Cheng@amd.com> +Acked-by: Harry Wentland <harry.wentland@amd.com> +Signed-off-by: Alex Deucher <alexander.deucher@amd.com> +--- + drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c | 176 ++++++++++++++++++++- + drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h | 12 +- + .../drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c | 5 + + drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h | 16 ++ + 4 files changed, 202 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c +index 3691c74..b60524a 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c ++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c +@@ -49,9 +49,18 @@ + #define PSR_EXIT 0x21 + #define PSR_SET 0x23 + #define PSR_SET_WAITLOOP 0x31 ++#define MCP_INIT_DMCU 0x88 ++#define MCP_INIT_IRAM 0x89 ++#define MCP_DMCU_VERSION 0x90 + #define MASTER_COMM_CNTL_REG__MASTER_COMM_INTERRUPT_MASK 0x00000001L + unsigned int cached_wait_loop_number = 0; + ++static bool dce_dmcu_init(struct dmcu *dmcu) ++{ ++ // Do nothing ++ return true; ++} ++ + bool dce_dmcu_load_iram(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, +@@ -84,7 +93,7 @@ static void dce_get_dmcu_psr_state(struct dmcu *dmcu, uint32_t *psr_state) + { + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + +- uint32_t psrStateOffset = 0xf0; ++ uint32_t psr_state_offset = 0xf0; + + /* Enable write access to IRAM */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 1); +@@ -92,7 +101,7 @@ static void dce_get_dmcu_psr_state(struct dmcu *dmcu, uint32_t *psr_state) + REG_WAIT(DCI_MEM_PWR_STATUS, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ +- REG_WRITE(DMCU_IRAM_RD_CTRL, psrStateOffset); ++ REG_WRITE(DMCU_IRAM_RD_CTRL, psr_state_offset); + + /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ + *psr_state = REG_READ(DMCU_IRAM_RD_DATA); +@@ -286,7 +295,128 @@ static void dce_get_psr_wait_loop(unsigned int *psr_wait_loop_number) + } + + #if defined(CONFIG_DRM_AMD_DC_DCN1_0) +-bool dcn10_dmcu_load_iram(struct dmcu *dmcu, ++static void dcn10_get_dmcu_state(struct dmcu *dmcu) ++{ ++ struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); ++ uint32_t dmcu_state_offset = 0xf6; ++ ++ /* Enable write access to IRAM */ ++ REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, ++ IRAM_HOST_ACCESS_EN, 1, ++ IRAM_RD_ADDR_AUTO_INC, 1); ++ ++ REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); ++ ++ /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ ++ REG_WRITE(DMCU_IRAM_RD_CTRL, dmcu_state_offset); ++ ++ /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ ++ dmcu->dmcu_state = REG_READ(DMCU_IRAM_RD_DATA); ++ ++ /* Disable write access to IRAM to allow dynamic sleep state */ ++ REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, ++ IRAM_HOST_ACCESS_EN, 0, ++ IRAM_RD_ADDR_AUTO_INC, 0); ++} ++ ++static void dcn10_get_dmcu_version(struct dmcu *dmcu) ++{ ++ struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); ++ uint32_t dmcu_version_offset = 0xf1; ++ ++ /* Clear scratch */ ++ REG_WRITE(DC_DMCU_SCRATCH, 0); ++ ++ /* Enable write access to IRAM */ ++ REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, ++ IRAM_HOST_ACCESS_EN, 1, ++ IRAM_RD_ADDR_AUTO_INC, 1); ++ ++ REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); ++ ++ /* Write address to IRAM_RD_ADDR and read from DATA register */ ++ REG_WRITE(DMCU_IRAM_RD_CTRL, dmcu_version_offset); ++ dmcu->dmcu_version.interface_version = REG_READ(DMCU_IRAM_RD_DATA); ++ dmcu->dmcu_version.year = ((REG_READ(DMCU_IRAM_RD_DATA) << 8) | ++ REG_READ(DMCU_IRAM_RD_DATA)); ++ dmcu->dmcu_version.month = REG_READ(DMCU_IRAM_RD_DATA); ++ dmcu->dmcu_version.day = REG_READ(DMCU_IRAM_RD_DATA); ++ ++ /* Disable write access to IRAM to allow dynamic sleep state */ ++ REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, ++ IRAM_HOST_ACCESS_EN, 0, ++ IRAM_RD_ADDR_AUTO_INC, 0); ++ ++ /* Send MCP command message to DMCU to get version reply from FW. ++ * We expect this version should match the one in IRAM, otherwise ++ * something is wrong with DMCU and we should fail and disable UC. ++ */ ++ REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); ++ ++ /* Set command to get DMCU version from microcontroller */ ++ REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, ++ MCP_DMCU_VERSION); ++ ++ /* Notify microcontroller of new command */ ++ REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); ++ ++ /* Ensure command has been executed before continuing */ ++ REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); ++ ++ /* Somehow version does not match, so fail and return version 0 */ ++ if (dmcu->dmcu_version.interface_version != REG_READ(DC_DMCU_SCRATCH)) ++ dmcu->dmcu_version.interface_version = 0; ++} ++ ++static bool dcn10_dmcu_init(struct dmcu *dmcu) ++{ ++ struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); ++ ++ /* DMCU FW should populate the scratch register if running */ ++ if (REG_READ(DC_DMCU_SCRATCH) == 0) ++ return false; ++ ++ /* Check state is uninitialized */ ++ dcn10_get_dmcu_state(dmcu); ++ ++ /* If microcontroller is already initialized, do nothing */ ++ if (dmcu->dmcu_state == DMCU_RUNNING) ++ return true; ++ ++ /* Retrieve and cache the DMCU firmware version. */ ++ dcn10_get_dmcu_version(dmcu); ++ ++ /* Check interface version to confirm firmware is loaded and running */ ++ if (dmcu->dmcu_version.interface_version == 0) ++ return false; ++ ++ /* Wait until microcontroller is ready to process interrupt */ ++ REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); ++ ++ /* Set initialized ramping boundary value */ ++ REG_WRITE(MASTER_COMM_DATA_REG1, 0xFFFF); ++ ++ /* Set command to initialize microcontroller */ ++ REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, ++ MCP_INIT_DMCU); ++ ++ /* Notify microcontroller of new command */ ++ REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); ++ ++ /* Ensure command has been executed before continuing */ ++ REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); ++ ++ // Check state is initialized ++ dcn10_get_dmcu_state(dmcu); ++ ++ // If microcontroller is not in running state, fail ++ if (dmcu->dmcu_state != DMCU_RUNNING) ++ return false; ++ ++ return true; ++} ++ ++static bool dcn10_dmcu_load_iram(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, + unsigned int bytes) +@@ -294,7 +424,9 @@ bool dcn10_dmcu_load_iram(struct dmcu *dmcu, + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + unsigned int count = 0; + +- REG_UPDATE(DMCU_CTRL, DMCU_ENABLE, 1); ++ /* If microcontroller is not running, do nothing */ ++ if (dmcu->dmcu_state != DMCU_RUNNING) ++ return false; + + /* Enable write access to IRAM */ + REG_UPDATE_2(DMCU_RAM_ACCESS_CTRL, +@@ -313,6 +445,19 @@ bool dcn10_dmcu_load_iram(struct dmcu *dmcu, + IRAM_HOST_ACCESS_EN, 0, + IRAM_WR_ADDR_AUTO_INC, 0); + ++ /* Wait until microcontroller is ready to process interrupt */ ++ REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); ++ ++ /* Set command to signal IRAM is loaded and to initialize IRAM */ ++ REG_UPDATE(MASTER_COMM_CMD_REG, MASTER_COMM_CMD_REG_BYTE0, ++ MCP_INIT_IRAM); ++ ++ /* Notify microcontroller of new command */ ++ REG_UPDATE(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 1); ++ ++ /* Ensure command has been executed before continuing */ ++ REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 100, 800); ++ + return true; + } + +@@ -320,7 +465,11 @@ static void dcn10_get_dmcu_psr_state(struct dmcu *dmcu, uint32_t *psr_state) + { + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + +- uint32_t psrStateOffset = 0xf0; ++ uint32_t psr_state_offset = 0xf0; ++ ++ /* If microcontroller is not running, do nothing */ ++ if (dmcu->dmcu_state != DMCU_RUNNING) ++ return; + + /* Enable write access to IRAM */ + REG_UPDATE(DMCU_RAM_ACCESS_CTRL, IRAM_HOST_ACCESS_EN, 1); +@@ -328,7 +477,7 @@ static void dcn10_get_dmcu_psr_state(struct dmcu *dmcu, uint32_t *psr_state) + REG_WAIT(DMU_MEM_PWR_CNTL, DMCU_IRAM_MEM_PWR_STATE, 0, 2, 10); + + /* Write address to IRAM_RD_ADDR in DMCU_IRAM_RD_CTRL */ +- REG_WRITE(DMCU_IRAM_RD_CTRL, psrStateOffset); ++ REG_WRITE(DMCU_IRAM_RD_CTRL, psr_state_offset); + + /* Read data from IRAM_RD_DATA in DMCU_IRAM_RD_DATA*/ + *psr_state = REG_READ(DMCU_IRAM_RD_DATA); +@@ -348,6 +497,10 @@ static void dcn10_dmcu_set_psr_enable(struct dmcu *dmcu, bool enable, bool wait) + unsigned int retryCount; + uint32_t psr_state = 0; + ++ /* If microcontroller is not running, do nothing */ ++ if (dmcu->dmcu_state != DMCU_RUNNING) ++ return; ++ + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, + dmcu_wait_reg_ready_interval, +@@ -399,6 +552,10 @@ static void dcn10_dmcu_setup_psr(struct dmcu *dmcu, + union dce_dmcu_psr_config_data_reg2 masterCmdData2; + union dce_dmcu_psr_config_data_reg3 masterCmdData3; + ++ /* If microcontroller is not running, do nothing */ ++ if (dmcu->dmcu_state != DMCU_RUNNING) ++ return; ++ + link->link_enc->funcs->psr_program_dp_dphy_fast_training(link->link_enc, + psr_context->psrExitLinkTrainingRequired); + +@@ -505,6 +662,11 @@ static void dcn10_psr_wait_loop( + { + struct dce_dmcu *dmcu_dce = TO_DCE_DMCU(dmcu); + union dce_dmcu_psr_config_data_wait_loop_reg1 masterCmdData1; ++ ++ /* If microcontroller is not running, do nothing */ ++ if (dmcu->dmcu_state != DMCU_RUNNING) ++ return; ++ + if (wait_loop_number != 0) { + /* waitDMCUReadyForCmd */ + REG_WAIT(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, 0, 1, 10000); +@@ -531,6 +693,7 @@ static void dcn10_get_psr_wait_loop(unsigned int *psr_wait_loop_number) + #endif + + static const struct dmcu_funcs dce_funcs = { ++ .dmcu_init = dce_dmcu_init, + .load_iram = dce_dmcu_load_iram, + .set_psr_enable = dce_dmcu_set_psr_enable, + .setup_psr = dce_dmcu_setup_psr, +@@ -541,6 +704,7 @@ static const struct dmcu_funcs dce_funcs = { + + #if defined(CONFIG_DRM_AMD_DC_DCN1_0) + static const struct dmcu_funcs dcn10_funcs = { ++ .dmcu_init = dcn10_dmcu_init, + .load_iram = dcn10_dmcu_load_iram, + .set_psr_enable = dcn10_dmcu_set_psr_enable, + .setup_psr = dcn10_dmcu_setup_psr, +diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h +index b85f53c..4c25e2d 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h ++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.h +@@ -31,6 +31,7 @@ + + #define DMCU_COMMON_REG_LIST_DCE_BASE() \ + SR(DMCU_CTRL), \ ++ SR(DMCU_STATUS), \ + SR(DMCU_RAM_ACCESS_CTRL), \ + SR(DMCU_IRAM_WR_CTRL), \ + SR(DMCU_IRAM_WR_DATA), \ +@@ -42,7 +43,8 @@ + SR(DMCU_IRAM_RD_CTRL), \ + SR(DMCU_IRAM_RD_DATA), \ + SR(DMCU_INTERRUPT_TO_UC_EN_MASK), \ +- SR(SMU_INTERRUPT_CONTROL) ++ SR(SMU_INTERRUPT_CONTROL), \ ++ SR(DC_DMCU_SCRATCH) + + #define DMCU_DCE110_COMMON_REG_LIST() \ + DMCU_COMMON_REG_LIST_DCE_BASE(), \ +@@ -58,10 +60,14 @@ + #define DMCU_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(mask_sh) \ + DMCU_SF(DMCU_CTRL, \ + DMCU_ENABLE, mask_sh), \ ++ DMCU_SF(DMCU_STATUS, \ ++ UC_IN_STOP_MODE, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_HOST_ACCESS_EN, mask_sh), \ + DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ + IRAM_WR_ADDR_AUTO_INC, mask_sh), \ ++ DMCU_SF(DMCU_RAM_ACCESS_CTRL, \ ++ IRAM_RD_ADDR_AUTO_INC, mask_sh), \ + DMCU_SF(MASTER_COMM_CMD_REG, \ + MASTER_COMM_CMD_REG_BYTE0, mask_sh), \ + DMCU_SF(MASTER_COMM_CNTL_REG, MASTER_COMM_INTERRUPT, mask_sh), \ +@@ -89,7 +95,9 @@ + type DMCU_IRAM_MEM_PWR_STATE; \ + type IRAM_HOST_ACCESS_EN; \ + type IRAM_WR_ADDR_AUTO_INC; \ ++ type IRAM_RD_ADDR_AUTO_INC; \ + type DMCU_ENABLE; \ ++ type UC_IN_STOP_MODE; \ + type MASTER_COMM_CMD_REG_BYTE0; \ + type MASTER_COMM_INTERRUPT; \ + type DPHY_RX_FAST_TRAINING_CAPABLE; \ +@@ -112,6 +120,7 @@ struct dce_dmcu_mask { + + struct dce_dmcu_registers { + uint32_t DMCU_CTRL; ++ uint32_t DMCU_STATUS; + uint32_t DMCU_RAM_ACCESS_CTRL; + uint32_t DCI_MEM_PWR_STATUS; + uint32_t DMU_MEM_PWR_CNTL; +@@ -127,6 +136,7 @@ struct dce_dmcu_registers { + uint32_t DMCU_IRAM_RD_DATA; + uint32_t DMCU_INTERRUPT_TO_UC_EN_MASK; + uint32_t SMU_INTERRUPT_CONTROL; ++ uint32_t DC_DMCU_SCRATCH; + }; + + struct dce_dmcu { +diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +index d23cd16..63bed92 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +@@ -31,6 +31,7 @@ + #include "dce110/dce110_hw_sequencer.h" + #include "dce/dce_hwseq.h" + #include "abm.h" ++#include "dmcu.h" + #include "dcn10/dcn10_timing_generator.h" + #include "dcn10/dcn10_dpp.h" + #include "dcn10/dcn10_mpc.h" +@@ -863,6 +864,7 @@ static void dcn10_init_hw(struct dc *dc) + { + int i; + struct abm *abm = dc->res_pool->abm; ++ struct dmcu *dmcu = dc->res_pool->dmcu; + struct dce_hwseq *hws = dc->hwseq; + + if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { +@@ -925,6 +927,9 @@ static void dcn10_init_hw(struct dc *dc) + abm->funcs->abm_init(abm); + } + ++ if (dmcu != NULL) ++ dmcu->funcs->dmcu_init(dmcu); ++ + /* power AFMT HDMI memory TODO: may move to dis/en output save power*/ + REG_WRITE(DIO_MEM_PWR_CTRL, 0); + +diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h +index 0574c29..67996c6 100644 +--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h ++++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dmcu.h +@@ -27,12 +27,28 @@ + + #include "dm_services_types.h" + ++enum dmcu_state { ++ DMCU_NOT_INITIALIZED = 0, ++ DMCU_RUNNING = 1 ++}; ++ ++struct dmcu_version { ++ unsigned int day; ++ unsigned int month; ++ unsigned int year; ++ unsigned int interface_version; ++}; ++ + struct dmcu { + struct dc_context *ctx; + const struct dmcu_funcs *funcs; ++ ++ enum dmcu_state dmcu_state; ++ struct dmcu_version dmcu_version; + }; + + struct dmcu_funcs { ++ bool (*dmcu_init)(struct dmcu *dmcu); + bool (*load_iram)(struct dmcu *dmcu, + unsigned int start_offset, + const char *src, +-- +2.7.4 + |