#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,
{
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);
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);
}
#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)
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,
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;
}
{
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);
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);
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,
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);
{
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);
#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,
#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,