aboutsummaryrefslogtreecommitdiffstats
path: root/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/2643-drm-amd-display-DMCU-FW-loading-from-PSP.patch
diff options
context:
space:
mode:
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.patch434
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
+