drm/amd/display: Add I2C memory low power support
authorJacky Liao <ziyu.liao@amd.com>
Tue, 27 Oct 2020 14:56:18 +0000 (10:56 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 16 Nov 2020 17:19:22 +0000 (12:19 -0500)
[Why]
The I2C memory blocks should be powered down when they are not in use.
This will reduce power consumption.

[How]
1. Write to I2C_LIGHT_SLEEP_FORCE to put memory in light sleep when
   released
2. Added a debug option to allow this behaviour to be turned off

Signed-off-by: Jacky Liao <ziyu.liao@amd.com>
Acked-by: Bindu Ramamurthy <bindu.r@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.c
drivers/gpu/drm/amd/display/dc/dce/dce_i2c_hw.h
drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c

index 52e8196..e282c22 100644 (file)
@@ -419,6 +419,7 @@ struct dc_bw_validation_profile {
 
 union mem_low_power_enable_options {
        struct {
+               bool i2c: 1;
                bool mpc: 1;
                bool optc: 1;
        } bits;
index 3e34afe..7fbd92f 100644 (file)
@@ -293,6 +293,14 @@ static bool setup_engine(
 {
        uint32_t i2c_setup_limit = I2C_SETUP_TIME_LIMIT_DCE;
        uint32_t  reset_length = 0;
+
+        if (dce_i2c_hw->ctx->dc->debug.enable_mem_low_power.bits.i2c) {
+            if (dce_i2c_hw->regs->DIO_MEM_PWR_CTRL) {
+                    REG_UPDATE(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, 0);
+                    REG_WAIT(DIO_MEM_PWR_STATUS, I2C_MEM_PWR_STATE, 0, 0, 5);
+                    }
+            }
+
        /* we have checked I2c not used by DMCU, set SW use I2C REQ to 1 to indicate SW using it*/
        REG_UPDATE(DC_I2C_ARBITRATION, DC_I2C_SW_USE_I2C_REG_REQ, 1);
 
@@ -369,6 +377,10 @@ static void release_engine(
        REG_UPDATE_2(DC_I2C_ARBITRATION, DC_I2C_SW_DONE_USING_I2C_REG, 1,
                DC_I2C_SW_USE_I2C_REG_REQ, 0);
 
+       if (dce_i2c_hw->ctx->dc->debug.enable_mem_low_power.bits.i2c) {
+               if (dce_i2c_hw->regs->DIO_MEM_PWR_CTRL)
+                       REG_UPDATE(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, 1);
+       }
 }
 
 struct dce_i2c_hw *acquire_i2c_hw_engine(
index fb055e6..2309f2b 100644 (file)
@@ -95,6 +95,11 @@ enum {
        SR(DC_I2C_DATA),\
        SR(MICROSECOND_TIME_BASE_DIV)
 
+#define I2C_HW_ENGINE_COMMON_REG_LIST_DCN30(id)\
+       I2C_HW_ENGINE_COMMON_REG_LIST(id),\
+       SR(DIO_MEM_PWR_CTRL),\
+       SR(DIO_MEM_PWR_STATUS)
+
 #define I2C_SF(reg_name, field_name, post_fix)\
        .field_name = reg_name ## __ ## field_name ## post_fix
 
@@ -179,6 +184,8 @@ struct dce_i2c_shift {
        uint8_t XTAL_REF_DIV;
        uint8_t DC_I2C_DDC1_SEND_RESET_LENGTH;
        uint8_t DC_I2C_REG_RW_CNTL_STATUS;
+       uint8_t I2C_LIGHT_SLEEP_FORCE;
+       uint8_t I2C_MEM_PWR_STATE;
 };
 
 struct dce_i2c_mask {
@@ -220,12 +227,19 @@ struct dce_i2c_mask {
        uint32_t XTAL_REF_DIV;
        uint32_t DC_I2C_DDC1_SEND_RESET_LENGTH;
        uint32_t DC_I2C_REG_RW_CNTL_STATUS;
+       uint32_t I2C_LIGHT_SLEEP_FORCE;
+       uint32_t I2C_MEM_PWR_STATE;
 };
 
 #define I2C_COMMON_MASK_SH_LIST_DCN2(mask_sh)\
        I2C_COMMON_MASK_SH_LIST_DCE110(mask_sh),\
        I2C_SF(DC_I2C_DDC1_SETUP, DC_I2C_DDC1_SEND_RESET_LENGTH, mask_sh)
 
+#define I2C_COMMON_MASK_SH_LIST_DCN30(mask_sh)\
+       I2C_COMMON_MASK_SH_LIST_DCN2(mask_sh),\
+       I2C_SF(DIO_MEM_PWR_CTRL, I2C_LIGHT_SLEEP_FORCE, mask_sh),\
+       I2C_SF(DIO_MEM_PWR_STATUS, I2C_MEM_PWR_STATE, mask_sh)
+
 struct dce_i2c_registers {
        uint32_t SETUP;
        uint32_t SPEED;
@@ -239,6 +253,8 @@ struct dce_i2c_registers {
        uint32_t DC_I2C_TRANSACTION3;
        uint32_t DC_I2C_DATA;
        uint32_t MICROSECOND_TIME_BASE_DIV;
+       uint32_t DIO_MEM_PWR_CTRL;
+       uint32_t DIO_MEM_PWR_STATUS;
 };
 
 enum dce_i2c_transaction_address_space {
index d5c81ad..5e126fd 100644 (file)
@@ -933,7 +933,7 @@ static struct dce_aux *dcn30_aux_engine_create(
        return &aux_engine->base;
 }
 
-#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST(id) }
+#define i2c_inst_regs(id) { I2C_HW_ENGINE_COMMON_REG_LIST_DCN30(id) }
 
 static const struct dce_i2c_registers i2c_hw_regs[] = {
                i2c_inst_regs(1),
@@ -945,11 +945,11 @@ static const struct dce_i2c_registers i2c_hw_regs[] = {
 };
 
 static const struct dce_i2c_shift i2c_shifts = {
-               I2C_COMMON_MASK_SH_LIST_DCN2(__SHIFT)
+               I2C_COMMON_MASK_SH_LIST_DCN30(__SHIFT)
 };
 
 static const struct dce_i2c_mask i2c_masks = {
-               I2C_COMMON_MASK_SH_LIST_DCN2(_MASK)
+               I2C_COMMON_MASK_SH_LIST_DCN30(_MASK)
 };
 
 static struct dce_i2c_hw *dcn30_i2c_hw_create(