drm/radeon: add dpm support for CI dGPUs (v2)
authorAlex Deucher <alexander.deucher@amd.com>
Wed, 14 Aug 2013 05:03:41 +0000 (01:03 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Fri, 30 Aug 2013 20:30:29 +0000 (16:30 -0400)
This adds dpm support for btc asics. This includes:
- dynamic engine clock scaling
- dynamic memory clock scaling
- dynamic voltage scaling
- dynamic pcie gen switching

Set radeon.dpm=1 to enable.

v2: remove unused radeon_atombios.c changes,
    make missing smc ucode non-fatal

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
15 files changed:
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/ci_dpm.c [new file with mode: 0644]
drivers/gpu/drm/radeon/ci_dpm.h [new file with mode: 0644]
drivers/gpu/drm/radeon/ci_smc.c [new file with mode: 0644]
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/cikd.h
drivers/gpu/drm/radeon/ppsmc.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_asic.c
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_atombios.c
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_ucode.h
drivers/gpu/drm/radeon/si_dpm.c
drivers/gpu/drm/radeon/smu7_discrete.h [new file with mode: 0644]

index d3265b5d466139ee1e4442fc482231e5692765fa..ea913cc681b4e31ef1293d78efae8698420935ef 100644 (file)
@@ -79,7 +79,8 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
        si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
        r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
        rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
-       trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o
+       trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
+       ci_dpm.o
 
 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
 radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c
new file mode 100644 (file)
index 0000000..72ab92b
--- /dev/null
@@ -0,0 +1,5006 @@
+/*
+ * Copyright 2013 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.
+ *
+ */
+
+#include "drmP.h"
+#include "radeon.h"
+#include "cikd.h"
+#include "r600_dpm.h"
+#include "ci_dpm.h"
+#include "atom.h"
+
+#define MC_CG_ARB_FREQ_F0           0x0a
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define MC_CG_ARB_FREQ_F2           0x0c
+#define MC_CG_ARB_FREQ_F3           0x0d
+
+#define SMC_RAM_END 0x40000
+
+#define VOLTAGE_SCALE               4
+#define VOLTAGE_VID_OFFSET_SCALE1    625
+#define VOLTAGE_VID_OFFSET_SCALE2    100
+
+static const struct ci_pt_defaults defaults_bonaire_xt =
+{
+       1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
+       { 0x79,  0x253, 0x25D, 0xAE,  0x72,  0x80,  0x83,  0x86,  0x6F,  0xC8,  0xC9,  0xC9,  0x2F,  0x4D,  0x61  },
+       { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 }
+};
+
+static const struct ci_pt_defaults defaults_bonaire_pro =
+{
+       1, 0xF, 0xFD, 0x19, 5, 45, 0, 0x65062,
+       { 0x8C,  0x23F, 0x244, 0xA6,  0x83,  0x85,  0x86,  0x86,  0x83,  0xDB,  0xDB,  0xDA,  0x67,  0x60,  0x5F  },
+       { 0x187, 0x193, 0x193, 0x1C7, 0x1D1, 0x1D1, 0x210, 0x219, 0x219, 0x266, 0x26C, 0x26C, 0x2C9, 0x2CB, 0x2CB }
+};
+
+static const struct ci_pt_defaults defaults_saturn_xt =
+{
+       1, 0xF, 0xFD, 0x19, 5, 55, 0, 0x70000,
+       { 0x8C,  0x247, 0x249, 0xA6,  0x80,  0x81,  0x8B,  0x89,  0x86,  0xC9,  0xCA,  0xC9,  0x4D,  0x4D,  0x4D  },
+       { 0x187, 0x187, 0x187, 0x1C7, 0x1C7, 0x1C7, 0x210, 0x210, 0x210, 0x266, 0x266, 0x266, 0x2C9, 0x2C9, 0x2C9 }
+};
+
+static const struct ci_pt_defaults defaults_saturn_pro =
+{
+       1, 0xF, 0xFD, 0x19, 5, 55, 0, 0x30000,
+       { 0x96,  0x21D, 0x23B, 0xA1,  0x85,  0x87,  0x83,  0x84,  0x81,  0xE6,  0xE6,  0xE6,  0x71,  0x6A,  0x6A  },
+       { 0x193, 0x19E, 0x19E, 0x1D2, 0x1DC, 0x1DC, 0x21A, 0x223, 0x223, 0x26E, 0x27E, 0x274, 0x2CF, 0x2D2, 0x2D2 }
+};
+
+static const struct ci_pt_config_reg didt_config_ci[] =
+{
+       { 0x10, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x10, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x10, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x10, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x11, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x11, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x11, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x11, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x12, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x12, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x12, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x12, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x2, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x2, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x2, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x1, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x1, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x0, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x30, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x30, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x30, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x30, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x31, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x31, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x31, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x31, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x32, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x32, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x32, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x32, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x22, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x22, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x22, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x21, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x21, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x20, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x50, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x50, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x50, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x50, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x51, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x51, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x51, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x51, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x52, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x52, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x52, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x52, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x42, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x42, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x42, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x41, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x41, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x40, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x70, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x70, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x70, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x70, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x71, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x71, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x71, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x71, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x72, 0x000000ff, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x72, 0x0000ff00, 8, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x72, 0x00ff0000, 16, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x72, 0xff000000, 24, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x62, 0x00003fff, 0, 0x4, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x62, 0x03ff0000, 16, 0x80, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x62, 0x78000000, 27, 0x3, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x61, 0x0000ffff, 0, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x61, 0xffff0000, 16, 0x3FFF, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0x60, 0x00000001, 0, 0x0, CISLANDS_CONFIGREG_DIDT_IND },
+       { 0xFFFFFFFF }
+};
+
+extern u8 rv770_get_memory_module_index(struct radeon_device *rdev);
+extern int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
+                                      u32 arb_freq_src, u32 arb_freq_dest);
+extern u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock);
+extern u8 si_get_mclk_frequency_ratio(u32 memory_clock, bool strobe_mode);
+extern void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
+                                                    u32 max_voltage_steps,
+                                                    struct atom_voltage_table *voltage_table);
+extern void cik_enter_rlc_safe_mode(struct radeon_device *rdev);
+extern void cik_exit_rlc_safe_mode(struct radeon_device *rdev);
+
+static int ci_get_std_voltage_value_sidd(struct radeon_device *rdev,
+                                        struct atom_voltage_table_entry *voltage_table,
+                                        u16 *std_voltage_hi_sidd, u16 *std_voltage_lo_sidd);
+static int ci_set_power_limit(struct radeon_device *rdev, u32 n);
+static int ci_set_overdrive_target_tdp(struct radeon_device *rdev,
+                                      u32 target_tdp);
+static int ci_update_uvd_dpm(struct radeon_device *rdev, bool gate);
+
+static struct ci_power_info *ci_get_pi(struct radeon_device *rdev)
+{
+        struct ci_power_info *pi = rdev->pm.dpm.priv;
+
+        return pi;
+}
+
+static struct ci_ps *ci_get_ps(struct radeon_ps *rps)
+{
+       struct ci_ps *ps = rps->ps_priv;
+
+       return ps;
+}
+
+static void ci_initialize_powertune_defaults(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       switch (rdev->pdev->device) {
+        case 0x6650:
+        case 0x6658:
+        case 0x665C:
+        default:
+               pi->powertune_defaults = &defaults_bonaire_xt;
+               break;
+        case 0x6651:
+        case 0x665D:
+               pi->powertune_defaults = &defaults_bonaire_pro;
+               break;
+        case 0x6640:
+               pi->powertune_defaults = &defaults_saturn_xt;
+               break;
+        case 0x6641:
+               pi->powertune_defaults = &defaults_saturn_pro;
+               break;
+       }
+
+       pi->dte_tj_offset = 0;
+
+       pi->caps_power_containment = true;
+       pi->caps_cac = false;
+       pi->caps_sq_ramping = false;
+       pi->caps_db_ramping = false;
+       pi->caps_td_ramping = false;
+       pi->caps_tcp_ramping = false;
+
+       if (pi->caps_power_containment) {
+               pi->caps_cac = true;
+               pi->enable_bapm_feature = true;
+               pi->enable_tdc_limit_feature = true;
+               pi->enable_pkg_pwr_tracking_feature = true;
+       }
+}
+
+static u8 ci_convert_to_vid(u16 vddc)
+{
+       return (6200 - (vddc * VOLTAGE_SCALE)) / 25;
+}
+
+static int ci_populate_bapm_vddc_vid_sidd(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u8 *hi_vid = pi->smc_powertune_table.BapmVddCVidHiSidd;
+       u8 *lo_vid = pi->smc_powertune_table.BapmVddCVidLoSidd;
+       u8 *hi2_vid = pi->smc_powertune_table.BapmVddCVidHiSidd2;
+       u32 i;
+
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries == NULL)
+               return -EINVAL;
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.count > 8)
+               return -EINVAL;
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.count !=
+           rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count)
+               return -EINVAL;
+
+       for (i = 0; i < rdev->pm.dpm.dyn_state.cac_leakage_table.count; i++) {
+               if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) {
+                       lo_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc1);
+                       hi_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc2);
+                       hi2_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc3);
+               } else {
+                       lo_vid[i] = ci_convert_to_vid(rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc);
+                       hi_vid[i] = ci_convert_to_vid((u16)rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage);
+               }
+       }
+       return 0;
+}
+
+static int ci_populate_vddc_vid(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u8 *vid = pi->smc_powertune_table.VddCVid;
+       u32 i;
+
+       if (pi->vddc_voltage_table.count > 8)
+               return -EINVAL;
+
+       for (i = 0; i < pi->vddc_voltage_table.count; i++)
+               vid[i] = ci_convert_to_vid(pi->vddc_voltage_table.entries[i].value);
+
+       return 0;
+}
+
+static int ci_populate_svi_load_line(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;
+
+       pi->smc_powertune_table.SviLoadLineEn = pt_defaults->svi_load_line_en;
+       pi->smc_powertune_table.SviLoadLineVddC = pt_defaults->svi_load_line_vddc;
+       pi->smc_powertune_table.SviLoadLineTrimVddC = 3;
+       pi->smc_powertune_table.SviLoadLineOffsetVddC = 0;
+
+       return 0;
+}
+
+static int ci_populate_tdc_limit(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;
+       u16 tdc_limit;
+
+       tdc_limit = rdev->pm.dpm.dyn_state.cac_tdp_table->tdc * 256;
+       pi->smc_powertune_table.TDC_VDDC_PkgLimit = cpu_to_be16(tdc_limit);
+       pi->smc_powertune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+               pt_defaults->tdc_vddc_throttle_release_limit_perc;
+       pi->smc_powertune_table.TDC_MAWt = pt_defaults->tdc_mawt;
+
+       return 0;
+}
+
+static int ci_populate_dw8(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;
+       int ret;
+
+       ret = ci_read_smc_sram_dword(rdev,
+                                    SMU7_FIRMWARE_HEADER_LOCATION +
+                                    offsetof(SMU7_Firmware_Header, PmFuseTable) +
+                                    offsetof(SMU7_Discrete_PmFuses, TdcWaterfallCtl),
+                                    (u32 *)&pi->smc_powertune_table.TdcWaterfallCtl,
+                                    pi->sram_end);
+       if (ret)
+               return -EINVAL;
+       else
+               pi->smc_powertune_table.TdcWaterfallCtl = pt_defaults->tdc_waterfall_ctl;
+
+       return 0;
+}
+
+static int ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u8 *hi_vid = pi->smc_powertune_table.BapmVddCVidHiSidd;
+       u8 *lo_vid = pi->smc_powertune_table.BapmVddCVidLoSidd;
+       int i, min, max;
+
+       min = max = hi_vid[0];
+       for (i = 0; i < 8; i++) {
+               if (0 != hi_vid[i]) {
+                       if (min > hi_vid[i])
+                               min = hi_vid[i];
+                       if (max < hi_vid[i])
+                               max = hi_vid[i];
+               }
+
+               if (0 != lo_vid[i]) {
+                       if (min > lo_vid[i])
+                               min = lo_vid[i];
+                       if (max < lo_vid[i])
+                               max = lo_vid[i];
+               }
+       }
+
+       if ((min == 0) || (max == 0))
+               return -EINVAL;
+       pi->smc_powertune_table.GnbLPMLMaxVid = (u8)max;
+       pi->smc_powertune_table.GnbLPMLMinVid = (u8)min;
+
+       return 0;
+}
+
+static int ci_populate_bapm_vddc_base_leakage_sidd(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u16 hi_sidd = pi->smc_powertune_table.BapmVddCBaseLeakageHiSidd;
+       u16 lo_sidd = pi->smc_powertune_table.BapmVddCBaseLeakageLoSidd;
+       struct radeon_cac_tdp_table *cac_tdp_table =
+               rdev->pm.dpm.dyn_state.cac_tdp_table;
+
+       hi_sidd = cac_tdp_table->high_cac_leakage / 100 * 256;
+       lo_sidd = cac_tdp_table->low_cac_leakage / 100 * 256;
+
+       pi->smc_powertune_table.BapmVddCBaseLeakageHiSidd = cpu_to_be16(hi_sidd);
+       pi->smc_powertune_table.BapmVddCBaseLeakageLoSidd = cpu_to_be16(lo_sidd);
+
+       return 0;
+}
+
+static int ci_populate_bapm_parameters_in_dpm_table(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct ci_pt_defaults *pt_defaults = pi->powertune_defaults;
+       SMU7_Discrete_DpmTable  *dpm_table = &pi->smc_state_table;
+       struct radeon_cac_tdp_table *cac_tdp_table =
+               rdev->pm.dpm.dyn_state.cac_tdp_table;
+       struct radeon_ppm_table *ppm = rdev->pm.dpm.dyn_state.ppm_table;
+       int i, j, k;
+       const u16 *def1;
+       const u16 *def2;
+
+       dpm_table->DefaultTdp = cac_tdp_table->tdp * 256;
+       dpm_table->TargetTdp = cac_tdp_table->configurable_tdp * 256;
+
+       dpm_table->DTETjOffset = (u8)pi->dte_tj_offset;
+       dpm_table->GpuTjMax =
+               (u8)(pi->thermal_temp_setting.temperature_high / 1000);
+       dpm_table->GpuTjHyst = 8;
+
+       dpm_table->DTEAmbientTempBase = pt_defaults->dte_ambient_temp_base;
+
+       if (ppm) {
+               dpm_table->PPM_PkgPwrLimit = cpu_to_be16((u16)ppm->dgpu_tdp * 256 / 1000);
+               dpm_table->PPM_TemperatureLimit = cpu_to_be16((u16)ppm->tj_max * 256);
+       } else {
+               dpm_table->PPM_PkgPwrLimit = cpu_to_be16(0);
+               dpm_table->PPM_TemperatureLimit = cpu_to_be16(0);
+       }
+
+       dpm_table->BAPM_TEMP_GRADIENT = cpu_to_be32(pt_defaults->bapm_temp_gradient);
+       def1 = pt_defaults->bapmti_r;
+       def2 = pt_defaults->bapmti_rc;
+
+       for (i = 0; i < SMU7_DTE_ITERATIONS; i++) {
+               for (j = 0; j < SMU7_DTE_SOURCES; j++) {
+                       for (k = 0; k < SMU7_DTE_SINKS; k++) {
+                               dpm_table->BAPMTI_R[i][j][k] = cpu_to_be16(*def1);
+                               dpm_table->BAPMTI_RC[i][j][k] = cpu_to_be16(*def2);
+                               def1++;
+                               def2++;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int ci_populate_pm_base(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 pm_fuse_table_offset;
+       int ret;
+
+       if (pi->caps_power_containment) {
+               ret = ci_read_smc_sram_dword(rdev,
+                                            SMU7_FIRMWARE_HEADER_LOCATION +
+                                            offsetof(SMU7_Firmware_Header, PmFuseTable),
+                                            &pm_fuse_table_offset, pi->sram_end);
+               if (ret)
+                       return ret;
+               ret = ci_populate_bapm_vddc_vid_sidd(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_populate_vddc_vid(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_populate_svi_load_line(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_populate_tdc_limit(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_populate_dw8(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_min_max_v_gnbl_pm_lid_from_bapm_vddc(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_populate_bapm_vddc_base_leakage_sidd(rdev);
+               if (ret)
+                       return ret;
+               ret = ci_copy_bytes_to_smc(rdev, pm_fuse_table_offset,
+                                          (u8 *)&pi->smc_powertune_table,
+                                          sizeof(SMU7_Discrete_PmFuses), pi->sram_end);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void ci_do_enable_didt(struct radeon_device *rdev, const bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 data;
+
+       if (pi->caps_sq_ramping) {
+               data = RREG32_DIDT(DIDT_SQ_CTRL0);
+               if (enable)
+                       data |= DIDT_CTRL_EN;
+               else
+                       data &= ~DIDT_CTRL_EN;
+               WREG32_DIDT(DIDT_SQ_CTRL0, data);
+       }
+
+       if (pi->caps_db_ramping) {
+               data = RREG32_DIDT(DIDT_DB_CTRL0);
+               if (enable)
+                       data |= DIDT_CTRL_EN;
+               else
+                       data &= ~DIDT_CTRL_EN;
+               WREG32_DIDT(DIDT_DB_CTRL0, data);
+       }
+
+       if (pi->caps_td_ramping) {
+               data = RREG32_DIDT(DIDT_TD_CTRL0);
+               if (enable)
+                       data |= DIDT_CTRL_EN;
+               else
+                       data &= ~DIDT_CTRL_EN;
+               WREG32_DIDT(DIDT_TD_CTRL0, data);
+       }
+
+       if (pi->caps_tcp_ramping) {
+               data = RREG32_DIDT(DIDT_TCP_CTRL0);
+               if (enable)
+                       data |= DIDT_CTRL_EN;
+               else
+                       data &= ~DIDT_CTRL_EN;
+               WREG32_DIDT(DIDT_TCP_CTRL0, data);
+       }
+}
+
+static int ci_program_pt_config_registers(struct radeon_device *rdev,
+                                         const struct ci_pt_config_reg *cac_config_regs)
+{
+       const struct ci_pt_config_reg *config_regs = cac_config_regs;
+       u32 data;
+       u32 cache = 0;
+
+       if (config_regs == NULL)
+               return -EINVAL;
+
+       while (config_regs->offset != 0xFFFFFFFF) {
+               if (config_regs->type == CISLANDS_CONFIGREG_CACHE) {
+                       cache |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+               } else {
+                       switch (config_regs->type) {
+                       case CISLANDS_CONFIGREG_SMC_IND:
+                               data = RREG32_SMC(config_regs->offset);
+                               break;
+                       case CISLANDS_CONFIGREG_DIDT_IND:
+                               data = RREG32_DIDT(config_regs->offset);
+                               break;
+                       default:
+                               data = RREG32(config_regs->offset << 2);
+                               break;
+                       }
+
+                       data &= ~config_regs->mask;
+                       data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+                       data |= cache;
+
+                       switch (config_regs->type) {
+                       case CISLANDS_CONFIGREG_SMC_IND:
+                               WREG32_SMC(config_regs->offset, data);
+                               break;
+                       case CISLANDS_CONFIGREG_DIDT_IND:
+                               WREG32_DIDT(config_regs->offset, data);
+                               break;
+                       default:
+                               WREG32(config_regs->offset << 2, data);
+                               break;
+                       }
+                       cache = 0;
+               }
+               config_regs++;
+       }
+       return 0;
+}
+
+static int ci_enable_didt(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int ret;
+
+       if (pi->caps_sq_ramping || pi->caps_db_ramping ||
+           pi->caps_td_ramping || pi->caps_tcp_ramping) {
+               cik_enter_rlc_safe_mode(rdev);
+
+               if (enable) {
+                       ret = ci_program_pt_config_registers(rdev, didt_config_ci);
+                       if (ret) {
+                               cik_exit_rlc_safe_mode(rdev);
+                               return ret;
+                       }
+               }
+
+               ci_do_enable_didt(rdev, enable);
+
+               cik_exit_rlc_safe_mode(rdev);
+       }
+
+       return 0;
+}
+
+static int ci_enable_power_containment(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (enable) {
+               pi->power_containment_features = 0;
+               if (pi->caps_power_containment) {
+                       if (pi->enable_bapm_feature) {
+                               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableDTE);
+                               if (smc_result != PPSMC_Result_OK)
+                                       ret = -EINVAL;
+                               else
+                                       pi->power_containment_features |= POWERCONTAINMENT_FEATURE_BAPM;
+                       }
+
+                       if (pi->enable_tdc_limit_feature) {
+                               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_TDCLimitEnable);
+                               if (smc_result != PPSMC_Result_OK)
+                                       ret = -EINVAL;
+                               else
+                                       pi->power_containment_features |= POWERCONTAINMENT_FEATURE_TDCLimit;
+                       }
+
+                       if (pi->enable_pkg_pwr_tracking_feature) {
+                               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_PkgPwrLimitEnable);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                               } else {
+                                       struct radeon_cac_tdp_table *cac_tdp_table =
+                                               rdev->pm.dpm.dyn_state.cac_tdp_table;
+                                       u32 default_pwr_limit =
+                                               (u32)(cac_tdp_table->maximum_power_delivery_limit * 256);
+
+                                       pi->power_containment_features |= POWERCONTAINMENT_FEATURE_PkgPwrLimit;
+
+                                       ci_set_power_limit(rdev, default_pwr_limit);
+                               }
+                       }
+               }
+       } else {
+               if (pi->caps_power_containment && pi->power_containment_features) {
+                       if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_TDCLimit)
+                               ci_send_msg_to_smc(rdev, PPSMC_MSG_TDCLimitDisable);
+
+                       if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_BAPM)
+                               ci_send_msg_to_smc(rdev, PPSMC_MSG_DisableDTE);
+
+                       if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_PkgPwrLimit)
+                               ci_send_msg_to_smc(rdev, PPSMC_MSG_PkgPwrLimitDisable);
+                       pi->power_containment_features = 0;
+               }
+       }
+
+       return ret;
+}
+
+static int ci_enable_smc_cac(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (pi->caps_cac) {
+               if (enable) {
+                       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableCac);
+                       if (smc_result != PPSMC_Result_OK) {
+                               ret = -EINVAL;
+                               pi->cac_enabled = false;
+                       } else {
+                               pi->cac_enabled = true;
+                       }
+               } else if (pi->cac_enabled) {
+                       ci_send_msg_to_smc(rdev, PPSMC_MSG_DisableCac);
+                       pi->cac_enabled = false;
+               }
+       }
+
+       return ret;
+}
+
+static int ci_power_control_set_level(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_cac_tdp_table *cac_tdp_table =
+               rdev->pm.dpm.dyn_state.cac_tdp_table;
+       s32 adjust_percent;
+       s32 target_tdp;
+       int ret = 0;
+       bool adjust_polarity = false; /* ??? */
+
+       if (pi->caps_power_containment &&
+           (pi->power_containment_features & POWERCONTAINMENT_FEATURE_BAPM)) {
+               adjust_percent = adjust_polarity ?
+                       rdev->pm.dpm.tdp_adjustment : (-1 * rdev->pm.dpm.tdp_adjustment);
+               target_tdp = ((100 + adjust_percent) *
+                             (s32)cac_tdp_table->configurable_tdp) / 100;
+               target_tdp *= 256;
+
+               ret = ci_set_overdrive_target_tdp(rdev, (u32)target_tdp);
+       }
+
+       return ret;
+}
+
+static void ci_dpm_powergate_uvd(struct radeon_device *rdev, bool gate)
+{
+       ci_update_uvd_dpm(rdev, gate);
+}
+
+static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
+                                       struct radeon_ps *rps)
+{
+       struct ci_ps *ps = ci_get_ps(rps);
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_clock_and_voltage_limits *max_limits;
+       bool disable_mclk_switching;
+       u32 sclk, mclk;
+       int i;
+
+       if (rdev->pm.dpm.new_active_crtc_count > 1)
+               disable_mclk_switching = true;
+       else
+               disable_mclk_switching = false;
+
+       if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
+               pi->battery_state = true;
+       else
+               pi->battery_state = false;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       if (rdev->pm.dpm.ac_power == false) {
+               for (i = 0; i < ps->performance_level_count; i++) {
+                       if (ps->performance_levels[i].mclk > max_limits->mclk)
+                               ps->performance_levels[i].mclk = max_limits->mclk;
+                       if (ps->performance_levels[i].sclk > max_limits->sclk)
+                               ps->performance_levels[i].sclk = max_limits->sclk;
+               }
+       }
+
+       /* XXX validate the min clocks required for display */
+
+       if (disable_mclk_switching) {
+               mclk  = ps->performance_levels[ps->performance_level_count - 1].mclk;
+               sclk = ps->performance_levels[0].sclk;
+       } else {
+               mclk = ps->performance_levels[0].mclk;
+               sclk = ps->performance_levels[0].sclk;
+       }
+
+       ps->performance_levels[0].sclk = sclk;
+       ps->performance_levels[0].mclk = mclk;
+
+       if (ps->performance_levels[1].sclk < ps->performance_levels[0].sclk)
+               ps->performance_levels[1].sclk = ps->performance_levels[0].sclk;
+
+       if (disable_mclk_switching) {
+               if (ps->performance_levels[0].mclk < ps->performance_levels[1].mclk)
+                       ps->performance_levels[0].mclk = ps->performance_levels[1].mclk;
+       } else {
+               if (ps->performance_levels[1].mclk < ps->performance_levels[0].mclk)
+                       ps->performance_levels[1].mclk = ps->performance_levels[0].mclk;
+       }
+}
+
+static int ci_set_thermal_temperature_range(struct radeon_device *rdev,
+                                           int min_temp, int max_temp)
+{
+       int low_temp = 0 * 1000;
+       int high_temp = 255 * 1000;
+       u32 tmp;
+
+       if (low_temp < min_temp)
+               low_temp = min_temp;
+       if (high_temp > max_temp)
+               high_temp = max_temp;
+       if (high_temp < low_temp) {
+               DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
+               return -EINVAL;
+       }
+
+       tmp = RREG32_SMC(CG_THERMAL_INT);
+       tmp &= ~(CI_DIG_THERM_INTH_MASK | CI_DIG_THERM_INTL_MASK);
+       tmp |= CI_DIG_THERM_INTH(high_temp / 1000) |
+               CI_DIG_THERM_INTL(low_temp / 1000);
+       WREG32_SMC(CG_THERMAL_INT, tmp);
+
+#if 0
+       /* XXX: need to figure out how to handle this properly */
+       tmp = RREG32_SMC(CG_THERMAL_CTRL);
+       tmp &= DIG_THERM_DPM_MASK;
+       tmp |= DIG_THERM_DPM(high_temp / 1000);
+       WREG32_SMC(CG_THERMAL_CTRL, tmp);
+#endif
+
+       return 0;
+}
+
+#if 0
+static int ci_read_smc_soft_register(struct radeon_device *rdev,
+                                    u16 reg_offset, u32 *value)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       return ci_read_smc_sram_dword(rdev,
+                                     pi->soft_regs_start + reg_offset,
+                                     value, pi->sram_end);
+}
+#endif
+
+static int ci_write_smc_soft_register(struct radeon_device *rdev,
+                                     u16 reg_offset, u32 value)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       return ci_write_smc_sram_dword(rdev,
+                                      pi->soft_regs_start + reg_offset,
+                                      value, pi->sram_end);
+}
+
+static void ci_init_fps_limits(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       SMU7_Discrete_DpmTable *table = &pi->smc_state_table;
+
+       if (pi->caps_fps) {
+               u16 tmp;
+
+               tmp = 45;
+               table->FpsHighT = cpu_to_be16(tmp);
+
+               tmp = 30;
+               table->FpsLowT = cpu_to_be16(tmp);
+       }
+}
+
+static int ci_update_sclk_t(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int ret = 0;
+       u32 low_sclk_interrupt_t = 0;
+
+       if (pi->caps_sclk_throttle_low_notification) {
+               low_sclk_interrupt_t = cpu_to_be32(pi->low_sclk_interrupt_t);
+
+               ret = ci_copy_bytes_to_smc(rdev,
+                                          pi->dpm_table_start +
+                                          offsetof(SMU7_Discrete_DpmTable, LowSclkInterruptT),
+                                          (u8 *)&low_sclk_interrupt_t,
+                                          sizeof(u32), pi->sram_end);
+
+       }
+
+       return ret;
+}
+
+static void ci_get_leakage_voltages(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u16 leakage_id, virtual_voltage_id;
+       u16 vddc, vddci;
+       int i;
+
+       pi->vddc_leakage.count = 0;
+       pi->vddci_leakage.count = 0;
+
+       if (radeon_atom_get_leakage_id_from_vbios(rdev, &leakage_id) == 0) {
+               for (i = 0; i < CISLANDS_MAX_LEAKAGE_COUNT; i++) {
+                       virtual_voltage_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
+                       if (radeon_atom_get_leakage_vddc_based_on_leakage_params(rdev, &vddc, &vddci,
+                                                                                virtual_voltage_id,
+                                                                                leakage_id) == 0) {
+                               if (vddc != 0 && vddc != virtual_voltage_id) {
+                                       pi->vddc_leakage.actual_voltage[pi->vddc_leakage.count] = vddc;
+                                       pi->vddc_leakage.leakage_id[pi->vddc_leakage.count] = virtual_voltage_id;
+                                       pi->vddc_leakage.count++;
+                               }
+                               if (vddci != 0 && vddci != virtual_voltage_id) {
+                                       pi->vddci_leakage.actual_voltage[pi->vddci_leakage.count] = vddci;
+                                       pi->vddci_leakage.leakage_id[pi->vddci_leakage.count] = virtual_voltage_id;
+                                       pi->vddci_leakage.count++;
+                               }
+                       }
+               }
+       }
+}
+
+static void ci_set_dpm_event_sources(struct radeon_device *rdev, u32 sources)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       bool want_thermal_protection;
+       enum radeon_dpm_event_src dpm_event_src;
+       u32 tmp;
+
+       switch (sources) {
+       case 0:
+       default:
+               want_thermal_protection = false;
+               break;
+       case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL;
+               break;
+       case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL;
+               break;
+       case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
+             (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)):
+               want_thermal_protection = true;
+               dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL;
+               break;
+       }
+
+       if (want_thermal_protection) {
+#if 0
+               /* XXX: need to figure out how to handle this properly */
+               tmp = RREG32_SMC(CG_THERMAL_CTRL);
+               tmp &= DPM_EVENT_SRC_MASK;
+               tmp |= DPM_EVENT_SRC(dpm_event_src);
+               WREG32_SMC(CG_THERMAL_CTRL, tmp);
+#endif
+
+               tmp = RREG32_SMC(GENERAL_PWRMGT);
+               if (pi->thermal_protection)
+                       tmp &= ~THERMAL_PROTECTION_DIS;
+               else
+                       tmp |= THERMAL_PROTECTION_DIS;
+               WREG32_SMC(GENERAL_PWRMGT, tmp);
+       } else {
+               tmp = RREG32_SMC(GENERAL_PWRMGT);
+               tmp |= THERMAL_PROTECTION_DIS;
+               WREG32_SMC(GENERAL_PWRMGT, tmp);
+       }
+}
+
+static void ci_enable_auto_throttle_source(struct radeon_device *rdev,
+                                          enum radeon_dpm_auto_throttle_src source,
+                                          bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (enable) {
+               if (!(pi->active_auto_throttle_sources & (1 << source))) {
+                       pi->active_auto_throttle_sources |= 1 << source;
+                       ci_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       } else {
+               if (pi->active_auto_throttle_sources & (1 << source)) {
+                       pi->active_auto_throttle_sources &= ~(1 << source);
+                       ci_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
+               }
+       }
+}
+
+static void ci_enable_vr_hot_gpio_interrupt(struct radeon_device *rdev)
+{
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT)
+               ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableVRHotGPIOInterrupt);
+}
+
+static int ci_unfreeze_sclk_mclk_dpm(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+
+       if (!pi->need_update_smu7_dpm_table)
+               return 0;
+
+       if ((!pi->sclk_dpm_key_disabled) &&
+           (pi->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_UPDATE_SCLK))) {
+               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_SCLKDPM_UnfreezeLevel);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       if ((!pi->mclk_dpm_key_disabled) &&
+           (pi->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
+               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_UnfreezeLevel);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       pi->need_update_smu7_dpm_table = 0;
+       return 0;
+}
+
+static int ci_enable_sclk_mclk_dpm(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+
+       if (enable) {
+               if (!pi->sclk_dpm_key_disabled) {
+                       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_DPM_Enable);
+                       if (smc_result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+
+               if (!pi->mclk_dpm_key_disabled) {
+                       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_Enable);
+                       if (smc_result != PPSMC_Result_OK)
+                               return -EINVAL;
+
+                       WREG32_P(MC_SEQ_CNTL_3, CAC_EN, ~CAC_EN);
+
+                       WREG32_SMC(LCAC_MC0_CNTL, 0x05);
+                       WREG32_SMC(LCAC_MC1_CNTL, 0x05);
+                       WREG32_SMC(LCAC_CPL_CNTL, 0x100005);
+
+                       udelay(10);
+
+                       WREG32_SMC(LCAC_MC0_CNTL, 0x400005);
+                       WREG32_SMC(LCAC_MC1_CNTL, 0x400005);
+                       WREG32_SMC(LCAC_CPL_CNTL, 0x500005);
+               }
+       } else {
+               if (!pi->sclk_dpm_key_disabled) {
+                       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_DPM_Disable);
+                       if (smc_result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+
+               if (!pi->mclk_dpm_key_disabled) {
+                       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_Disable);
+                       if (smc_result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int ci_start_dpm(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret;
+       u32 tmp;
+
+       tmp = RREG32_SMC(GENERAL_PWRMGT);
+       tmp |= GLOBAL_PWRMGT_EN;
+       WREG32_SMC(GENERAL_PWRMGT, tmp);
+
+       tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
+       tmp |= DYNAMIC_PM_EN;
+       WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);
+
+       ci_write_smc_soft_register(rdev, offsetof(SMU7_SoftRegisters, VoltageChangeTimeout), 0x1000);
+
+       WREG32_P(BIF_LNCNT_RESET, 0, ~RESET_LNCNT_EN);
+
+       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Voltage_Cntl_Enable);
+       if (smc_result != PPSMC_Result_OK)
+               return -EINVAL;
+
+       ret = ci_enable_sclk_mclk_dpm(rdev, true);
+       if (ret)
+               return ret;
+
+       if (!pi->pcie_dpm_key_disabled) {
+               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_PCIeDPM_Enable);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ci_freeze_sclk_mclk_dpm(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+
+       if (!pi->need_update_smu7_dpm_table)
+               return 0;
+
+       if ((!pi->sclk_dpm_key_disabled) &&
+           (pi->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_UPDATE_SCLK))) {
+               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_SCLKDPM_FreezeLevel);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       if ((!pi->mclk_dpm_key_disabled) &&
+           (pi->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
+               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_MCLKDPM_FreezeLevel);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ci_stop_dpm(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+       int ret;
+       u32 tmp;
+
+       tmp = RREG32_SMC(GENERAL_PWRMGT);
+       tmp &= ~GLOBAL_PWRMGT_EN;
+       WREG32_SMC(GENERAL_PWRMGT, tmp);
+
+       tmp = RREG32(SCLK_PWRMGT_CNTL);
+       tmp &= ~DYNAMIC_PM_EN;
+       WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);
+
+       if (!pi->pcie_dpm_key_disabled) {
+               smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_PCIeDPM_Disable);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       ret = ci_enable_sclk_mclk_dpm(rdev, false);
+       if (ret)
+               return ret;
+
+       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_Voltage_Cntl_Disable);
+       if (smc_result != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void ci_enable_sclk_control(struct radeon_device *rdev, bool enable)
+{
+       u32 tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
+
+       if (enable)
+               tmp &= ~SCLK_PWRMGT_OFF;
+       else
+               tmp |= SCLK_PWRMGT_OFF;
+       WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);
+}
+
+#if 0
+static int ci_notify_hw_of_power_source(struct radeon_device *rdev,
+                                       bool ac_power)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_cac_tdp_table *cac_tdp_table =
+               rdev->pm.dpm.dyn_state.cac_tdp_table;
+       u32 power_limit;
+
+       if (ac_power)
+               power_limit = (u32)(cac_tdp_table->maximum_power_delivery_limit * 256);
+       else
+               power_limit = (u32)(cac_tdp_table->battery_power_limit * 256);
+
+        ci_set_power_limit(rdev, power_limit);
+
+       if (pi->caps_automatic_dc_transition) {
+               if (ac_power)
+                       ci_send_msg_to_smc(rdev, PPSMC_MSG_RunningOnAC);
+               else
+                       ci_send_msg_to_smc(rdev, PPSMC_MSG_Remove_DC_Clamp);
+       }
+
+       return 0;
+}
+#endif
+
+static PPSMC_Result ci_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
+                                                     PPSMC_Msg msg, u32 parameter)
+{
+       WREG32(SMC_MSG_ARG_0, parameter);
+       return ci_send_msg_to_smc(rdev, msg);
+}
+
+static PPSMC_Result ci_send_msg_to_smc_return_parameter(struct radeon_device *rdev,
+                                                       PPSMC_Msg msg, u32 *parameter)
+{
+       PPSMC_Result smc_result;
+
+       smc_result = ci_send_msg_to_smc(rdev, msg);
+
+       if ((smc_result == PPSMC_Result_OK) && parameter)
+               *parameter = RREG32(SMC_MSG_ARG_0);
+
+       return smc_result;
+}
+
+static int ci_dpm_force_state_sclk(struct radeon_device *rdev, u32 n)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (!pi->sclk_dpm_key_disabled) {
+               PPSMC_Result smc_result =
+                       ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_DPM_ForceState, n);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ci_dpm_force_state_mclk(struct radeon_device *rdev, u32 n)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (!pi->mclk_dpm_key_disabled) {
+               PPSMC_Result smc_result =
+                       ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_MCLKDPM_ForceState, n);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ci_dpm_force_state_pcie(struct radeon_device *rdev, u32 n)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (!pi->pcie_dpm_key_disabled) {
+               PPSMC_Result smc_result =
+                       ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_PCIeDPM_ForceLevel, n);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ci_set_power_limit(struct radeon_device *rdev, u32 n)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (pi->power_containment_features & POWERCONTAINMENT_FEATURE_PkgPwrLimit) {
+               PPSMC_Result smc_result =
+                       ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_PkgPwrSetLimit, n);
+               if (smc_result != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ci_set_overdrive_target_tdp(struct radeon_device *rdev,
+                                      u32 target_tdp)
+{
+       PPSMC_Result smc_result =
+               ci_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_OverDriveSetTargetTdp, target_tdp);
+       if (smc_result != PPSMC_Result_OK)
+               return -EINVAL;
+       return 0;
+}
+
+static int ci_set_boot_state(struct radeon_device *rdev)
+{
+       return ci_enable_sclk_mclk_dpm(rdev, false);
+}
+
+static u32 ci_get_average_sclk_freq(struct radeon_device *rdev)
+{
+       u32 sclk_freq;
+       PPSMC_Result smc_result =
+               ci_send_msg_to_smc_return_parameter(rdev,
+                                                   PPSMC_MSG_API_GetSclkFrequency,
+                                                   &sclk_freq);
+       if (smc_result != PPSMC_Result_OK)
+               sclk_freq = 0;
+
+       return sclk_freq;
+}
+
+static u32 ci_get_average_mclk_freq(struct radeon_device *rdev)
+{
+       u32 mclk_freq;
+       PPSMC_Result smc_result =
+               ci_send_msg_to_smc_return_parameter(rdev,
+                                                   PPSMC_MSG_API_GetMclkFrequency,
+                                                   &mclk_freq);
+       if (smc_result != PPSMC_Result_OK)
+               mclk_freq = 0;
+
+       return mclk_freq;
+}
+
+static void ci_dpm_start_smc(struct radeon_device *rdev)
+{
+       int i;
+
+       ci_program_jump_on_start(rdev);
+       ci_start_smc_clock(rdev);
+       ci_start_smc(rdev);
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32_SMC(FIRMWARE_FLAGS) & INTERRUPTS_ENABLED)
+                       break;
+       }
+}
+
+static void ci_dpm_stop_smc(struct radeon_device *rdev)
+{
+       ci_reset_smc(rdev);
+       ci_stop_smc_clock(rdev);
+}
+
+static int ci_process_firmware_header(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = ci_read_smc_sram_dword(rdev,
+                                    SMU7_FIRMWARE_HEADER_LOCATION +
+                                    offsetof(SMU7_Firmware_Header, DpmTable),
+                                    &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       pi->dpm_table_start = tmp;
+
+       ret = ci_read_smc_sram_dword(rdev,
+                                    SMU7_FIRMWARE_HEADER_LOCATION +
+                                    offsetof(SMU7_Firmware_Header, SoftRegisters),
+                                    &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       pi->soft_regs_start = tmp;
+
+       ret = ci_read_smc_sram_dword(rdev,
+                                    SMU7_FIRMWARE_HEADER_LOCATION +
+                                    offsetof(SMU7_Firmware_Header, mcRegisterTable),
+                                    &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       pi->mc_reg_table_start = tmp;
+
+       ret = ci_read_smc_sram_dword(rdev,
+                                    SMU7_FIRMWARE_HEADER_LOCATION +
+                                    offsetof(SMU7_Firmware_Header, FanTable),
+                                    &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       pi->fan_table_start = tmp;
+
+       ret = ci_read_smc_sram_dword(rdev,
+                                    SMU7_FIRMWARE_HEADER_LOCATION +
+                                    offsetof(SMU7_Firmware_Header, mcArbDramTimingTable),
+                                    &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       pi->arb_table_start = tmp;
+
+       return 0;
+}
+
+static void ci_read_clock_registers(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       pi->clock_registers.cg_spll_func_cntl =
+               RREG32_SMC(CG_SPLL_FUNC_CNTL);
+       pi->clock_registers.cg_spll_func_cntl_2 =
+               RREG32_SMC(CG_SPLL_FUNC_CNTL_2);
+       pi->clock_registers.cg_spll_func_cntl_3 =
+               RREG32_SMC(CG_SPLL_FUNC_CNTL_3);
+       pi->clock_registers.cg_spll_func_cntl_4 =
+               RREG32_SMC(CG_SPLL_FUNC_CNTL_4);
+       pi->clock_registers.cg_spll_spread_spectrum =
+               RREG32_SMC(CG_SPLL_SPREAD_SPECTRUM);
+       pi->clock_registers.cg_spll_spread_spectrum_2 =
+               RREG32_SMC(CG_SPLL_SPREAD_SPECTRUM_2);
+       pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
+       pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
+       pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
+       pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
+       pi->clock_registers.mpll_func_cntl = RREG32(MPLL_FUNC_CNTL);
+       pi->clock_registers.mpll_func_cntl_1 = RREG32(MPLL_FUNC_CNTL_1);
+       pi->clock_registers.mpll_func_cntl_2 = RREG32(MPLL_FUNC_CNTL_2);
+       pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
+       pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
+}
+
+static void ci_init_sclk_t(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       pi->low_sclk_interrupt_t = 0;
+}
+
+static void ci_enable_thermal_protection(struct radeon_device *rdev,
+                                        bool enable)
+{
+       u32 tmp = RREG32_SMC(GENERAL_PWRMGT);
+
+       if (enable)
+               tmp &= ~THERMAL_PROTECTION_DIS;
+       else
+               tmp |= THERMAL_PROTECTION_DIS;
+       WREG32_SMC(GENERAL_PWRMGT, tmp);
+}
+
+static void ci_enable_acpi_power_management(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(GENERAL_PWRMGT);
+
+       tmp |= STATIC_PM_EN;
+
+       WREG32_SMC(GENERAL_PWRMGT, tmp);
+}
+
+#if 0
+static int ci_enter_ulp_state(struct radeon_device *rdev)
+{
+
+       WREG32(SMC_MESSAGE_0, PPSMC_MSG_SwitchToMinimumPower);
+
+       udelay(25000);
+
+       return 0;
+}
+
+static int ci_exit_ulp_state(struct radeon_device *rdev)
+{
+       int i;
+
+       WREG32(SMC_MESSAGE_0, PPSMC_MSG_ResumeFromMinimumPower);
+
+       udelay(7000);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32(SMC_RESP_0) == 1)
+                       break;
+               udelay(1000);
+       }
+
+       return 0;
+}
+#endif
+
+static int ci_notify_smc_display_change(struct radeon_device *rdev,
+                                       bool has_display)
+{
+       PPSMC_Msg msg = has_display ? PPSMC_MSG_HasDisplay : PPSMC_MSG_NoDisplay;
+
+       return (ci_send_msg_to_smc(rdev, msg) == PPSMC_Result_OK) ?  0 : -EINVAL;
+}
+
+static int ci_enable_ds_master_switch(struct radeon_device *rdev,
+                                     bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (enable) {
+               if (pi->caps_sclk_ds) {
+                       if (ci_send_msg_to_smc(rdev, PPSMC_MSG_MASTER_DeepSleep_ON) != PPSMC_Result_OK)
+                               return -EINVAL;
+               } else {
+                       if (ci_send_msg_to_smc(rdev, PPSMC_MSG_MASTER_DeepSleep_OFF) != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+       } else {
+               if (pi->caps_sclk_ds) {
+                       if (ci_send_msg_to_smc(rdev, PPSMC_MSG_MASTER_DeepSleep_OFF) != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static void ci_program_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(CG_DISPLAY_GAP_CNTL);
+       u32 pre_vbi_time_in_us;
+       u32 frame_time_in_us;
+       u32 ref_clock = rdev->clock.spll.reference_freq;
+       u32 refresh_rate = r600_dpm_get_vrefresh(rdev);
+       u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+
+       tmp &= ~DISP_GAP_MASK;
+       if (rdev->pm.dpm.new_active_crtc_count > 0)
+               tmp |= DISP_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM);
+       else
+               tmp |= DISP_GAP(R600_PM_DISPLAY_GAP_IGNORE);
+       WREG32_SMC(CG_DISPLAY_GAP_CNTL, tmp);
+
+       if (refresh_rate == 0)
+               refresh_rate = 60;
+       if (vblank_time == 0xffffffff)
+               vblank_time = 500;
+       frame_time_in_us = 1000000 / refresh_rate;
+       pre_vbi_time_in_us =
+               frame_time_in_us - 200 - vblank_time;
+       tmp = pre_vbi_time_in_us * (ref_clock / 100);
+
+       WREG32_SMC(CG_DISPLAY_GAP_CNTL2, tmp);
+       ci_write_smc_soft_register(rdev, offsetof(SMU7_SoftRegisters, PreVBlankGap), 0x64);
+       ci_write_smc_soft_register(rdev, offsetof(SMU7_SoftRegisters, VBlankTimeout), (frame_time_in_us - pre_vbi_time_in_us));
+
+
+       ci_notify_smc_display_change(rdev, (rdev->pm.dpm.new_active_crtc_count == 1));
+
+}
+
+static void ci_enable_spread_spectrum(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+
+       if (enable) {
+               if (pi->caps_sclk_ss_support) {
+                       tmp = RREG32_SMC(GENERAL_PWRMGT);
+                       tmp |= DYN_SPREAD_SPECTRUM_EN;
+                       WREG32_SMC(GENERAL_PWRMGT, tmp);
+               }
+       } else {
+               tmp = RREG32_SMC(CG_SPLL_SPREAD_SPECTRUM);
+               tmp &= ~SSEN;
+               WREG32_SMC(CG_SPLL_SPREAD_SPECTRUM, tmp);
+
+               tmp = RREG32_SMC(GENERAL_PWRMGT);
+               tmp &= ~DYN_SPREAD_SPECTRUM_EN;
+               WREG32_SMC(GENERAL_PWRMGT, tmp);
+       }
+}
+
+static void ci_program_sstp(struct radeon_device *rdev)
+{
+       WREG32_SMC(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT)));
+}
+
+static void ci_enable_display_gap(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(CG_DISPLAY_GAP_CNTL);
+
+        tmp &= ~(DISP_GAP_MASK | DISP_GAP_MCHG_MASK);
+        tmp |= (DISP_GAP(R600_PM_DISPLAY_GAP_IGNORE) |
+                DISP_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK));
+
+       WREG32_SMC(CG_DISPLAY_GAP_CNTL, tmp);
+}
+
+static void ci_program_vc(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
+       tmp &= ~(RESET_SCLK_CNT | RESET_BUSY_CNT);
+       WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);
+
+       WREG32_SMC(CG_FTV_0, CISLANDS_VRC_DFLT0);
+       WREG32_SMC(CG_FTV_1, CISLANDS_VRC_DFLT1);
+       WREG32_SMC(CG_FTV_2, CISLANDS_VRC_DFLT2);
+       WREG32_SMC(CG_FTV_3, CISLANDS_VRC_DFLT3);
+       WREG32_SMC(CG_FTV_4, CISLANDS_VRC_DFLT4);
+       WREG32_SMC(CG_FTV_5, CISLANDS_VRC_DFLT5);
+       WREG32_SMC(CG_FTV_6, CISLANDS_VRC_DFLT6);
+       WREG32_SMC(CG_FTV_7, CISLANDS_VRC_DFLT7);
+}
+
+static void ci_clear_vc(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = RREG32_SMC(SCLK_PWRMGT_CNTL);
+       tmp |= (RESET_SCLK_CNT | RESET_BUSY_CNT);
+       WREG32_SMC(SCLK_PWRMGT_CNTL, tmp);
+
+       WREG32_SMC(CG_FTV_0, 0);
+       WREG32_SMC(CG_FTV_1, 0);
+       WREG32_SMC(CG_FTV_2, 0);
+       WREG32_SMC(CG_FTV_3, 0);
+       WREG32_SMC(CG_FTV_4, 0);
+       WREG32_SMC(CG_FTV_5, 0);
+       WREG32_SMC(CG_FTV_6, 0);
+       WREG32_SMC(CG_FTV_7, 0);
+}
+
+static int ci_upload_firmware(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int i, ret;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               if (RREG32_SMC(RCU_UC_EVENTS) & BOOT_SEQ_DONE)
+                       break;
+       }
+       WREG32_SMC(SMC_SYSCON_MISC_CNTL, 1);
+
+       ci_stop_smc_clock(rdev);
+       ci_reset_smc(rdev);
+
+       ret = ci_load_smc_ucode(rdev, pi->sram_end);
+
+       return ret;
+
+}
+
+static int ci_get_svi2_voltage_table(struct radeon_device *rdev,
+                                    struct radeon_clock_voltage_dependency_table *voltage_dependency_table,
+                                    struct atom_voltage_table *voltage_table)
+{
+       u32 i;
+
+       if (voltage_dependency_table == NULL)
+               return -EINVAL;
+
+       voltage_table->mask_low = 0;
+       voltage_table->phase_delay = 0;
+
+       voltage_table->count = voltage_dependency_table->count;
+       for (i = 0; i < voltage_table->count; i++) {
+               voltage_table->entries[i].value = voltage_dependency_table->entries[i].v;
+               voltage_table->entries[i].smio_low = 0;
+       }
+
+       return 0;
+}
+
+static int ci_construct_voltage_tables(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int ret;
+
+       if (pi->voltage_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
+                                                   VOLTAGE_OBJ_GPIO_LUT,
+                                                   &pi->vddc_voltage_table);
+               if (ret)
+                       return ret;
+       } else if (pi->voltage_control == CISLANDS_VOLTAGE_CONTROL_BY_SVID2) {
+               ret = ci_get_svi2_voltage_table(rdev,
+                                               &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                               &pi->vddc_voltage_table);
+               if (ret)
+                       return ret;
+       }
+
+       if (pi->vddc_voltage_table.count > SMU7_MAX_LEVELS_VDDC)
+               si_trim_voltage_table_to_fit_state_table(rdev, SMU7_MAX_LEVELS_VDDC,
+                                                        &pi->vddc_voltage_table);
+
+       if (pi->vddci_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDCI,
+                                                   VOLTAGE_OBJ_GPIO_LUT,
+                                                   &pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+       } else if (pi->vddci_control == CISLANDS_VOLTAGE_CONTROL_BY_SVID2) {
+               ret = ci_get_svi2_voltage_table(rdev,
+                                               &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                               &pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+       }
+
+       if (pi->vddci_voltage_table.count > SMU7_MAX_LEVELS_VDDCI)
+               si_trim_voltage_table_to_fit_state_table(rdev, SMU7_MAX_LEVELS_VDDCI,
+                                                        &pi->vddci_voltage_table);
+
+       if (pi->mvdd_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO) {
+               ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_MVDDC,
+                                                   VOLTAGE_OBJ_GPIO_LUT,
+                                                   &pi->mvdd_voltage_table);
+               if (ret)
+                       return ret;
+       } else if (pi->mvdd_control == CISLANDS_VOLTAGE_CONTROL_BY_SVID2) {
+               ret = ci_get_svi2_voltage_table(rdev,
+                                               &rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk,
+                                               &pi->mvdd_voltage_table);
+               if (ret)
+                       return ret;
+       }
+
+       if (pi->mvdd_voltage_table.count > SMU7_MAX_LEVELS_MVDD)
+               si_trim_voltage_table_to_fit_state_table(rdev, SMU7_MAX_LEVELS_MVDD,
+                                                        &pi->mvdd_voltage_table);
+
+       return 0;
+}
+
+static void ci_populate_smc_voltage_table(struct radeon_device *rdev,
+                                         struct atom_voltage_table_entry *voltage_table,
+                                         SMU7_Discrete_VoltageLevel *smc_voltage_table)
+{
+       int ret;
+
+       ret = ci_get_std_voltage_value_sidd(rdev, voltage_table,
+                                           &smc_voltage_table->StdVoltageHiSidd,
+                                           &smc_voltage_table->StdVoltageLoSidd);
+
+       if (ret) {
+               smc_voltage_table->StdVoltageHiSidd = voltage_table->value * VOLTAGE_SCALE;
+               smc_voltage_table->StdVoltageLoSidd = voltage_table->value * VOLTAGE_SCALE;
+       }
+
+       smc_voltage_table->Voltage = cpu_to_be16(voltage_table->value * VOLTAGE_SCALE);
+       smc_voltage_table->StdVoltageHiSidd =
+               cpu_to_be16(smc_voltage_table->StdVoltageHiSidd);
+       smc_voltage_table->StdVoltageLoSidd =
+               cpu_to_be16(smc_voltage_table->StdVoltageLoSidd);
+}
+
+static int ci_populate_smc_vddc_table(struct radeon_device *rdev,
+                                     SMU7_Discrete_DpmTable *table)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       unsigned int count;
+
+       table->VddcLevelCount = pi->vddc_voltage_table.count;
+       for (count = 0; count < table->VddcLevelCount; count++) {
+               ci_populate_smc_voltage_table(rdev,
+                                             &pi->vddc_voltage_table.entries[count],
+                                             &table->VddcLevel[count]);
+
+               if (pi->voltage_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO)
+                       table->VddcLevel[count].Smio |=
+                               pi->vddc_voltage_table.entries[count].smio_low;
+               else
+                       table->VddcLevel[count].Smio = 0;
+       }
+       table->VddcLevelCount = cpu_to_be32(table->VddcLevelCount);
+
+       return 0;
+}
+
+static int ci_populate_smc_vddci_table(struct radeon_device *rdev,
+                                      SMU7_Discrete_DpmTable *table)
+{
+       unsigned int count;
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       table->VddciLevelCount = pi->vddci_voltage_table.count;
+       for (count = 0; count < table->VddciLevelCount; count++) {
+               ci_populate_smc_voltage_table(rdev,
+                                             &pi->vddci_voltage_table.entries[count],
+                                             &table->VddciLevel[count]);
+
+               if (pi->vddci_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO)
+                       table->VddciLevel[count].Smio |=
+                               pi->vddci_voltage_table.entries[count].smio_low;
+               else
+                       table->VddciLevel[count].Smio = 0;
+       }
+       table->VddciLevelCount = cpu_to_be32(table->VddciLevelCount);
+
+       return 0;
+}
+
+static int ci_populate_smc_mvdd_table(struct radeon_device *rdev,
+                                     SMU7_Discrete_DpmTable *table)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       unsigned int count;
+
+       table->MvddLevelCount = pi->mvdd_voltage_table.count;
+       for (count = 0; count < table->MvddLevelCount; count++) {
+               ci_populate_smc_voltage_table(rdev,
+                                             &pi->mvdd_voltage_table.entries[count],
+                                             &table->MvddLevel[count]);
+
+               if (pi->mvdd_control == CISLANDS_VOLTAGE_CONTROL_BY_GPIO)
+                       table->MvddLevel[count].Smio |=
+                               pi->mvdd_voltage_table.entries[count].smio_low;
+               else
+                       table->MvddLevel[count].Smio = 0;
+       }
+       table->MvddLevelCount = cpu_to_be32(table->MvddLevelCount);
+
+       return 0;
+}
+
+static int ci_populate_smc_voltage_tables(struct radeon_device *rdev,
+                                         SMU7_Discrete_DpmTable *table)
+{
+       int ret;
+
+       ret = ci_populate_smc_vddc_table(rdev, table);
+       if (ret)
+               return ret;
+
+       ret = ci_populate_smc_vddci_table(rdev, table);
+       if (ret)
+               return ret;
+
+       ret = ci_populate_smc_mvdd_table(rdev, table);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ci_populate_mvdd_value(struct radeon_device *rdev, u32 mclk,
+                                 SMU7_Discrete_VoltageLevel *voltage)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 i = 0;
+
+       if (pi->mvdd_control != CISLANDS_VOLTAGE_CONTROL_NONE) {
+               for (i = 0; i < rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk.count; i++) {
+                       if (mclk <= rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk.entries[i].clk) {
+                               voltage->Voltage = pi->mvdd_voltage_table.entries[i].value;
+                               break;
+                       }
+               }
+
+               if (i >= rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk.count)
+                       return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int ci_get_std_voltage_value_sidd(struct radeon_device *rdev,
+                                        struct atom_voltage_table_entry *voltage_table,
+                                        u16 *std_voltage_hi_sidd, u16 *std_voltage_lo_sidd)
+{
+       u16 v_index, idx;
+       bool voltage_found = false;
+       *std_voltage_hi_sidd = voltage_table->value * VOLTAGE_SCALE;
+       *std_voltage_lo_sidd = voltage_table->value * VOLTAGE_SCALE;
+
+       if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries == NULL)
+               return -EINVAL;
+
+       if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries) {
+               for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                       if (voltage_table->value ==
+                           rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+                               voltage_found = true;
+                               if ((u32)v_index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
+                                       idx = v_index;
+                               else
+                                       idx = rdev->pm.dpm.dyn_state.cac_leakage_table.count - 1;
+                               *std_voltage_lo_sidd =
+                                       rdev->pm.dpm.dyn_state.cac_leakage_table.entries[idx].vddc * VOLTAGE_SCALE;
+                               *std_voltage_hi_sidd =
+                                       rdev->pm.dpm.dyn_state.cac_leakage_table.entries[idx].leakage * VOLTAGE_SCALE;
+                               break;
+                       }
+               }
+
+               if (!voltage_found) {
+                       for (v_index = 0; (u32)v_index < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                               if (voltage_table->value <=
+                                   rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+                                       voltage_found = true;
+                                       if ((u32)v_index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)
+                                               idx = v_index;
+                                       else
+                                               idx = rdev->pm.dpm.dyn_state.cac_leakage_table.count - 1;
+                                       *std_voltage_lo_sidd =
+                                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[idx].vddc * VOLTAGE_SCALE;
+                                       *std_voltage_hi_sidd =
+                                               rdev->pm.dpm.dyn_state.cac_leakage_table.entries[idx].leakage * VOLTAGE_SCALE;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void ci_populate_phase_value_based_on_sclk(struct radeon_device *rdev,
+                                                 const struct radeon_phase_shedding_limits_table *limits,
+                                                 u32 sclk,
+                                                 u32 *phase_shedding)
+{
+       unsigned int i;
+
+       *phase_shedding = 1;
+
+       for (i = 0; i < limits->count; i++) {
+               if (sclk < limits->entries[i].sclk) {
+                       *phase_shedding = i;
+                       break;
+               }
+       }
+}
+
+static void ci_populate_phase_value_based_on_mclk(struct radeon_device *rdev,
+                                                 const struct radeon_phase_shedding_limits_table *limits,
+                                                 u32 mclk,
+                                                 u32 *phase_shedding)
+{
+       unsigned int i;
+
+       *phase_shedding = 1;
+
+       for (i = 0; i < limits->count; i++) {
+               if (mclk < limits->entries[i].mclk) {
+                       *phase_shedding = i;
+                       break;
+               }
+       }
+}
+
+static int ci_init_arb_table_index(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+       int ret;
+
+       ret = ci_read_smc_sram_dword(rdev, pi->arb_table_start,
+                                    &tmp, pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= MC_CG_ARB_FREQ_F1 << 24;
+
+       return ci_write_smc_sram_dword(rdev, pi->arb_table_start,
+                                      tmp, pi->sram_end);
+}
+
+static int ci_get_dependency_volt_by_clk(struct radeon_device *rdev,
+                                        struct radeon_clock_voltage_dependency_table *allowed_clock_voltage_table,
+                                        u32 clock, u32 *voltage)
+{
+       u32 i = 0;
+
+       if (allowed_clock_voltage_table->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < allowed_clock_voltage_table->count; i++) {
+               if (allowed_clock_voltage_table->entries[i].clk >= clock) {
+                       *voltage = allowed_clock_voltage_table->entries[i].v;
+                       return 0;
+               }
+       }
+
+       *voltage = allowed_clock_voltage_table->entries[i-1].v;
+
+       return 0;
+}
+
+static u8 ci_get_sleep_divider_id_from_clock(struct radeon_device *rdev,
+                                            u32 sclk, u32 min_sclk_in_sr)
+{
+       u32 i;
+       u32 tmp;
+       u32 min = (min_sclk_in_sr > CISLAND_MINIMUM_ENGINE_CLOCK) ?
+               min_sclk_in_sr : CISLAND_MINIMUM_ENGINE_CLOCK;
+
+       if (sclk < min)
+               return 0;
+
+       for (i = CISLAND_MAX_DEEPSLEEP_DIVIDER_ID;  ; i--) {
+               tmp = sclk / (1 << i);
+               if (tmp >= min || i == 0)
+                       break;
+       }
+
+       return (u8)i;
+}
+
+static int ci_initial_switch_from_arb_f0_to_f1(struct radeon_device *rdev)
+{
+       return ni_copy_and_switch_arb_sets(rdev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int ci_reset_to_default(struct radeon_device *rdev)
+{
+       return (ci_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int ci_force_switch_to_arb_f0(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       tmp = (RREG32_SMC(SMC_SCRATCH9) & 0x0000ff00) >> 8;
+
+       if (tmp == MC_CG_ARB_FREQ_F0)
+               return 0;
+
+       return ni_copy_and_switch_arb_sets(rdev, tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static int ci_populate_memory_timing_parameters(struct radeon_device *rdev,
+                                               u32 sclk,
+                                               u32 mclk,
+                                               SMU7_Discrete_MCArbDramTimingTableEntry *arb_regs)
+{
+       u32 dram_timing;
+       u32 dram_timing2;
+       u32 burst_time;
+
+       radeon_atom_set_engine_dram_timings(rdev, sclk, mclk);
+
+       dram_timing  = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+       burst_time = RREG32(MC_ARB_BURST_TIME) & STATE0_MASK;
+
+       arb_regs->McArbDramTiming  = cpu_to_be32(dram_timing);
+       arb_regs->McArbDramTiming2 = cpu_to_be32(dram_timing2);
+       arb_regs->McArbBurstTime = (u8)burst_time;
+
+       return 0;
+}
+
+static int ci_do_program_memory_timing_parameters(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       SMU7_Discrete_MCArbDramTimingTable arb_regs;
+       u32 i, j;
+       int ret =  0;
+
+       memset(&arb_regs, 0, sizeof(SMU7_Discrete_MCArbDramTimingTable));
+
+       for (i = 0; i < pi->dpm_table.sclk_table.count; i++) {
+               for (j = 0; j < pi->dpm_table.mclk_table.count; j++) {
+                       ret = ci_populate_memory_timing_parameters(rdev,
+                                                                  pi->dpm_table.sclk_table.dpm_levels[i].value,
+                                                                  pi->dpm_table.mclk_table.dpm_levels[j].value,
+                                                                  &arb_regs.entries[i][j]);
+                       if (ret)
+                               break;
+               }
+       }
+
+       if (ret == 0)
+               ret = ci_copy_bytes_to_smc(rdev,
+                                          pi->arb_table_start,
+                                          (u8 *)&arb_regs,
+                                          sizeof(SMU7_Discrete_MCArbDramTimingTable),
+                                          pi->sram_end);
+
+       return ret;
+}
+
+static int ci_program_memory_timing_parameters(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (pi->need_update_smu7_dpm_table == 0)
+               return 0;
+
+       return ci_do_program_memory_timing_parameters(rdev);
+}
+
+static void ci_populate_smc_initial_state(struct radeon_device *rdev,
+                                         struct radeon_ps *radeon_boot_state)
+{
+       struct ci_ps *boot_state = ci_get_ps(radeon_boot_state);
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 level = 0;
+
+       for (level = 0; level < rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; level++) {
+               if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[level].clk >=
+                   boot_state->performance_levels[0].sclk) {
+                       pi->smc_state_table.GraphicsBootLevel = level;
+                       break;
+               }
+       }
+
+       for (level = 0; level < rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.count; level++) {
+               if (rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries[level].clk >=
+                   boot_state->performance_levels[0].mclk) {
+                       pi->smc_state_table.MemoryBootLevel = level;
+                       break;
+               }
+       }
+}
+
+static u32 ci_get_dpm_level_enable_mask_value(struct ci_single_dpm_table *dpm_table)
+{
+       u32 i;
+       u32 mask_value = 0;
+
+       for (i = dpm_table->count; i > 0; i--) {
+               mask_value = mask_value << 1;
+               if (dpm_table->dpm_levels[i-1].enabled)
+                       mask_value |= 0x1;
+               else
+                       mask_value &= 0xFFFFFFFE;
+       }
+
+       return mask_value;
+}
+
+static void ci_populate_smc_link_level(struct radeon_device *rdev,
+                                      SMU7_Discrete_DpmTable *table)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_dpm_table *dpm_table = &pi->dpm_table;
+       u32 i;
+
+       for (i = 0; i < dpm_table->pcie_speed_table.count; i++) {
+               table->LinkLevel[i].PcieGenSpeed =
+                       (u8)dpm_table->pcie_speed_table.dpm_levels[i].value;
+               table->LinkLevel[i].PcieLaneCount =
+                       r600_encode_pci_lane_width(dpm_table->pcie_speed_table.dpm_levels[i].param1);
+               table->LinkLevel[i].EnabledForActivity = 1;
+               table->LinkLevel[i].DownT = cpu_to_be32(5);
+               table->LinkLevel[i].UpT = cpu_to_be32(30);
+       }
+
+       pi->smc_state_table.LinkLevelCount = (u8)dpm_table->pcie_speed_table.count;
+       pi->dpm_level_enable_mask.pcie_dpm_enable_mask =
+               ci_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+}
+
+static int ci_populate_smc_uvd_level(struct radeon_device *rdev,
+                                    SMU7_Discrete_DpmTable *table)
+{
+       u32 count;
+       struct atom_clock_dividers dividers;
+       int ret = -EINVAL;
+
+       table->UvdLevelCount =
+               rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count;
+
+       for (count = 0; count < table->UvdLevelCount; count++) {
+               table->UvdLevel[count].VclkFrequency =
+                       rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[count].vclk;
+               table->UvdLevel[count].DclkFrequency =
+                       rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[count].dclk;
+               table->UvdLevel[count].MinVddc =
+                       rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[count].v * VOLTAGE_SCALE;
+               table->UvdLevel[count].MinVddcPhases = 1;
+
+               ret = radeon_atom_get_clock_dividers(rdev,
+                                                    COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+                                                    table->UvdLevel[count].VclkFrequency, false, &dividers);
+               if (ret)
+                       return ret;
+
+               table->UvdLevel[count].VclkDivider = (u8)dividers.post_divider;
+
+               ret = radeon_atom_get_clock_dividers(rdev,
+                                                    COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+                                                    table->UvdLevel[count].DclkFrequency, false, &dividers);
+               if (ret)
+                       return ret;
+
+               table->UvdLevel[count].DclkDivider = (u8)dividers.post_divider;
+
+               table->UvdLevel[count].VclkFrequency = cpu_to_be32(table->UvdLevel[count].VclkFrequency);
+               table->UvdLevel[count].DclkFrequency = cpu_to_be32(table->UvdLevel[count].DclkFrequency);
+               table->UvdLevel[count].MinVddc = cpu_to_be16(table->UvdLevel[count].MinVddc);
+       }
+
+       return ret;
+}
+
+static int ci_populate_smc_vce_level(struct radeon_device *rdev,
+                                    SMU7_Discrete_DpmTable *table)
+{
+       u32 count;
+       struct atom_clock_dividers dividers;
+       int ret = -EINVAL;
+
+       table->VceLevelCount =
+               rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.count;
+
+       for (count = 0; count < table->VceLevelCount; count++) {
+               table->VceLevel[count].Frequency =
+                       rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[count].evclk;
+               table->VceLevel[count].MinVoltage =
+                       (u16)rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[count].v * VOLTAGE_SCALE;
+               table->VceLevel[count].MinPhases = 1;
+
+               ret = radeon_atom_get_clock_dividers(rdev,
+                                                    COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+                                                    table->VceLevel[count].Frequency, false, &dividers);
+               if (ret)
+                       return ret;
+
+               table->VceLevel[count].Divider = (u8)dividers.post_divider;
+
+               table->VceLevel[count].Frequency = cpu_to_be32(table->VceLevel[count].Frequency);
+               table->VceLevel[count].MinVoltage = cpu_to_be16(table->VceLevel[count].MinVoltage);
+       }
+
+       return ret;
+
+}
+
+static int ci_populate_smc_acp_level(struct radeon_device *rdev,
+                                    SMU7_Discrete_DpmTable *table)
+{
+       u32 count;
+       struct atom_clock_dividers dividers;
+       int ret = -EINVAL;
+
+       table->AcpLevelCount = (u8)
+               (rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.count);
+
+       for (count = 0; count < table->AcpLevelCount; count++) {
+               table->AcpLevel[count].Frequency =
+                       rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[count].clk;
+               table->AcpLevel[count].MinVoltage =
+                       rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[count].v;
+               table->AcpLevel[count].MinPhases = 1;
+
+               ret = radeon_atom_get_clock_dividers(rdev,
+                                                    COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+                                                    table->AcpLevel[count].Frequency, false, &dividers);
+               if (ret)
+                       return ret;
+
+               table->AcpLevel[count].Divider = (u8)dividers.post_divider;
+
+               table->AcpLevel[count].Frequency = cpu_to_be32(table->AcpLevel[count].Frequency);
+               table->AcpLevel[count].MinVoltage = cpu_to_be16(table->AcpLevel[count].MinVoltage);
+       }
+
+       return ret;
+}
+
+static int ci_populate_smc_samu_level(struct radeon_device *rdev,
+                                     SMU7_Discrete_DpmTable *table)
+{
+       u32 count;
+       struct atom_clock_dividers dividers;
+       int ret = -EINVAL;
+
+       table->SamuLevelCount =
+               rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.count;
+
+       for (count = 0; count < table->SamuLevelCount; count++) {
+               table->SamuLevel[count].Frequency =
+                       rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[count].clk;
+               table->SamuLevel[count].MinVoltage =
+                       rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[count].v * VOLTAGE_SCALE;
+               table->SamuLevel[count].MinPhases = 1;
+
+               ret = radeon_atom_get_clock_dividers(rdev,
+                                                    COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
+                                                    table->SamuLevel[count].Frequency, false, &dividers);
+               if (ret)
+                       return ret;
+
+               table->SamuLevel[count].Divider = (u8)dividers.post_divider;
+
+               table->SamuLevel[count].Frequency = cpu_to_be32(table->SamuLevel[count].Frequency);
+               table->SamuLevel[count].MinVoltage = cpu_to_be16(table->SamuLevel[count].MinVoltage);
+       }
+
+       return ret;
+}
+
+static int ci_calculate_mclk_params(struct radeon_device *rdev,
+                                   u32 memory_clock,
+                                   SMU7_Discrete_MemoryLevel *mclk,
+                                   bool strobe_mode,
+                                   bool dll_state_on)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32  dll_cntl = pi->clock_registers.dll_cntl;
+       u32  mclk_pwrmgt_cntl = pi->clock_registers.mclk_pwrmgt_cntl;
+       u32  mpll_ad_func_cntl = pi->clock_registers.mpll_ad_func_cntl;
+       u32  mpll_dq_func_cntl = pi->clock_registers.mpll_dq_func_cntl;
+       u32  mpll_func_cntl = pi->clock_registers.mpll_func_cntl;
+       u32  mpll_func_cntl_1 = pi->clock_registers.mpll_func_cntl_1;
+       u32  mpll_func_cntl_2 = pi->clock_registers.mpll_func_cntl_2;
+       u32  mpll_ss1 = pi->clock_registers.mpll_ss1;
+       u32  mpll_ss2 = pi->clock_registers.mpll_ss2;
+       struct atom_mpll_param mpll_param;
+       int ret;
+
+       ret = radeon_atom_get_memory_pll_dividers(rdev, memory_clock, strobe_mode, &mpll_param);
+       if (ret)
+               return ret;
+
+       mpll_func_cntl &= ~BWCTRL_MASK;
+       mpll_func_cntl |= BWCTRL(mpll_param.bwcntl);
+
+       mpll_func_cntl_1 &= ~(CLKF_MASK | CLKFRAC_MASK | VCO_MODE_MASK);
+       mpll_func_cntl_1 |= CLKF(mpll_param.clkf) |
+               CLKFRAC(mpll_param.clkfrac) | VCO_MODE(mpll_param.vco_mode);
+
+       mpll_ad_func_cntl &= ~YCLK_POST_DIV_MASK;
+       mpll_ad_func_cntl |= YCLK_POST_DIV(mpll_param.post_div);
+
+       if (pi->mem_gddr5) {
+               mpll_dq_func_cntl &= ~(YCLK_SEL_MASK | YCLK_POST_DIV_MASK);
+               mpll_dq_func_cntl |= YCLK_SEL(mpll_param.yclk_sel) |
+                       YCLK_POST_DIV(mpll_param.post_div);
+       }
+
+       if (pi->caps_mclk_ss_support) {
+               struct radeon_atom_ss ss;
+               u32 freq_nom;
+               u32 tmp;
+               u32 reference_clock = rdev->clock.mpll.reference_freq;
+
+               if (pi->mem_gddr5)
+                       freq_nom = memory_clock * 4;
+               else
+                       freq_nom = memory_clock * 2;
+
+               tmp = (freq_nom / reference_clock);
+               tmp = tmp * tmp;
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_MEMORY_SS, freq_nom)) {
+                       u32 clks = reference_clock * 5 / ss.rate;
+                       u32 clkv = (u32)((((131 * ss.percentage * ss.rate) / 100) * tmp) / freq_nom);
+
+                       mpll_ss1 &= ~CLKV_MASK;
+                       mpll_ss1 |= CLKV(clkv);
+
+                       mpll_ss2 &= ~CLKS_MASK;
+                       mpll_ss2 |= CLKS(clks);
+               }
+       }
+
+       mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+       mclk_pwrmgt_cntl |= DLL_SPEED(mpll_param.dll_speed);
+
+       if (dll_state_on)
+               mclk_pwrmgt_cntl |= MRDCK0_PDNB | MRDCK1_PDNB;
+       else
+               mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       mclk->MclkFrequency = memory_clock;
+       mclk->MpllFuncCntl = mpll_func_cntl;
+       mclk->MpllFuncCntl_1 = mpll_func_cntl_1;
+       mclk->MpllFuncCntl_2 = mpll_func_cntl_2;
+       mclk->MpllAdFuncCntl = mpll_ad_func_cntl;
+       mclk->MpllDqFuncCntl = mpll_dq_func_cntl;
+       mclk->MclkPwrmgtCntl = mclk_pwrmgt_cntl;
+       mclk->DllCntl = dll_cntl;
+       mclk->MpllSs1 = mpll_ss1;
+       mclk->MpllSs2 = mpll_ss2;
+
+       return 0;
+}
+
+static int ci_populate_single_memory_level(struct radeon_device *rdev,
+                                          u32 memory_clock,
+                                          SMU7_Discrete_MemoryLevel *memory_level)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int ret;
+       bool dll_state_on;
+
+       if (rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries) {
+               ret = ci_get_dependency_volt_by_clk(rdev,
+                                                   &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                   memory_clock, &memory_level->MinVddc);
+               if (ret)
+                       return ret;
+       }
+
+       if (rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries) {
+               ret = ci_get_dependency_volt_by_clk(rdev,
+                                                   &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                   memory_clock, &memory_level->MinVddci);
+               if (ret)
+                       return ret;
+       }
+
+       if (rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk.entries) {
+               ret = ci_get_dependency_volt_by_clk(rdev,
+                                                   &rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk,
+                                                   memory_clock, &memory_level->MinMvdd);
+               if (ret)
+                       return ret;
+       }
+
+       memory_level->MinVddcPhases = 1;
+
+       if (pi->vddc_phase_shed_control)
+               ci_populate_phase_value_based_on_mclk(rdev,
+                                                     &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                     memory_clock,
+                                                     &memory_level->MinVddcPhases);
+
+       memory_level->EnabledForThrottle = 1;
+       memory_level->EnabledForActivity = 1;
+       memory_level->UpH = 0;
+       memory_level->DownH = 100;
+       memory_level->VoltageDownH = 0;
+       memory_level->ActivityLevel = (u16)pi->mclk_activity_target;
+
+       memory_level->StutterEnable = false;
+       memory_level->StrobeEnable = false;
+       memory_level->EdcReadEnable = false;
+       memory_level->EdcWriteEnable = false;
+       memory_level->RttEnable = false;
+
+       memory_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       if (pi->mclk_stutter_mode_threshold &&
+           (memory_clock <= pi->mclk_stutter_mode_threshold) &&
+           (pi->uvd_enabled == false) &&
+           (RREG32(DPG_PIPE_STUTTER_CONTROL) & STUTTER_ENABLE) &&
+           (rdev->pm.dpm.new_active_crtc_count <= 2))
+               memory_level->StutterEnable = true;
+
+       if (pi->mclk_strobe_mode_threshold &&
+           (memory_clock <= pi->mclk_strobe_mode_threshold))
+               memory_level->StrobeEnable = 1;
+
+       if (pi->mem_gddr5) {
+               memory_level->StrobeRatio =
+                       si_get_mclk_frequency_ratio(memory_clock, memory_level->StrobeEnable);
+               if (pi->mclk_edc_enable_threshold &&
+                   (memory_clock > pi->mclk_edc_enable_threshold))
+                       memory_level->EdcReadEnable = true;
+
+               if (pi->mclk_edc_wr_enable_threshold &&
+                   (memory_clock > pi->mclk_edc_wr_enable_threshold))
+                       memory_level->EdcWriteEnable = true;
+
+               if (memory_level->StrobeEnable) {
+                       if (si_get_mclk_frequency_ratio(memory_clock, true) >=
+                           ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf))
+                               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+                       else
+                               dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false;
+               } else {
+                       dll_state_on = pi->dll_default_on;
+               }
+       } else {
+               memory_level->StrobeRatio = si_get_ddr3_mclk_frequency_ratio(memory_clock);
+               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+       }
+
+       ret = ci_calculate_mclk_params(rdev, memory_clock, memory_level, memory_level->StrobeEnable, dll_state_on);
+       if (ret)
+               return ret;
+
+       memory_level->MinVddc = cpu_to_be32(memory_level->MinVddc * VOLTAGE_SCALE);
+       memory_level->MinVddcPhases = cpu_to_be32(memory_level->MinVddcPhases);
+        memory_level->MinVddci = cpu_to_be32(memory_level->MinVddci * VOLTAGE_SCALE);
+        memory_level->MinMvdd = cpu_to_be32(memory_level->MinMvdd * VOLTAGE_SCALE);
+
+       memory_level->MclkFrequency = cpu_to_be32(memory_level->MclkFrequency);
+       memory_level->ActivityLevel = cpu_to_be16(memory_level->ActivityLevel);
+       memory_level->MpllFuncCntl = cpu_to_be32(memory_level->MpllFuncCntl);
+       memory_level->MpllFuncCntl_1 = cpu_to_be32(memory_level->MpllFuncCntl_1);
+       memory_level->MpllFuncCntl_2 = cpu_to_be32(memory_level->MpllFuncCntl_2);
+       memory_level->MpllAdFuncCntl = cpu_to_be32(memory_level->MpllAdFuncCntl);
+       memory_level->MpllDqFuncCntl = cpu_to_be32(memory_level->MpllDqFuncCntl);
+       memory_level->MclkPwrmgtCntl = cpu_to_be32(memory_level->MclkPwrmgtCntl);
+       memory_level->DllCntl = cpu_to_be32(memory_level->DllCntl);
+       memory_level->MpllSs1 = cpu_to_be32(memory_level->MpllSs1);
+       memory_level->MpllSs2 = cpu_to_be32(memory_level->MpllSs2);
+
+       return 0;
+}
+
+static int ci_populate_smc_acpi_level(struct radeon_device *rdev,
+                                     SMU7_Discrete_DpmTable *table)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       SMU7_Discrete_VoltageLevel voltage_level;
+       u32 spll_func_cntl = pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = pi->clock_registers.cg_spll_func_cntl_2;
+       u32 dll_cntl = pi->clock_registers.dll_cntl;
+       u32 mclk_pwrmgt_cntl = pi->clock_registers.mclk_pwrmgt_cntl;
+       int ret;
+
+       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc)
+               table->ACPILevel.MinVddc = cpu_to_be32(pi->acpi_vddc * VOLTAGE_SCALE);
+       else
+               table->ACPILevel.MinVddc = cpu_to_be32(pi->min_vddc_in_pp_table * VOLTAGE_SCALE);
+
+       table->ACPILevel.MinVddcPhases = pi->vddc_phase_shed_control ? 0 : 1;
+
+       table->ACPILevel.SclkFrequency = rdev->clock.spll.reference_freq;
+
+       ret = radeon_atom_get_clock_dividers(rdev,
+                                            COMPUTE_GPUCLK_INPUT_FLAG_SCLK,
+                                            table->ACPILevel.SclkFrequency, false, &dividers);
+       if (ret)
+               return ret;
+
+       table->ACPILevel.SclkDid = (u8)dividers.post_divider;
+       table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+       table->ACPILevel.DeepSleepDivId = 0;
+
+       spll_func_cntl &= ~SPLL_PWRON;
+       spll_func_cntl |= SPLL_RESET;
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+       table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+       table->ACPILevel.CgSpllFuncCntl3 = pi->clock_registers.cg_spll_func_cntl_3;
+       table->ACPILevel.CgSpllFuncCntl4 = pi->clock_registers.cg_spll_func_cntl_4;
+       table->ACPILevel.SpllSpreadSpectrum = pi->clock_registers.cg_spll_spread_spectrum;
+       table->ACPILevel.SpllSpreadSpectrum2 = pi->clock_registers.cg_spll_spread_spectrum_2;
+       table->ACPILevel.CcPwrDynRm = 0;
+       table->ACPILevel.CcPwrDynRm1 = 0;
+
+       table->ACPILevel.Flags = cpu_to_be32(table->ACPILevel.Flags);
+       table->ACPILevel.MinVddcPhases = cpu_to_be32(table->ACPILevel.MinVddcPhases);
+       table->ACPILevel.SclkFrequency = cpu_to_be32(table->ACPILevel.SclkFrequency);
+       table->ACPILevel.CgSpllFuncCntl = cpu_to_be32(table->ACPILevel.CgSpllFuncCntl);
+       table->ACPILevel.CgSpllFuncCntl2 = cpu_to_be32(table->ACPILevel.CgSpllFuncCntl2);
+       table->ACPILevel.CgSpllFuncCntl3 = cpu_to_be32(table->ACPILevel.CgSpllFuncCntl3);
+       table->ACPILevel.CgSpllFuncCntl4 = cpu_to_be32(table->ACPILevel.CgSpllFuncCntl4);
+       table->ACPILevel.SpllSpreadSpectrum = cpu_to_be32(table->ACPILevel.SpllSpreadSpectrum);
+       table->ACPILevel.SpllSpreadSpectrum2 = cpu_to_be32(table->ACPILevel.SpllSpreadSpectrum2);
+       table->ACPILevel.CcPwrDynRm = cpu_to_be32(table->ACPILevel.CcPwrDynRm);
+       table->ACPILevel.CcPwrDynRm1 = cpu_to_be32(table->ACPILevel.CcPwrDynRm1);
+
+       table->MemoryACPILevel.MinVddc = table->ACPILevel.MinVddc;
+       table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;
+
+       if (pi->vddci_control != CISLANDS_VOLTAGE_CONTROL_NONE) {
+               if (pi->acpi_vddci)
+                       table->MemoryACPILevel.MinVddci =
+                               cpu_to_be32(pi->acpi_vddci * VOLTAGE_SCALE);
+               else
+                       table->MemoryACPILevel.MinVddci =
+                               cpu_to_be32(pi->min_vddci_in_pp_table * VOLTAGE_SCALE);
+       }
+
+       if (ci_populate_mvdd_value(rdev, 0, &voltage_level))
+               table->MemoryACPILevel.MinMvdd = 0;
+       else
+               table->MemoryACPILevel.MinMvdd =
+                       cpu_to_be32(voltage_level.Voltage * VOLTAGE_SCALE);
+
+       mclk_pwrmgt_cntl |= MRDCK0_RESET | MRDCK1_RESET;
+       mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       dll_cntl &= ~(MRDCK0_BYPASS | MRDCK1_BYPASS);
+
+       table->MemoryACPILevel.DllCntl = cpu_to_be32(dll_cntl);
+       table->MemoryACPILevel.MclkPwrmgtCntl = cpu_to_be32(mclk_pwrmgt_cntl);
+       table->MemoryACPILevel.MpllAdFuncCntl =
+               cpu_to_be32(pi->clock_registers.mpll_ad_func_cntl);
+       table->MemoryACPILevel.MpllDqFuncCntl =
+               cpu_to_be32(pi->clock_registers.mpll_dq_func_cntl);
+       table->MemoryACPILevel.MpllFuncCntl =
+               cpu_to_be32(pi->clock_registers.mpll_func_cntl);
+       table->MemoryACPILevel.MpllFuncCntl_1 =
+               cpu_to_be32(pi->clock_registers.mpll_func_cntl_1);
+       table->MemoryACPILevel.MpllFuncCntl_2 =
+               cpu_to_be32(pi->clock_registers.mpll_func_cntl_2);
+       table->MemoryACPILevel.MpllSs1 = cpu_to_be32(pi->clock_registers.mpll_ss1);
+       table->MemoryACPILevel.MpllSs2 = cpu_to_be32(pi->clock_registers.mpll_ss2);
+
+       table->MemoryACPILevel.EnabledForThrottle = 0;
+       table->MemoryACPILevel.EnabledForActivity = 0;
+       table->MemoryACPILevel.UpH = 0;
+       table->MemoryACPILevel.DownH = 100;
+       table->MemoryACPILevel.VoltageDownH = 0;
+       table->MemoryACPILevel.ActivityLevel =
+               cpu_to_be16((u16)pi->mclk_activity_target);
+
+       table->MemoryACPILevel.StutterEnable = false;
+       table->MemoryACPILevel.StrobeEnable = false;
+       table->MemoryACPILevel.EdcReadEnable = false;
+       table->MemoryACPILevel.EdcWriteEnable = false;
+       table->MemoryACPILevel.RttEnable = false;
+
+       return 0;
+}
+
+
+static int ci_enable_ulv(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_ulv_parm *ulv = &pi->ulv;
+
+       if (ulv->supported) {
+               if (enable)
+                       return (ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableULV) == PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+               else
+                       return (ci_send_msg_to_smc(rdev, PPSMC_MSG_DisableULV) == PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ci_populate_ulv_level(struct radeon_device *rdev,
+                                SMU7_Discrete_Ulv *state)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u16 ulv_voltage = rdev->pm.dpm.backbias_response_time;
+
+       state->CcPwrDynRm = 0;
+       state->CcPwrDynRm1 = 0;
+
+       if (ulv_voltage == 0) {
+               pi->ulv.supported = false;
+               return 0;
+       }
+
+       if (pi->voltage_control != CISLANDS_VOLTAGE_CONTROL_BY_SVID2) {
+               if (ulv_voltage > rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[0].v)
+                       state->VddcOffset = 0;
+               else
+                       state->VddcOffset =
+                               rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[0].v - ulv_voltage;
+       } else {
+               if (ulv_voltage > rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[0].v)
+                       state->VddcOffsetVid = 0;
+               else
+                       state->VddcOffsetVid = (u8)
+                               ((rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[0].v - ulv_voltage) *
+                                VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+       }
+       state->VddcPhase = pi->vddc_phase_shed_control ? 0 : 1;
+
+       state->CcPwrDynRm = cpu_to_be32(state->CcPwrDynRm);
+       state->CcPwrDynRm1 = cpu_to_be32(state->CcPwrDynRm1);
+       state->VddcOffset = cpu_to_be16(state->VddcOffset);
+
+       return 0;
+}
+
+static int ci_calculate_sclk_params(struct radeon_device *rdev,
+                                   u32 engine_clock,
+                                   SMU7_Discrete_GraphicsLevel *sclk)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl_3 = pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = pi->clock_registers.cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum = pi->clock_registers.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 = pi->clock_registers.cg_spll_spread_spectrum_2;
+       u32 reference_clock = rdev->clock.spll.reference_freq;
+       u32 reference_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = radeon_atom_get_clock_dividers(rdev,
+                                            COMPUTE_GPUCLK_INPUT_FLAG_SCLK,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+       fbdiv = dividers.fb_div & 0x3FFFFFF;
+
+       spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
+       spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
+        spll_func_cntl_3 |= SPLL_DITHEN;
+
+       if (pi->caps_sclk_ss_support) {
+               struct radeon_atom_ss ss;
+               u32 vco_freq = engine_clock * dividers.post_div;
+
+               if (radeon_atombios_get_asic_ss_info(rdev, &ss,
+                                                    ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
+                       u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
+                       u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum &= ~CLK_S_MASK;
+                       cg_spll_spread_spectrum |= CLK_S(clk_s);
+                       cg_spll_spread_spectrum |= SSEN;
+
+                       cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
+                       cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
+               }
+       }
+
+       sclk->SclkFrequency = engine_clock;
+       sclk->CgSpllFuncCntl3 = spll_func_cntl_3;
+       sclk->CgSpllFuncCntl4 = spll_func_cntl_4;
+       sclk->SpllSpreadSpectrum = cg_spll_spread_spectrum;
+       sclk->SpllSpreadSpectrum2  = cg_spll_spread_spectrum_2;
+       sclk->SclkDid = (u8)dividers.post_divider;
+
+       return 0;
+}
+
+static int ci_populate_single_graphic_level(struct radeon_device *rdev,
+                                           u32 engine_clock,
+                                           u16 sclk_activity_level_t,
+                                           SMU7_Discrete_GraphicsLevel *graphic_level)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int ret;
+
+       ret = ci_calculate_sclk_params(rdev, engine_clock, graphic_level);
+       if (ret)
+               return ret;
+
+       ret = ci_get_dependency_volt_by_clk(rdev,
+                                           &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                           engine_clock, &graphic_level->MinVddc);
+       if (ret)
+               return ret;
+
+       graphic_level->SclkFrequency = engine_clock;
+
+       graphic_level->Flags =  0;
+       graphic_level->MinVddcPhases = 1;
+
+       if (pi->vddc_phase_shed_control)
+               ci_populate_phase_value_based_on_sclk(rdev,
+                                                     &rdev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                     engine_clock,
+                                                     &graphic_level->MinVddcPhases);
+
+       graphic_level->ActivityLevel = sclk_activity_level_t;
+
+       graphic_level->CcPwrDynRm = 0;
+       graphic_level->CcPwrDynRm1 = 0;
+       graphic_level->EnabledForActivity = 1;
+       graphic_level->EnabledForThrottle = 1;
+       graphic_level->UpH = 0;
+       graphic_level->DownH = 0;
+       graphic_level->VoltageDownH = 0;
+       graphic_level->PowerThrottle = 0;
+
+       if (pi->caps_sclk_ds)
+               graphic_level->DeepSleepDivId = ci_get_sleep_divider_id_from_clock(rdev,
+                                                                                  engine_clock,
+                                                                                  CISLAND_MINIMUM_ENGINE_CLOCK);
+
+       graphic_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       graphic_level->Flags = cpu_to_be32(graphic_level->Flags);
+        graphic_level->MinVddc = cpu_to_be32(graphic_level->MinVddc * VOLTAGE_SCALE);
+       graphic_level->MinVddcPhases = cpu_to_be32(graphic_level->MinVddcPhases);
+       graphic_level->SclkFrequency = cpu_to_be32(graphic_level->SclkFrequency);
+       graphic_level->ActivityLevel = cpu_to_be16(graphic_level->ActivityLevel);
+       graphic_level->CgSpllFuncCntl3 = cpu_to_be32(graphic_level->CgSpllFuncCntl3);
+       graphic_level->CgSpllFuncCntl4 = cpu_to_be32(graphic_level->CgSpllFuncCntl4);
+       graphic_level->SpllSpreadSpectrum = cpu_to_be32(graphic_level->SpllSpreadSpectrum);
+       graphic_level->SpllSpreadSpectrum2 = cpu_to_be32(graphic_level->SpllSpreadSpectrum2);
+       graphic_level->CcPwrDynRm = cpu_to_be32(graphic_level->CcPwrDynRm);
+       graphic_level->CcPwrDynRm1 = cpu_to_be32(graphic_level->CcPwrDynRm1);
+
+       return 0;
+}
+
+static int ci_populate_all_graphic_levels(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_dpm_table *dpm_table = &pi->dpm_table;
+       u32 level_array_address = pi->dpm_table_start +
+               offsetof(SMU7_Discrete_DpmTable, GraphicsLevel);
+       u32 level_array_size = sizeof(SMU7_Discrete_GraphicsLevel) *
+               SMU7_MAX_LEVELS_GRAPHICS;
+       SMU7_Discrete_GraphicsLevel *levels = pi->smc_state_table.GraphicsLevel;
+       u32 i, ret;
+
+       memset(levels, 0, level_array_size);
+
+       for (i = 0; i < dpm_table->sclk_table.count; i++) {
+               ret = ci_populate_single_graphic_level(rdev,
+                                                      dpm_table->sclk_table.dpm_levels[i].value,
+                                                      (u16)pi->activity_target[i],
+                                                      &pi->smc_state_table.GraphicsLevel[i]);
+               if (ret)
+                       return ret;
+               if (i == (dpm_table->sclk_table.count - 1))
+                       pi->smc_state_table.GraphicsLevel[i].DisplayWatermark =
+                               PPSMC_DISPLAY_WATERMARK_HIGH;
+       }
+
+       pi->smc_state_table.GraphicsDpmLevelCount = (u8)dpm_table->sclk_table.count;
+       pi->dpm_level_enable_mask.sclk_dpm_enable_mask =
+               ci_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+       ret = ci_copy_bytes_to_smc(rdev, level_array_address,
+                                  (u8 *)levels, level_array_size,
+                                  pi->sram_end);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ci_populate_ulv_state(struct radeon_device *rdev,
+                                SMU7_Discrete_Ulv *ulv_level)
+{
+       return ci_populate_ulv_level(rdev, ulv_level);
+}
+
+static int ci_populate_all_memory_levels(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_dpm_table *dpm_table = &pi->dpm_table;
+       u32 level_array_address = pi->dpm_table_start +
+               offsetof(SMU7_Discrete_DpmTable, MemoryLevel);
+       u32 level_array_size = sizeof(SMU7_Discrete_MemoryLevel) *
+               SMU7_MAX_LEVELS_MEMORY;
+       SMU7_Discrete_MemoryLevel *levels = pi->smc_state_table.MemoryLevel;
+       u32 i, ret;
+
+       memset(levels, 0, level_array_size);
+
+       for (i = 0; i < dpm_table->mclk_table.count; i++) {
+               if (dpm_table->mclk_table.dpm_levels[i].value == 0)
+                       return -EINVAL;
+               ret = ci_populate_single_memory_level(rdev,
+                                                     dpm_table->mclk_table.dpm_levels[i].value,
+                                                     &pi->smc_state_table.MemoryLevel[i]);
+               if (ret)
+                       return ret;
+       }
+
+       pi->smc_state_table.MemoryLevel[0].ActivityLevel = cpu_to_be16(0x1F);
+
+       pi->smc_state_table.MemoryDpmLevelCount = (u8)dpm_table->mclk_table.count;
+       pi->dpm_level_enable_mask.mclk_dpm_enable_mask =
+               ci_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+
+       pi->smc_state_table.MemoryLevel[dpm_table->mclk_table.count - 1].DisplayWatermark =
+               PPSMC_DISPLAY_WATERMARK_HIGH;
+
+       ret = ci_copy_bytes_to_smc(rdev, level_array_address,
+                                  (u8 *)levels, level_array_size,
+                                  pi->sram_end);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void ci_reset_single_dpm_table(struct radeon_device *rdev,
+                                     struct ci_single_dpm_table* dpm_table,
+                                     u32 count)
+{
+       u32 i;
+
+       dpm_table->count = count;
+       for (i = 0; i < MAX_REGULAR_DPM_NUMBER; i++)
+               dpm_table->dpm_levels[i].enabled = false;
+}
+
+static void ci_setup_pcie_table_entry(struct ci_single_dpm_table* dpm_table,
+                                     u32 index, u32 pcie_gen, u32 pcie_lanes)
+{
+       dpm_table->dpm_levels[index].value = pcie_gen;
+       dpm_table->dpm_levels[index].param1 = pcie_lanes;
+       dpm_table->dpm_levels[index].enabled = true;
+}
+
+static int ci_setup_default_pcie_tables(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (!pi->use_pcie_performance_levels && !pi->use_pcie_powersaving_levels)
+               return -EINVAL;
+
+       if (pi->use_pcie_performance_levels && !pi->use_pcie_powersaving_levels) {
+               pi->pcie_gen_powersaving = pi->pcie_gen_performance;
+               pi->pcie_lane_powersaving = pi->pcie_lane_performance;
+       } else if (!pi->use_pcie_performance_levels && pi->use_pcie_powersaving_levels) {
+               pi->pcie_gen_performance = pi->pcie_gen_powersaving;
+               pi->pcie_lane_performance = pi->pcie_lane_powersaving;
+       }
+
+       ci_reset_single_dpm_table(rdev,
+                                 &pi->dpm_table.pcie_speed_table,
+                                 SMU7_MAX_LEVELS_LINK);
+
+       ci_setup_pcie_table_entry(&pi->dpm_table.pcie_speed_table, 0,
+                                 pi->pcie_gen_powersaving.min,
+                                 pi->pcie_lane_powersaving.min);
+       ci_setup_pcie_table_entry(&pi->dpm_table.pcie_speed_table, 1,
+                                 pi->pcie_gen_performance.min,
+                                 pi->pcie_lane_performance.min);
+       ci_setup_pcie_table_entry(&pi->dpm_table.pcie_speed_table, 2,
+                                 pi->pcie_gen_powersaving.min,
+                                 pi->pcie_lane_powersaving.max);
+       ci_setup_pcie_table_entry(&pi->dpm_table.pcie_speed_table, 3,
+                                 pi->pcie_gen_performance.min,
+                                 pi->pcie_lane_performance.max);
+       ci_setup_pcie_table_entry(&pi->dpm_table.pcie_speed_table, 4,
+                                 pi->pcie_gen_powersaving.max,
+                                 pi->pcie_lane_powersaving.max);
+       ci_setup_pcie_table_entry(&pi->dpm_table.pcie_speed_table, 5,
+                                 pi->pcie_gen_performance.max,
+                                 pi->pcie_lane_performance.max);
+
+       pi->dpm_table.pcie_speed_table.count = 6;
+
+       return 0;
+}
+
+static int ci_setup_default_dpm_tables(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_clock_voltage_dependency_table *allowed_sclk_vddc_table =
+               &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+       struct radeon_clock_voltage_dependency_table *allowed_mclk_table =
+               &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk;
+       struct radeon_cac_leakage_table *std_voltage_table =
+               &rdev->pm.dpm.dyn_state.cac_leakage_table;
+       u32 i;
+
+       if (allowed_sclk_vddc_table == NULL)
+               return -EINVAL;
+       if (allowed_sclk_vddc_table->count < 1)
+               return -EINVAL;
+       if (allowed_mclk_table == NULL)
+               return -EINVAL;
+       if (allowed_mclk_table->count < 1)
+               return -EINVAL;
+
+       memset(&pi->dpm_table, 0, sizeof(struct ci_dpm_table));
+
+       ci_reset_single_dpm_table(rdev,
+                                 &pi->dpm_table.sclk_table,
+                                 SMU7_MAX_LEVELS_GRAPHICS);
+       ci_reset_single_dpm_table(rdev,
+                                 &pi->dpm_table.mclk_table,
+                                 SMU7_MAX_LEVELS_MEMORY);
+       ci_reset_single_dpm_table(rdev,
+                                 &pi->dpm_table.vddc_table,
+                                 SMU7_MAX_LEVELS_VDDC);
+       ci_reset_single_dpm_table(rdev,
+                                 &pi->dpm_table.vddci_table,
+                                 SMU7_MAX_LEVELS_VDDCI);
+       ci_reset_single_dpm_table(rdev,
+                                 &pi->dpm_table.mvdd_table,
+                                 SMU7_MAX_LEVELS_MVDD);
+
+       pi->dpm_table.sclk_table.count = 0;
+       for (i = 0; i < allowed_sclk_vddc_table->count; i++) {
+               if ((i == 0) ||
+                   (pi->dpm_table.sclk_table.dpm_levels[pi->dpm_table.sclk_table.count-1].value !=
+                    allowed_sclk_vddc_table->entries[i].clk)) {
+                       pi->dpm_table.sclk_table.dpm_levels[pi->dpm_table.sclk_table.count].value =
+                               allowed_sclk_vddc_table->entries[i].clk;
+                       pi->dpm_table.sclk_table.dpm_levels[pi->dpm_table.sclk_table.count].enabled = true;
+                       pi->dpm_table.sclk_table.count++;
+               }
+       }
+
+       pi->dpm_table.mclk_table.count = 0;
+       for (i = 0; i < allowed_mclk_table->count; i++) {
+               if ((i==0) ||
+                   (pi->dpm_table.mclk_table.dpm_levels[pi->dpm_table.mclk_table.count-1].value !=
+                    allowed_mclk_table->entries[i].clk)) {
+                       pi->dpm_table.mclk_table.dpm_levels[pi->dpm_table.mclk_table.count].value =
+                               allowed_mclk_table->entries[i].clk;
+                       pi->dpm_table.mclk_table.dpm_levels[pi->dpm_table.mclk_table.count].enabled = true;
+                       pi->dpm_table.mclk_table.count++;
+               }
+       }
+
+       for (i = 0; i < allowed_sclk_vddc_table->count; i++) {
+               pi->dpm_table.vddc_table.dpm_levels[i].value =
+                       allowed_sclk_vddc_table->entries[i].v;
+               pi->dpm_table.vddc_table.dpm_levels[i].param1 =
+                       std_voltage_table->entries[i].leakage;
+               pi->dpm_table.vddc_table.dpm_levels[i].enabled = true;
+       }
+       pi->dpm_table.vddc_table.count = allowed_sclk_vddc_table->count;
+
+       allowed_mclk_table = &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk;
+       if (allowed_mclk_table) {
+               for (i = 0; i < allowed_mclk_table->count; i++) {
+                       pi->dpm_table.vddci_table.dpm_levels[i].value =
+                               allowed_mclk_table->entries[i].v;
+                       pi->dpm_table.vddci_table.dpm_levels[i].enabled = true;
+               }
+               pi->dpm_table.vddci_table.count = allowed_mclk_table->count;
+       }
+
+       allowed_mclk_table = &rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk;
+       if (allowed_mclk_table) {
+               for (i = 0; i < allowed_mclk_table->count; i++) {
+                       pi->dpm_table.mvdd_table.dpm_levels[i].value =
+                               allowed_mclk_table->entries[i].v;
+                       pi->dpm_table.mvdd_table.dpm_levels[i].enabled = true;
+               }
+               pi->dpm_table.mvdd_table.count = allowed_mclk_table->count;
+       }
+
+       ci_setup_default_pcie_tables(rdev);
+
+       return 0;
+}
+
+static int ci_find_boot_level(struct ci_single_dpm_table *table,
+                             u32 value, u32 *boot_level)
+{
+       u32 i;
+       int ret = -EINVAL;
+
+       for(i = 0; i < table->count; i++) {
+               if (value == table->dpm_levels[i].value) {
+                       *boot_level = i;
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
+static int ci_init_smc_table(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_ulv_parm *ulv = &pi->ulv;
+       struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps;
+       SMU7_Discrete_DpmTable *table = &pi->smc_state_table;
+       int ret;
+
+       ret = ci_setup_default_dpm_tables(rdev);
+       if (ret)
+               return ret;
+
+       if (pi->voltage_control != CISLANDS_VOLTAGE_CONTROL_NONE)
+               ci_populate_smc_voltage_tables(rdev, table);
+
+       ci_init_fps_limits(rdev);
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (pi->mem_gddr5)
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       if (ulv->supported) {
+               ret = ci_populate_ulv_state(rdev, &pi->smc_state_table.Ulv);
+               if (ret)
+                       return ret;
+               WREG32_SMC(CG_ULV_PARAMETER, ulv->cg_ulv_parameter);
+       }
+
+       ret = ci_populate_all_graphic_levels(rdev);
+       if (ret)
+               return ret;
+
+       ret = ci_populate_all_memory_levels(rdev);
+       if (ret)
+               return ret;
+
+       ci_populate_smc_link_level(rdev, table);
+
+       ret = ci_populate_smc_acpi_level(rdev, table);
+       if (ret)
+               return ret;
+
+       ret = ci_populate_smc_vce_level(rdev, table);
+       if (ret)
+               return ret;
+
+       ret = ci_populate_smc_acp_level(rdev, table);
+       if (ret)
+               return ret;
+
+       ret = ci_populate_smc_samu_level(rdev, table);
+       if (ret)
+               return ret;
+
+       ret = ci_do_program_memory_timing_parameters(rdev);
+       if (ret)
+               return ret;
+
+       ret = ci_populate_smc_uvd_level(rdev, table);
+       if (ret)
+               return ret;
+
+       table->UvdBootLevel  = 0;
+       table->VceBootLevel  = 0;
+       table->AcpBootLevel  = 0;
+       table->SamuBootLevel  = 0;
+       table->GraphicsBootLevel  = 0;
+       table->MemoryBootLevel  = 0;
+
+       ret = ci_find_boot_level(&pi->dpm_table.sclk_table,
+                                pi->vbios_boot_state.sclk_bootup_value,
+                                (u32 *)&pi->smc_state_table.GraphicsBootLevel);
+
+       ret = ci_find_boot_level(&pi->dpm_table.mclk_table,
+                                pi->vbios_boot_state.mclk_bootup_value,
+                                (u32 *)&pi->smc_state_table.MemoryBootLevel);
+
+       table->BootVddc = pi->vbios_boot_state.vddc_bootup_value;
+       table->BootVddci = pi->vbios_boot_state.vddci_bootup_value;
+       table->BootMVdd = pi->vbios_boot_state.mvdd_bootup_value;
+
+       ci_populate_smc_initial_state(rdev, radeon_boot_state);
+
+       ret = ci_populate_bapm_parameters_in_dpm_table(rdev);
+       if (ret)
+               return ret;
+
+       table->UVDInterval = 1;
+       table->VCEInterval = 1;
+       table->ACPInterval = 1;
+       table->SAMUInterval = 1;
+       table->GraphicsVoltageChangeEnable = 1;
+       table->GraphicsThermThrottleEnable = 1;
+       table->GraphicsInterval = 1;
+       table->VoltageInterval = 1;
+       table->ThermalInterval = 1;
+       table->TemperatureLimitHigh = (u16)((pi->thermal_temp_setting.temperature_high *
+                                            CISLANDS_Q88_FORMAT_CONVERSION_UNIT) / 1000);
+       table->TemperatureLimitLow = (u16)((pi->thermal_temp_setting.temperature_low *
+                                           CISLANDS_Q88_FORMAT_CONVERSION_UNIT) / 1000);
+       table->MemoryVoltageChangeEnable = 1;
+       table->MemoryInterval = 1;
+       table->VoltageResponseTime = 0;
+       table->VddcVddciDelta = 4000;
+       table->PhaseResponseTime = 0;
+       table->MemoryThermThrottleEnable = 1;
+       table->PCIeBootLinkLevel = 0;
+       table->PCIeGenInterval = 1;
+       if (pi->voltage_control == CISLANDS_VOLTAGE_CONTROL_BY_SVID2)
+               table->SVI2Enable  = 1;
+       else
+               table->SVI2Enable  = 0;
+
+       table->ThermGpio = 17;
+       table->SclkStepSize = 0x4000;
+
+       table->SystemFlags = cpu_to_be32(table->SystemFlags);
+       table->SmioMaskVddcVid = cpu_to_be32(table->SmioMaskVddcVid);
+       table->SmioMaskVddcPhase = cpu_to_be32(table->SmioMaskVddcPhase);
+       table->SmioMaskVddciVid = cpu_to_be32(table->SmioMaskVddciVid);
+       table->SmioMaskMvddVid = cpu_to_be32(table->SmioMaskMvddVid);
+       table->SclkStepSize = cpu_to_be32(table->SclkStepSize);
+       table->TemperatureLimitHigh = cpu_to_be16(table->TemperatureLimitHigh);
+       table->TemperatureLimitLow = cpu_to_be16(table->TemperatureLimitLow);
+       table->VddcVddciDelta = cpu_to_be16(table->VddcVddciDelta);
+       table->VoltageResponseTime = cpu_to_be16(table->VoltageResponseTime);
+       table->PhaseResponseTime = cpu_to_be16(table->PhaseResponseTime);
+       table->BootVddc = cpu_to_be16(table->BootVddc * VOLTAGE_SCALE);
+       table->BootVddci = cpu_to_be16(table->BootVddci * VOLTAGE_SCALE);
+       table->BootMVdd = cpu_to_be16(table->BootMVdd * VOLTAGE_SCALE);
+
+       ret = ci_copy_bytes_to_smc(rdev,
+                                  pi->dpm_table_start +
+                                  offsetof(SMU7_Discrete_DpmTable, SystemFlags),
+                                  (u8 *)&table->SystemFlags,
+                                  sizeof(SMU7_Discrete_DpmTable) - 3 * sizeof(SMU7_PIDController),
+                                  pi->sram_end);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void ci_trim_single_dpm_states(struct radeon_device *rdev,
+                                     struct ci_single_dpm_table *dpm_table,
+                                     u32 low_limit, u32 high_limit)
+{
+       u32 i;
+
+       for (i = 0; i < dpm_table->count; i++) {
+               if ((dpm_table->dpm_levels[i].value < low_limit) ||
+                   (dpm_table->dpm_levels[i].value > high_limit))
+                       dpm_table->dpm_levels[i].enabled = false;
+               else
+                       dpm_table->dpm_levels[i].enabled = true;
+       }
+}
+
+static void ci_trim_pcie_dpm_states(struct radeon_device *rdev,
+                                   u32 speed_low, u32 lanes_low,
+                                   u32 speed_high, u32 lanes_high)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_single_dpm_table *pcie_table = &pi->dpm_table.pcie_speed_table;
+       u32 i, j;
+
+       for (i = 0; i < pcie_table->count; i++) {
+               if ((pcie_table->dpm_levels[i].value < speed_low) ||
+                   (pcie_table->dpm_levels[i].param1 < lanes_low) ||
+                   (pcie_table->dpm_levels[i].value > speed_high) ||
+                   (pcie_table->dpm_levels[i].param1 > lanes_high))
+                       pcie_table->dpm_levels[i].enabled = false;
+               else
+                       pcie_table->dpm_levels[i].enabled = true;
+       }
+
+       for (i = 0; i < pcie_table->count; i++) {
+               if (pcie_table->dpm_levels[i].enabled) {
+                       for (j = i + 1; j < pcie_table->count; j++) {
+                               if (pcie_table->dpm_levels[j].enabled) {
+                                       if ((pcie_table->dpm_levels[i].value == pcie_table->dpm_levels[j].value) &&
+                                           (pcie_table->dpm_levels[i].param1 == pcie_table->dpm_levels[j].param1))
+                                               pcie_table->dpm_levels[j].enabled = false;
+                               }
+                       }
+               }
+       }
+}
+
+static int ci_trim_dpm_states(struct radeon_device *rdev,
+                             struct radeon_ps *radeon_state)
+{
+       struct ci_ps *state = ci_get_ps(radeon_state);
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 high_limit_count;
+
+       if (state->performance_level_count < 1)
+               return -EINVAL;
+
+       if (state->performance_level_count == 1)
+               high_limit_count = 0;
+       else
+               high_limit_count = 1;
+
+       ci_trim_single_dpm_states(rdev,
+                                 &pi->dpm_table.sclk_table,
+                                 state->performance_levels[0].sclk,
+                                 state->performance_levels[high_limit_count].sclk);
+
+       ci_trim_single_dpm_states(rdev,
+                                 &pi->dpm_table.mclk_table,
+                                 state->performance_levels[0].mclk,
+                                 state->performance_levels[high_limit_count].mclk);
+
+       ci_trim_pcie_dpm_states(rdev,
+                               state->performance_levels[0].pcie_gen,
+                               state->performance_levels[0].pcie_lane,
+                               state->performance_levels[high_limit_count].pcie_gen,
+                               state->performance_levels[high_limit_count].pcie_lane);
+
+       return 0;
+}
+
+static int ci_apply_disp_minimum_voltage_request(struct radeon_device *rdev)
+{
+       struct radeon_clock_voltage_dependency_table *disp_voltage_table =
+               &rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk;
+       struct radeon_clock_voltage_dependency_table *vddc_table =
+               &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+       u32 requested_voltage = 0;
+       u32 i;
+
+       if (disp_voltage_table == NULL)
+               return -EINVAL;
+       if (!disp_voltage_table->count)
+               return -EINVAL;
+
+       for (i = 0; i < disp_voltage_table->count; i++) {
+               if (rdev->clock.current_dispclk == disp_voltage_table->entries[i].clk)
+                       requested_voltage = disp_voltage_table->entries[i].v;
+       }
+
+       for (i = 0; i < vddc_table->count; i++) {
+               if (requested_voltage <= vddc_table->entries[i].v) {
+                       requested_voltage = vddc_table->entries[i].v;
+                       return (ci_send_msg_to_smc_with_parameter(rdev,
+                                                                 PPSMC_MSG_VddC_Request,
+                                                                 requested_voltage * VOLTAGE_SCALE) == PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int ci_upload_dpm_level_enable_mask(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result result;
+
+       if (!pi->sclk_dpm_key_disabled) {
+               if (pi->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+                       result = ci_send_msg_to_smc_with_parameter(rdev,
+                                                                  PPSMC_MSG_SCLKDPM_SetEnabledMask,
+                                                                  pi->dpm_level_enable_mask.sclk_dpm_enable_mask);
+                       if (result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+       }
+
+       if (!pi->mclk_dpm_key_disabled) {
+               if (pi->dpm_level_enable_mask.mclk_dpm_enable_mask) {
+                       result = ci_send_msg_to_smc_with_parameter(rdev,
+                                                                  PPSMC_MSG_MCLKDPM_SetEnabledMask,
+                                                                  pi->dpm_level_enable_mask.mclk_dpm_enable_mask);
+                       if (result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+       }
+
+       if (!pi->pcie_dpm_key_disabled) {
+               if (pi->dpm_level_enable_mask.pcie_dpm_enable_mask) {
+                       result = ci_send_msg_to_smc_with_parameter(rdev,
+                                                                  PPSMC_MSG_PCIeDPM_SetEnabledMask,
+                                                                  pi->dpm_level_enable_mask.pcie_dpm_enable_mask);
+                       if (result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+       }
+
+       ci_apply_disp_minimum_voltage_request(rdev);
+
+       return 0;
+}
+
+static void ci_find_dpm_states_clocks_in_dpm_table(struct radeon_device *rdev,
+                                                  struct radeon_ps *radeon_state)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_ps *state = ci_get_ps(radeon_state);
+       struct ci_single_dpm_table *sclk_table = &pi->dpm_table.sclk_table;
+       u32 sclk = state->performance_levels[state->performance_level_count-1].sclk;
+       struct ci_single_dpm_table *mclk_table = &pi->dpm_table.mclk_table;
+       u32 mclk = state->performance_levels[state->performance_level_count-1].mclk;
+       u32 i;
+
+       pi->need_update_smu7_dpm_table = 0;
+
+       for (i = 0; i < sclk_table->count; i++) {
+               if (sclk == sclk_table->dpm_levels[i].value)
+                       break;
+       }
+
+       if (i >= sclk_table->count) {
+               pi->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+       } else {
+               /* XXX check display min clock requirements */
+               if (0 != CISLAND_MINIMUM_ENGINE_CLOCK)
+                       pi->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_SCLK;
+       }
+
+       for (i = 0; i < mclk_table->count; i++) {
+               if (mclk == mclk_table->dpm_levels[i].value)
+                       break;
+       }
+
+       if (i >= mclk_table->count)
+               pi->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+
+       if (rdev->pm.dpm.current_active_crtc_count !=
+           rdev->pm.dpm.new_active_crtc_count)
+               pi->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_MCLK;
+}
+
+static int ci_populate_and_upload_sclk_mclk_dpm_levels(struct radeon_device *rdev,
+                                                      struct radeon_ps *radeon_state)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_ps *state = ci_get_ps(radeon_state);
+       u32 sclk = state->performance_levels[state->performance_level_count-1].sclk;
+       u32 mclk = state->performance_levels[state->performance_level_count-1].mclk;
+       struct ci_dpm_table *dpm_table = &pi->dpm_table;
+       int ret;
+
+       if (!pi->need_update_smu7_dpm_table)
+               return 0;
+
+       if (pi->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_SCLK)
+               dpm_table->sclk_table.dpm_levels[dpm_table->sclk_table.count-1].value = sclk;
+
+       if (pi->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)
+               dpm_table->mclk_table.dpm_levels[dpm_table->mclk_table.count-1].value = mclk;
+
+       if (pi->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_SCLK | DPMTABLE_UPDATE_SCLK)) {
+               ret = ci_populate_all_graphic_levels(rdev);
+               if (ret)
+                       return ret;
+       }
+
+       if (pi->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_MCLK | DPMTABLE_UPDATE_MCLK)) {
+               ret = ci_populate_all_memory_levels(rdev);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int ci_enable_uvd_dpm(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct radeon_clock_and_voltage_limits *max_limits;
+       int i;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       if (enable) {
+               pi->dpm_level_enable_mask.uvd_dpm_enable_mask = 0;
+
+               for (i = rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count - 1; i >= 0; i--) {
+                       if (rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.entries[i].v <= max_limits->vddc) {
+                               pi->dpm_level_enable_mask.uvd_dpm_enable_mask |= 1 << i;
+
+                               if (!pi->caps_uvd_dpm)
+                                       break;
+                       }
+               }
+
+               ci_send_msg_to_smc_with_parameter(rdev,
+                                                 PPSMC_MSG_UVDDPM_SetEnabledMask,
+                                                 pi->dpm_level_enable_mask.uvd_dpm_enable_mask);
+
+               if (pi->last_mclk_dpm_enable_mask & 0x1) {
+                       pi->uvd_enabled = true;
+                       pi->dpm_level_enable_mask.mclk_dpm_enable_mask &= 0xFFFFFFFE;
+                       ci_send_msg_to_smc_with_parameter(rdev,
+                                                         PPSMC_MSG_MCLKDPM_SetEnabledMask,
+                                                         pi->dpm_level_enable_mask.mclk_dpm_enable_mask);
+               }
+       } else {
+               if (pi->last_mclk_dpm_enable_mask & 0x1) {
+                       pi->uvd_enabled = false;
+                       pi->dpm_level_enable_mask.mclk_dpm_enable_mask |= 1;
+                       ci_send_msg_to_smc_with_parameter(rdev,
+                                                         PPSMC_MSG_MCLKDPM_SetEnabledMask,
+                                                         pi->dpm_level_enable_mask.mclk_dpm_enable_mask);
+               }
+       }
+
+       return (ci_send_msg_to_smc(rdev, enable ?
+                                  PPSMC_MSG_UVDDPM_Enable : PPSMC_MSG_UVDDPM_Disable) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+#if 0
+static int ci_enable_vce_dpm(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct radeon_clock_and_voltage_limits *max_limits;
+       int i;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       if (enable) {
+               pi->dpm_level_enable_mask.vce_dpm_enable_mask = 0;
+               for (i = rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.count - 1; i >= 0; i--) {
+                       if (rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table.entries[i].v <= max_limits->vddc) {
+                               pi->dpm_level_enable_mask.vce_dpm_enable_mask |= 1 << i;
+
+                               if (!pi->caps_vce_dpm)
+                                       break;
+                       }
+               }
+
+               ci_send_msg_to_smc_with_parameter(rdev,
+                                                 PPSMC_MSG_VCEDPM_SetEnabledMask,
+                                                 pi->dpm_level_enable_mask.vce_dpm_enable_mask);
+       }
+
+       return (ci_send_msg_to_smc(rdev, enable ?
+                                  PPSMC_MSG_VCEDPM_Enable : PPSMC_MSG_VCEDPM_Disable) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int ci_enable_samu_dpm(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct radeon_clock_and_voltage_limits *max_limits;
+       int i;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       if (enable) {
+               pi->dpm_level_enable_mask.samu_dpm_enable_mask = 0;
+               for (i = rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.count - 1; i >= 0; i--) {
+                       if (rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table.entries[i].v <= max_limits->vddc) {
+                               pi->dpm_level_enable_mask.samu_dpm_enable_mask |= 1 << i;
+
+                               if (!pi->caps_samu_dpm)
+                                       break;
+                       }
+               }
+
+               ci_send_msg_to_smc_with_parameter(rdev,
+                                                 PPSMC_MSG_SAMUDPM_SetEnabledMask,
+                                                 pi->dpm_level_enable_mask.samu_dpm_enable_mask);
+       }
+       return (ci_send_msg_to_smc(rdev, enable ?
+                                  PPSMC_MSG_SAMUDPM_Enable : PPSMC_MSG_SAMUDPM_Disable) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int ci_enable_acp_dpm(struct radeon_device *rdev, bool enable)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       const struct radeon_clock_and_voltage_limits *max_limits;
+       int i;
+
+       if (rdev->pm.dpm.ac_power)
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       if (enable) {
+               pi->dpm_level_enable_mask.acp_dpm_enable_mask = 0;
+               for (i = rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.count - 1; i >= 0; i--) {
+                       if (rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table.entries[i].v <= max_limits->vddc) {
+                               pi->dpm_level_enable_mask.acp_dpm_enable_mask |= 1 << i;
+
+                               if (!pi->caps_acp_dpm)
+                                       break;
+                       }
+               }
+
+               ci_send_msg_to_smc_with_parameter(rdev,
+                                                 PPSMC_MSG_ACPDPM_SetEnabledMask,
+                                                 pi->dpm_level_enable_mask.acp_dpm_enable_mask);
+       }
+
+       return (ci_send_msg_to_smc(rdev, enable ?
+                                  PPSMC_MSG_ACPDPM_Enable : PPSMC_MSG_ACPDPM_Disable) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+#endif
+
+static int ci_update_uvd_dpm(struct radeon_device *rdev, bool gate)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+
+       if (!gate) {
+               if (pi->caps_uvd_dpm ||
+                   (rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count <= 0))
+                       pi->smc_state_table.UvdBootLevel = 0;
+               else
+                       pi->smc_state_table.UvdBootLevel =
+                               rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table.count - 1;
+
+               tmp = RREG32_SMC(DPM_TABLE_475);
+               tmp &= ~UvdBootLevel_MASK;
+               tmp |= UvdBootLevel(pi->smc_state_table.UvdBootLevel);
+               WREG32_SMC(DPM_TABLE_475, tmp);
+       }
+
+       return ci_enable_uvd_dpm(rdev, !gate);
+}
+
+#if 0
+static u8 ci_get_vce_boot_level(struct radeon_device *rdev)
+{
+       u8 i;
+       u32 min_evclk = 30000; /* ??? */
+       struct radeon_vce_clock_voltage_dependency_table *table =
+               &rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+
+       for (i = 0; i < table->count; i++) {
+               if (table->entries[i].evclk >= min_evclk)
+                       return i;
+       }
+
+       return table->count - 1;
+}
+
+static int ci_update_vce_dpm(struct radeon_device *rdev,
+                            struct radeon_ps *radeon_new_state,
+                            struct radeon_ps *radeon_current_state)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       bool new_vce_clock_non_zero = (radeon_new_state->evclk != 0);
+       bool old_vce_clock_non_zero = (radeon_current_state->evclk != 0);
+       int ret = 0;
+       u32 tmp;
+
+       if (new_vce_clock_non_zero != old_vce_clock_non_zero) {
+               if (new_vce_clock_non_zero) {
+                       pi->smc_state_table.VceBootLevel = ci_get_vce_boot_level(rdev);
+
+                       tmp = RREG32_SMC(DPM_TABLE_475);
+                       tmp &= ~VceBootLevel_MASK;
+                       tmp |= VceBootLevel(pi->smc_state_table.VceBootLevel);
+                       WREG32_SMC(DPM_TABLE_475, tmp);
+
+                       ret = ci_enable_vce_dpm(rdev, true);
+               } else {
+                       ret = ci_enable_vce_dpm(rdev, false);
+               }
+       }
+       return ret;
+}
+
+static int ci_update_samu_dpm(struct radeon_device *rdev, bool gate)
+{
+       return ci_enable_samu_dpm(rdev, gate);
+}
+
+static int ci_update_acp_dpm(struct radeon_device *rdev, bool gate)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+
+       if (!gate) {
+               pi->smc_state_table.AcpBootLevel = 0;
+
+               tmp = RREG32_SMC(DPM_TABLE_475);
+               tmp &= ~AcpBootLevel_MASK;
+               tmp |= AcpBootLevel(pi->smc_state_table.AcpBootLevel);
+               WREG32_SMC(DPM_TABLE_475, tmp);
+       }
+
+       return ci_enable_acp_dpm(rdev, !gate);
+}
+#endif
+
+static int ci_generate_dpm_level_enable_mask(struct radeon_device *rdev,
+                                            struct radeon_ps *radeon_state)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int ret;
+
+       ret = ci_trim_dpm_states(rdev, radeon_state);
+       if (ret)
+               return ret;
+
+       pi->dpm_level_enable_mask.sclk_dpm_enable_mask =
+               ci_get_dpm_level_enable_mask_value(&pi->dpm_table.sclk_table);
+       pi->dpm_level_enable_mask.mclk_dpm_enable_mask =
+               ci_get_dpm_level_enable_mask_value(&pi->dpm_table.mclk_table);
+       pi->last_mclk_dpm_enable_mask =
+               pi->dpm_level_enable_mask.mclk_dpm_enable_mask;
+       if (pi->uvd_enabled) {
+               if (pi->dpm_level_enable_mask.mclk_dpm_enable_mask & 1)
+                       pi->dpm_level_enable_mask.mclk_dpm_enable_mask &= 0xFFFFFFFE;
+       }
+       pi->dpm_level_enable_mask.pcie_dpm_enable_mask =
+               ci_get_dpm_level_enable_mask_value(&pi->dpm_table.pcie_speed_table);
+
+       return 0;
+}
+
+static int ci_set_mc_special_registers(struct radeon_device *rdev,
+                                      struct ci_mc_reg_table *table)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u8 i, j, k;
+       u32 temp_reg;
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               if (j >= SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE)
+                       return -EINVAL;
+               switch(table->mc_reg_address[i].s1 << 2) {
+               case MC_SEQ_MISC1:
+                       temp_reg = RREG32(MC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       ((temp_reg & 0xffff0000)) | ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
+                       }
+                       j++;
+                       if (j >= SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       temp_reg = RREG32(MC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) | (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                               if (!pi->mem_gddr5)
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                       }
+                       j++;
+                       if (j > SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       if (!pi->mem_gddr5) {
+                               table->mc_reg_address[j].s1 = MC_PMG_AUTO_CMD >> 2;
+                               table->mc_reg_address[j].s0 = MC_PMG_AUTO_CMD >> 2;
+                               for (k = 0; k < table->num_entries; k++) {
+                                       table->mc_reg_table_entry[k].mc_data[j] =
+                                               (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+                               }
+                               j++;
+                               if (j > SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE)
+                                       return -EINVAL;
+                       }
+                       break;
+               case MC_SEQ_RESERVE_M:
+                       temp_reg = RREG32(MC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+                       for (k = 0; k < table->num_entries; k++) {
+                               table->mc_reg_table_entry[k].mc_data[j] =
+                                       (temp_reg & 0xffff0000) | (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
+                       }
+                       j++;
+                       if (j > SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               default:
+                       break;
+               }
+
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static bool ci_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg)
+{
+       bool result = true;
+
+       switch(in_reg) {
+       case MC_SEQ_RAS_TIMING >> 2:
+               *out_reg = MC_SEQ_RAS_TIMING_LP >> 2;
+               break;
+       case MC_SEQ_DLL_STBY >> 2:
+               *out_reg = MC_SEQ_DLL_STBY_LP >> 2;
+               break;
+       case MC_SEQ_G5PDX_CMD0 >> 2:
+               *out_reg = MC_SEQ_G5PDX_CMD0_LP >> 2;
+               break;
+       case MC_SEQ_G5PDX_CMD1 >> 2:
+               *out_reg = MC_SEQ_G5PDX_CMD1_LP >> 2;
+               break;
+       case MC_SEQ_G5PDX_CTRL >> 2:
+               *out_reg = MC_SEQ_G5PDX_CTRL_LP >> 2;
+               break;
+       case MC_SEQ_CAS_TIMING >> 2:
+               *out_reg = MC_SEQ_CAS_TIMING_LP >> 2;
+            break;
+       case MC_SEQ_MISC_TIMING >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING_LP >> 2;
+               break;
+       case MC_SEQ_MISC_TIMING2 >> 2:
+               *out_reg = MC_SEQ_MISC_TIMING2_LP >> 2;
+               break;
+       case MC_SEQ_PMG_DVS_CMD >> 2:
+               *out_reg = MC_SEQ_PMG_DVS_CMD_LP >> 2;
+               break;
+       case MC_SEQ_PMG_DVS_CTL >> 2:
+               *out_reg = MC_SEQ_PMG_DVS_CTL_LP >> 2;
+               break;
+       case MC_SEQ_RD_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D0_LP >> 2;
+               break;
+       case MC_SEQ_RD_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_RD_CTL_D1_LP >> 2;
+               break;
+       case MC_SEQ_WR_CTL_D0 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D0_LP >> 2;
+               break;
+       case MC_SEQ_WR_CTL_D1 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_D1_LP >> 2;
+               break;
+       case MC_PMG_CMD_EMRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
+               break;
+       case MC_PMG_CMD_MRS >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2;
+               break;
+       case MC_PMG_CMD_MRS1 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
+               break;
+       case MC_SEQ_PMG_TIMING >> 2:
+               *out_reg = MC_SEQ_PMG_TIMING_LP >> 2;
+               break;
+       case MC_PMG_CMD_MRS2 >> 2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS2_LP >> 2;
+               break;
+       case MC_SEQ_WR_CTL_2 >> 2:
+               *out_reg = MC_SEQ_WR_CTL_2_LP >> 2;
+               break;
+       default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static void ci_set_valid_flag(struct ci_mc_reg_table *table)
+{
+       u8 i, j;
+
+       for (i = 0; i < table->last; i++) {
+               for (j = 1; j < table->num_entries; j++) {
+                       if (table->mc_reg_table_entry[j-1].mc_data[i] !=
+                           table->mc_reg_table_entry[j].mc_data[i]) {
+                               table->valid_flag |= 1 << i;
+                               break;
+                       }
+               }
+       }
+}
+
+static void ci_set_s0_mc_reg_index(struct ci_mc_reg_table *table)
+{
+       u32 i;
+       u16 address;
+
+       for (i = 0; i < table->last; i++) {
+               table->mc_reg_address[i].s0 =
+                       ci_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
+                       address : table->mc_reg_address[i].s1;
+       }
+}
+
+static int ci_copy_vbios_mc_reg_table(const struct atom_mc_reg_table *table,
+                                     struct ci_mc_reg_table *ci_table)
+{
+       u8 i, j;
+
+       if (table->last > SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE)
+               return -EINVAL;
+       if (table->num_entries > MAX_AC_TIMING_ENTRIES)
+               return -EINVAL;
+
+       for (i = 0; i < table->last; i++)
+               ci_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+
+       ci_table->last = table->last;
+
+       for (i = 0; i < table->num_entries; i++) {
+               ci_table->mc_reg_table_entry[i].mclk_max =
+                       table->mc_reg_table_entry[i].mclk_max;
+               for (j = 0; j < table->last; j++)
+                       ci_table->mc_reg_table_entry[i].mc_data[j] =
+                               table->mc_reg_table_entry[i].mc_data[j];
+       }
+       ci_table->num_entries = table->num_entries;
+
+       return 0;
+}
+
+static int ci_initialize_mc_reg_table(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct atom_mc_reg_table *table;
+       struct ci_mc_reg_table *ci_table = &pi->mc_reg_table;
+       u8 module_index = rv770_get_memory_module_index(rdev);
+       int ret;
+
+       table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING));
+       WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING));
+       WREG32(MC_SEQ_DLL_STBY_LP, RREG32(MC_SEQ_DLL_STBY));
+       WREG32(MC_SEQ_G5PDX_CMD0_LP, RREG32(MC_SEQ_G5PDX_CMD0));
+       WREG32(MC_SEQ_G5PDX_CMD1_LP, RREG32(MC_SEQ_G5PDX_CMD1));
+       WREG32(MC_SEQ_G5PDX_CTRL_LP, RREG32(MC_SEQ_G5PDX_CTRL));
+       WREG32(MC_SEQ_PMG_DVS_CMD_LP, RREG32(MC_SEQ_PMG_DVS_CMD));
+       WREG32(MC_SEQ_PMG_DVS_CTL_LP, RREG32(MC_SEQ_PMG_DVS_CTL));
+       WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING));
+       WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2));
+       WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS));
+       WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1));
+       WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0));
+       WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1));
+       WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0));
+       WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1));
+       WREG32(MC_SEQ_PMG_TIMING_LP, RREG32(MC_SEQ_PMG_TIMING));
+       WREG32(MC_SEQ_PMG_CMD_MRS2_LP, RREG32(MC_PMG_CMD_MRS2));
+       WREG32(MC_SEQ_WR_CTL_2_LP, RREG32(MC_SEQ_WR_CTL_2));
+
+       ret = radeon_atom_init_mc_reg_table(rdev, module_index, table);
+       if (ret)
+               goto init_mc_done;
+
+        ret = ci_copy_vbios_mc_reg_table(table, ci_table);
+       if (ret)
+               goto init_mc_done;
+
+       ci_set_s0_mc_reg_index(ci_table);
+
+       ret = ci_set_mc_special_registers(rdev, ci_table);
+       if (ret)
+               goto init_mc_done;
+
+       ci_set_valid_flag(ci_table);
+
+init_mc_done:
+       kfree(table);
+
+       return ret;
+}
+
+static int ci_populate_mc_reg_addresses(struct radeon_device *rdev,
+                                       SMU7_Discrete_MCRegisters *mc_reg_table)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 i, j;
+
+       for (i = 0, j = 0; j < pi->mc_reg_table.last; j++) {
+               if (pi->mc_reg_table.valid_flag & (1 << j)) {
+                       if (i >= SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       mc_reg_table->address[i].s0 = cpu_to_be16(pi->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 = cpu_to_be16(pi->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+
+       mc_reg_table->last = (u8)i;
+
+       return 0;
+}
+
+static void ci_convert_mc_registers(const struct ci_mc_reg_entry *entry,
+                                   SMU7_Discrete_MCRegisterSet *data,
+                                   u32 num_entries, u32 valid_flag)
+{
+       u32 i, j;
+
+       for (i = 0, j = 0; j < num_entries; j++) {
+               if (valid_flag & (1 << j)) {
+                       data->value[i] = cpu_to_be32(entry->mc_data[j]);
+                       i++;
+               }
+       }
+}
+
+static void ci_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev,
+                                                const u32 memory_clock,
+                                                SMU7_Discrete_MCRegisterSet *mc_reg_table_data)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 i = 0;
+
+       for(i = 0; i < pi->mc_reg_table.num_entries; i++) {
+               if (memory_clock <= pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
+                       break;
+       }
+
+       if ((i == pi->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       ci_convert_mc_registers(&pi->mc_reg_table.mc_reg_table_entry[i],
+                               mc_reg_table_data, pi->mc_reg_table.last,
+                               pi->mc_reg_table.valid_flag);
+}
+
+static void ci_convert_mc_reg_table_to_smc(struct radeon_device *rdev,
+                                          SMU7_Discrete_MCRegisters *mc_reg_table)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 i;
+
+       for (i = 0; i < pi->dpm_table.mclk_table.count; i++)
+               ci_convert_mc_reg_table_entry_to_smc(rdev,
+                                                    pi->dpm_table.mclk_table.dpm_levels[i].value,
+                                                    &mc_reg_table->data[i]);
+}
+
+static int ci_populate_initial_mc_reg_table(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       int ret;
+
+       memset(&pi->smc_mc_reg_table, 0, sizeof(SMU7_Discrete_MCRegisters));
+
+       ret = ci_populate_mc_reg_addresses(rdev, &pi->smc_mc_reg_table);
+       if (ret)
+               return ret;
+       ci_convert_mc_reg_table_to_smc(rdev, &pi->smc_mc_reg_table);
+
+       return ci_copy_bytes_to_smc(rdev,
+                                   pi->mc_reg_table_start,
+                                   (u8 *)&pi->smc_mc_reg_table,
+                                   sizeof(SMU7_Discrete_MCRegisters),
+                                   pi->sram_end);
+}
+
+static int ci_update_and_upload_mc_reg_table(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (!(pi->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK))
+               return 0;
+
+       memset(&pi->smc_mc_reg_table, 0, sizeof(SMU7_Discrete_MCRegisters));
+
+       ci_convert_mc_reg_table_to_smc(rdev, &pi->smc_mc_reg_table);
+
+       return ci_copy_bytes_to_smc(rdev,
+                                   pi->mc_reg_table_start +
+                                   offsetof(SMU7_Discrete_MCRegisters, data[0]),
+                                   (u8 *)&pi->smc_mc_reg_table.data[0],
+                                   sizeof(SMU7_Discrete_MCRegisterSet) *
+                                   pi->dpm_table.mclk_table.count,
+                                   pi->sram_end);
+}
+
+static void ci_enable_voltage_control(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(GENERAL_PWRMGT);
+
+       tmp |= VOLT_PWRMGT_EN;
+       WREG32_SMC(GENERAL_PWRMGT, tmp);
+}
+
+static enum radeon_pcie_gen ci_get_maximum_link_speed(struct radeon_device *rdev,
+                                                     struct radeon_ps *radeon_state)
+{
+       struct ci_ps *state = ci_get_ps(radeon_state);
+       int i;
+       u16 pcie_speed, max_speed = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               pcie_speed = state->performance_levels[i].pcie_gen;
+               if (max_speed < pcie_speed)
+                       max_speed = pcie_speed;
+       }
+
+       return max_speed;
+}
+
+static u16 ci_get_current_pcie_speed(struct radeon_device *rdev)
+{
+       u32 speed_cntl = 0;
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE_MASK;
+       speed_cntl >>= LC_CURRENT_DATA_RATE_SHIFT;
+
+       return (u16)speed_cntl;
+}
+
+static int ci_get_current_pcie_lane_number(struct radeon_device *rdev)
+{
+       u32 link_width = 0;
+
+       link_width = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL) & LC_LINK_WIDTH_RD_MASK;
+       link_width >>= LC_LINK_WIDTH_RD_SHIFT;
+
+       switch (link_width) {
+       case RADEON_PCIE_LC_LINK_WIDTH_X1:
+               return 1;
+       case RADEON_PCIE_LC_LINK_WIDTH_X2:
+               return 2;
+       case RADEON_PCIE_LC_LINK_WIDTH_X4:
+               return 4;
+       case RADEON_PCIE_LC_LINK_WIDTH_X8:
+               return 8;
+       case RADEON_PCIE_LC_LINK_WIDTH_X12:
+               /* not actually supported */
+               return 12;
+       case RADEON_PCIE_LC_LINK_WIDTH_X0:
+       case RADEON_PCIE_LC_LINK_WIDTH_X16:
+       default:
+               return 16;
+       }
+}
+
+static void ci_request_link_speed_change_before_state_change(struct radeon_device *rdev,
+                                                            struct radeon_ps *radeon_new_state,
+                                                            struct radeon_ps *radeon_current_state)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       enum radeon_pcie_gen target_link_speed =
+               ci_get_maximum_link_speed(rdev, radeon_new_state);
+       enum radeon_pcie_gen current_link_speed;
+
+       if (pi->force_pcie_gen == RADEON_PCIE_GEN_INVALID)
+               current_link_speed = ci_get_maximum_link_speed(rdev, radeon_current_state);
+       else
+               current_link_speed = pi->force_pcie_gen;
+
+       pi->force_pcie_gen = RADEON_PCIE_GEN_INVALID;
+       pi->pspp_notify_required = false;
+       if (target_link_speed > current_link_speed) {
+               switch (target_link_speed) {
+               case RADEON_PCIE_GEN3:
+                       if (radeon_acpi_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN3, false) == 0)
+                               break;
+                       pi->force_pcie_gen = RADEON_PCIE_GEN2;
+                       if (current_link_speed == RADEON_PCIE_GEN2)
+                               break;
+               case RADEON_PCIE_GEN2:
+                       if (radeon_acpi_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, false) == 0)
+                               break;
+               default:
+                       pi->force_pcie_gen = ci_get_current_pcie_speed(rdev);
+                       break;
+               }
+       } else {
+               if (target_link_speed < current_link_speed)
+                       pi->pspp_notify_required = true;
+       }
+}
+
+static void ci_notify_link_speed_change_after_state_change(struct radeon_device *rdev,
+                                                          struct radeon_ps *radeon_new_state,
+                                                          struct radeon_ps *radeon_current_state)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       enum radeon_pcie_gen target_link_speed =
+               ci_get_maximum_link_speed(rdev, radeon_new_state);
+       u8 request;
+
+       if (pi->pspp_notify_required) {
+               if (target_link_speed == RADEON_PCIE_GEN3)
+                       request = PCIE_PERF_REQ_PECI_GEN3;
+               else if (target_link_speed == RADEON_PCIE_GEN2)
+                       request = PCIE_PERF_REQ_PECI_GEN2;
+               else
+                       request = PCIE_PERF_REQ_PECI_GEN1;
+
+               if ((request == PCIE_PERF_REQ_PECI_GEN1) &&
+                   (ci_get_current_pcie_speed(rdev) > 0))
+                       return;
+
+               radeon_acpi_pcie_performance_request(rdev, request, false);
+       }
+}
+
+static int ci_set_private_data_variables_based_on_pptable(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_clock_voltage_dependency_table *allowed_sclk_vddc_table =
+               &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
+       struct radeon_clock_voltage_dependency_table *allowed_mclk_vddc_table =
+               &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk;
+       struct radeon_clock_voltage_dependency_table *allowed_mclk_vddci_table =
+               &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk;
+
+       if (allowed_sclk_vddc_table == NULL)
+               return -EINVAL;
+       if (allowed_sclk_vddc_table->count < 1)
+               return -EINVAL;
+       if (allowed_mclk_vddc_table == NULL)
+               return -EINVAL;
+       if (allowed_mclk_vddc_table->count < 1)
+               return -EINVAL;
+       if (allowed_mclk_vddci_table == NULL)
+               return -EINVAL;
+       if (allowed_mclk_vddci_table->count < 1)
+               return -EINVAL;
+
+       pi->min_vddc_in_pp_table = allowed_sclk_vddc_table->entries[0].v;
+       pi->max_vddc_in_pp_table =
+               allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].v;
+
+       pi->min_vddci_in_pp_table = allowed_mclk_vddci_table->entries[0].v;
+       pi->max_vddci_in_pp_table =
+               allowed_mclk_vddci_table->entries[allowed_mclk_vddci_table->count - 1].v;
+
+       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk =
+               allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].clk;
+       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk =
+               allowed_mclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].clk;
+       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc =
+               allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].v;
+        rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci =
+               allowed_mclk_vddci_table->entries[allowed_mclk_vddci_table->count - 1].v;
+
+       return 0;
+}
+
+static void ci_patch_with_vddc_leakage(struct radeon_device *rdev, u16 *vddc)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_leakage_voltage *leakage_table = &pi->vddc_leakage;
+       u32 leakage_index;
+
+       for (leakage_index = 0; leakage_index < leakage_table->count; leakage_index++) {
+               if (leakage_table->leakage_id[leakage_index] == *vddc) {
+                       *vddc = leakage_table->actual_voltage[leakage_index];
+                       break;
+               }
+       }
+}
+
+static void ci_patch_with_vddci_leakage(struct radeon_device *rdev, u16 *vddci)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_leakage_voltage *leakage_table = &pi->vddci_leakage;
+       u32 leakage_index;
+
+       for (leakage_index = 0; leakage_index < leakage_table->count; leakage_index++) {
+               if (leakage_table->leakage_id[leakage_index] == *vddci) {
+                       *vddci = leakage_table->actual_voltage[leakage_index];
+                       break;
+               }
+       }
+}
+
+static void ci_patch_clock_voltage_dependency_table_with_vddc_leakage(struct radeon_device *rdev,
+                                                                     struct radeon_clock_voltage_dependency_table *table)
+{
+       u32 i;
+
+       if (table) {
+               for (i = 0; i < table->count; i++)
+                       ci_patch_with_vddc_leakage(rdev, &table->entries[i].v);
+       }
+}
+
+static void ci_patch_clock_voltage_dependency_table_with_vddci_leakage(struct radeon_device *rdev,
+                                                                      struct radeon_clock_voltage_dependency_table *table)
+{
+       u32 i;
+
+       if (table) {
+               for (i = 0; i < table->count; i++)
+                       ci_patch_with_vddci_leakage(rdev, &table->entries[i].v);
+       }
+}
+
+static void ci_patch_vce_clock_voltage_dependency_table_with_vddc_leakage(struct radeon_device *rdev,
+                                                                         struct radeon_vce_clock_voltage_dependency_table *table)
+{
+       u32 i;
+
+       if (table) {
+               for (i = 0; i < table->count; i++)
+                       ci_patch_with_vddc_leakage(rdev, &table->entries[i].v);
+       }
+}
+
+static void ci_patch_uvd_clock_voltage_dependency_table_with_vddc_leakage(struct radeon_device *rdev,
+                                                                         struct radeon_uvd_clock_voltage_dependency_table *table)
+{
+       u32 i;
+
+       if (table) {
+               for (i = 0; i < table->count; i++)
+                       ci_patch_with_vddc_leakage(rdev, &table->entries[i].v);
+       }
+}
+
+static void ci_patch_vddc_phase_shed_limit_table_with_vddc_leakage(struct radeon_device *rdev,
+                                                                  struct radeon_phase_shedding_limits_table *table)
+{
+       u32 i;
+
+       if (table) {
+               for (i = 0; i < table->count; i++)
+                       ci_patch_with_vddc_leakage(rdev, &table->entries[i].voltage);
+       }
+}
+
+static void ci_patch_clock_voltage_limits_with_vddc_leakage(struct radeon_device *rdev,
+                                                           struct radeon_clock_and_voltage_limits *table)
+{
+       if (table) {
+               ci_patch_with_vddc_leakage(rdev, (u16 *)&table->vddc);
+               ci_patch_with_vddci_leakage(rdev, (u16 *)&table->vddci);
+       }
+}
+
+static void ci_patch_cac_leakage_table_with_vddc_leakage(struct radeon_device *rdev,
+                                                        struct radeon_cac_leakage_table *table)
+{
+       u32 i;
+
+       if (table) {
+               for (i = 0; i < table->count; i++)
+                       ci_patch_with_vddc_leakage(rdev, &table->entries[i].vddc);
+       }
+}
+
+static void ci_patch_dependency_tables_with_leakage(struct radeon_device *rdev)
+{
+
+       ci_patch_clock_voltage_dependency_table_with_vddc_leakage(rdev,
+                                                                 &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk);
+       ci_patch_clock_voltage_dependency_table_with_vddc_leakage(rdev,
+                                                                 &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk);
+       ci_patch_clock_voltage_dependency_table_with_vddc_leakage(rdev,
+                                                                 &rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk);
+       ci_patch_clock_voltage_dependency_table_with_vddci_leakage(rdev,
+                                                                  &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk);
+       ci_patch_vce_clock_voltage_dependency_table_with_vddc_leakage(rdev,
+                                                                     &rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table);
+       ci_patch_uvd_clock_voltage_dependency_table_with_vddc_leakage(rdev,
+                                                                     &rdev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table);
+       ci_patch_clock_voltage_dependency_table_with_vddc_leakage(rdev,
+                                                                 &rdev->pm.dpm.dyn_state.samu_clock_voltage_dependency_table);
+       ci_patch_clock_voltage_dependency_table_with_vddc_leakage(rdev,
+                                                                 &rdev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table);
+       ci_patch_vddc_phase_shed_limit_table_with_vddc_leakage(rdev,
+                                                              &rdev->pm.dpm.dyn_state.phase_shedding_limits_table);
+       ci_patch_clock_voltage_limits_with_vddc_leakage(rdev,
+                                                       &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac);
+       ci_patch_clock_voltage_limits_with_vddc_leakage(rdev,
+                                                       &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc);
+       ci_patch_cac_leakage_table_with_vddc_leakage(rdev,
+                                                    &rdev->pm.dpm.dyn_state.cac_leakage_table);
+
+}
+
+static void ci_get_memory_type(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 tmp;
+
+       tmp = RREG32(MC_SEQ_MISC0);
+
+       if (((tmp & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT) ==
+           MC_SEQ_MISC0_GDDR5_VALUE)
+               pi->mem_gddr5 = true;
+       else
+               pi->mem_gddr5 = false;
+
+}
+
+void ci_update_current_ps(struct radeon_device *rdev,
+                         struct radeon_ps *rps)
+{
+       struct ci_ps *new_ps = ci_get_ps(rps);
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       pi->current_rps = *rps;
+       pi->current_ps = *new_ps;
+       pi->current_rps.ps_priv = &pi->current_ps;
+}
+
+void ci_update_requested_ps(struct radeon_device *rdev,
+                           struct radeon_ps *rps)
+{
+       struct ci_ps *new_ps = ci_get_ps(rps);
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       pi->requested_rps = *rps;
+       pi->requested_ps = *new_ps;
+       pi->requested_rps.ps_priv = &pi->requested_ps;
+}
+
+int ci_dpm_pre_set_power_state(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps;
+       struct radeon_ps *new_ps = &requested_ps;
+
+       ci_update_requested_ps(rdev, new_ps);
+
+       ci_apply_state_adjust_rules(rdev, &pi->requested_rps);
+
+       return 0;
+}
+
+void ci_dpm_post_set_power_state(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_ps *new_ps = &pi->requested_rps;
+
+       ci_update_current_ps(rdev, new_ps);
+}
+
+
+void ci_dpm_setup_asic(struct radeon_device *rdev)
+{
+       ci_read_clock_registers(rdev);
+       ci_get_memory_type(rdev);
+       ci_enable_acpi_power_management(rdev);
+       ci_init_sclk_t(rdev);
+}
+
+int ci_dpm_enable(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+       int ret;
+
+       if (ci_is_smc_running(rdev))
+               return -EINVAL;
+       if (pi->voltage_control != CISLANDS_VOLTAGE_CONTROL_NONE) {
+               ci_enable_voltage_control(rdev);
+               ret = ci_construct_voltage_tables(rdev);
+               if (ret) {
+                       DRM_ERROR("ci_construct_voltage_tables failed\n");
+                       return ret;
+               }
+       }
+       if (pi->caps_dynamic_ac_timing) {
+               ret = ci_initialize_mc_reg_table(rdev);
+               if (ret)
+                       pi->caps_dynamic_ac_timing = false;
+       }
+       if (pi->dynamic_ss)
+               ci_enable_spread_spectrum(rdev, true);
+       if (pi->thermal_protection)
+               ci_enable_thermal_protection(rdev, true);
+       ci_program_sstp(rdev);
+       ci_enable_display_gap(rdev);
+       ci_program_vc(rdev);
+       ret = ci_upload_firmware(rdev);
+       if (ret) {
+               DRM_ERROR("ci_upload_firmware failed\n");
+               return ret;
+       }
+       ret = ci_process_firmware_header(rdev);
+       if (ret) {
+               DRM_ERROR("ci_process_firmware_header failed\n");
+               return ret;
+       }
+       ret = ci_initial_switch_from_arb_f0_to_f1(rdev);
+       if (ret) {
+               DRM_ERROR("ci_initial_switch_from_arb_f0_to_f1 failed\n");
+               return ret;
+       }
+       ret = ci_init_smc_table(rdev);
+       if (ret) {
+               DRM_ERROR("ci_init_smc_table failed\n");
+               return ret;
+       }
+       ret = ci_init_arb_table_index(rdev);
+       if (ret) {
+               DRM_ERROR("ci_init_arb_table_index failed\n");
+               return ret;
+       }
+       if (pi->caps_dynamic_ac_timing) {
+               ret = ci_populate_initial_mc_reg_table(rdev);
+               if (ret) {
+                       DRM_ERROR("ci_populate_initial_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = ci_populate_pm_base(rdev);
+       if (ret) {
+               DRM_ERROR("ci_populate_pm_base failed\n");
+               return ret;
+       }
+       ci_dpm_start_smc(rdev);
+       ci_enable_vr_hot_gpio_interrupt(rdev);
+       ret = ci_notify_smc_display_change(rdev, false);
+       if (ret) {
+               DRM_ERROR("ci_notify_smc_display_change failed\n");
+               return ret;
+       }
+       ci_enable_sclk_control(rdev, true);
+       ret = ci_enable_ulv(rdev, true);
+       if (ret) {
+               DRM_ERROR("ci_enable_ulv failed\n");
+               return ret;
+       }
+       ret = ci_enable_ds_master_switch(rdev, true);
+       if (ret) {
+               DRM_ERROR("ci_enable_ds_master_switch failed\n");
+               return ret;
+       }
+       ret = ci_start_dpm(rdev);
+       if (ret) {
+               DRM_ERROR("ci_start_dpm failed\n");
+               return ret;
+       }
+       ret = ci_enable_didt(rdev, true);
+       if (ret) {
+               DRM_ERROR("ci_enable_didt failed\n");
+               return ret;
+       }
+       ret = ci_enable_smc_cac(rdev, true);
+       if (ret) {
+               DRM_ERROR("ci_enable_smc_cac failed\n");
+               return ret;
+       }
+       ret = ci_enable_power_containment(rdev, true);
+       if (ret) {
+               DRM_ERROR("ci_enable_power_containment failed\n");
+               return ret;
+       }
+       if (rdev->irq.installed &&
+           r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
+#if 0
+               PPSMC_Result result;
+#endif
+               ret = ci_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+               if (ret) {
+                       DRM_ERROR("ci_set_thermal_temperature_range failed\n");
+                       return ret;
+               }
+               rdev->irq.dpm_thermal = true;
+               radeon_irq_set(rdev);
+#if 0
+               result = ci_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
+
+               if (result != PPSMC_Result_OK)
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+#endif
+       }
+
+       ci_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+
+       ci_update_current_ps(rdev, boot_ps);
+
+       return 0;
+}
+
+void ci_dpm_disable(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
+
+       if (!ci_is_smc_running(rdev))
+               return;
+
+       if (pi->thermal_protection)
+               ci_enable_thermal_protection(rdev, false);
+       ci_enable_power_containment(rdev, false);
+       ci_enable_smc_cac(rdev, false);
+       ci_enable_didt(rdev, false);
+       ci_enable_spread_spectrum(rdev, false);
+       ci_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, false);
+       ci_stop_dpm(rdev);
+       ci_enable_ds_master_switch(rdev, true);
+       ci_enable_ulv(rdev, false);
+       ci_clear_vc(rdev);
+       ci_reset_to_default(rdev);
+       ci_dpm_stop_smc(rdev);
+       ci_force_switch_to_arb_f0(rdev);
+
+       ci_update_current_ps(rdev, boot_ps);
+}
+
+int ci_dpm_set_power_state(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct radeon_ps *new_ps = &pi->requested_rps;
+       struct radeon_ps *old_ps = &pi->current_rps;
+       int ret;
+
+       ci_find_dpm_states_clocks_in_dpm_table(rdev, new_ps);
+       if (pi->pcie_performance_request)
+               ci_request_link_speed_change_before_state_change(rdev, new_ps, old_ps);
+       ret = ci_freeze_sclk_mclk_dpm(rdev);
+       if (ret) {
+               DRM_ERROR("ci_freeze_sclk_mclk_dpm failed\n");
+               return ret;
+       }
+       ret = ci_populate_and_upload_sclk_mclk_dpm_levels(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("ci_populate_and_upload_sclk_mclk_dpm_levels failed\n");
+               return ret;
+       }
+       ret = ci_generate_dpm_level_enable_mask(rdev, new_ps);
+       if (ret) {
+               DRM_ERROR("ci_generate_dpm_level_enable_mask failed\n");
+               return ret;
+       }
+#if 0
+       ret = ci_update_vce_dpm(rdev, new_ps, old_ps);
+       if (ret) {
+               DRM_ERROR("ci_update_vce_dpm failed\n");
+               return ret;
+       }
+#endif
+       ret = ci_update_uvd_dpm(rdev, false);
+       if (ret) {
+               DRM_ERROR("ci_update_uvd_dpm failed\n");
+               return ret;
+       }
+       ret = ci_update_sclk_t(rdev);
+       if (ret) {
+               DRM_ERROR("ci_update_sclk_t failed\n");
+               return ret;
+       }
+       if (pi->caps_dynamic_ac_timing) {
+               ret = ci_update_and_upload_mc_reg_table(rdev);
+               if (ret) {
+                       DRM_ERROR("ci_update_and_upload_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = ci_program_memory_timing_parameters(rdev);
+       if (ret) {
+               DRM_ERROR("ci_program_memory_timing_parameters failed\n");
+               return ret;
+       }
+       ret = ci_unfreeze_sclk_mclk_dpm(rdev);
+       if (ret) {
+               DRM_ERROR("ci_unfreeze_sclk_mclk_dpm failed\n");
+               return ret;
+       }
+       ret = ci_upload_dpm_level_enable_mask(rdev);
+       if (ret) {
+               DRM_ERROR("ci_upload_dpm_level_enable_mask failed\n");
+               return ret;
+       }
+       if (pi->pcie_performance_request)
+               ci_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
+
+       return 0;
+}
+
+int ci_dpm_power_control_set_level(struct radeon_device *rdev)
+{
+       return ci_power_control_set_level(rdev);
+}
+
+void ci_dpm_reset_asic(struct radeon_device *rdev)
+{
+       ci_set_boot_state(rdev);
+}
+
+void ci_dpm_display_configuration_changed(struct radeon_device *rdev)
+{
+       ci_program_display_gap(rdev);
+}
+
+union power_info {
+       struct _ATOM_POWERPLAY_INFO info;
+       struct _ATOM_POWERPLAY_INFO_V2 info_2;
+       struct _ATOM_POWERPLAY_INFO_V3 info_3;
+       struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
+       struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
+       struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+
+union pplib_clock_info {
+       struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
+       struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
+       struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
+       struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+       struct _ATOM_PPLIB_SI_CLOCK_INFO si;
+       struct _ATOM_PPLIB_CI_CLOCK_INFO ci;
+};
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static void ci_parse_pplib_non_clock_info(struct radeon_device *rdev,
+                                         struct radeon_ps *rps,
+                                         struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
+                                         u8 table_rev)
+{
+       rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
+       rps->class = le16_to_cpu(non_clock_info->usClassification);
+       rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
+
+       if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
+               rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
+               rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
+       } else {
+               rps->vclk = 0;
+               rps->dclk = 0;
+       }
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               rdev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               rdev->pm.dpm.uvd_ps = rps;
+}
+
+static void ci_parse_pplib_clock_info(struct radeon_device *rdev,
+                                     struct radeon_ps *rps, int index,
+                                     union pplib_clock_info *clock_info)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_ps *ps = ci_get_ps(rps);
+       struct ci_pl *pl = &ps->performance_levels[index];
+
+       ps->performance_level_count = index + 1;
+
+       pl->sclk = le16_to_cpu(clock_info->ci.usEngineClockLow);
+       pl->sclk |= clock_info->ci.ucEngineClockHigh << 16;
+       pl->mclk = le16_to_cpu(clock_info->ci.usMemoryClockLow);
+       pl->mclk |= clock_info->ci.ucMemoryClockHigh << 16;
+
+       pl->pcie_gen = r600_get_pcie_gen_support(rdev,
+                                                pi->sys_pcie_mask,
+                                                pi->vbios_boot_state.pcie_gen_bootup_value,
+                                                clock_info->ci.ucPCIEGen);
+       pl->pcie_lane = r600_get_pcie_lane_support(rdev,
+                                                  pi->vbios_boot_state.pcie_lane_bootup_value,
+                                                  le16_to_cpu(clock_info->ci.usPCIELane));
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
+               pi->acpi_pcie_gen = pl->pcie_gen;
+       }
+
+       if (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) {
+               pi->ulv.supported = true;
+               pi->ulv.pl = *pl;
+               pi->ulv.cg_ulv_parameter = CISLANDS_CGULVPARAMETER_DFLT;
+       }
+
+       /* patch up boot state */
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
+               pl->mclk = pi->vbios_boot_state.mclk_bootup_value;
+               pl->sclk = pi->vbios_boot_state.sclk_bootup_value;
+               pl->pcie_gen = pi->vbios_boot_state.pcie_gen_bootup_value;
+               pl->pcie_lane = pi->vbios_boot_state.pcie_lane_bootup_value;
+       }
+
+       switch (rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) {
+       case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY:
+               pi->use_pcie_powersaving_levels = true;
+               if (pi->pcie_gen_powersaving.max < pl->pcie_gen)
+                       pi->pcie_gen_powersaving.max = pl->pcie_gen;
+               if (pi->pcie_gen_powersaving.min > pl->pcie_gen)
+                       pi->pcie_gen_powersaving.min = pl->pcie_gen;
+               if (pi->pcie_lane_powersaving.max < pl->pcie_lane)
+                       pi->pcie_lane_powersaving.max = pl->pcie_lane;
+               if (pi->pcie_lane_powersaving.min > pl->pcie_lane)
+                       pi->pcie_lane_powersaving.min = pl->pcie_lane;
+               break;
+       case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE:
+               pi->use_pcie_performance_levels = true;
+               if (pi->pcie_gen_performance.max < pl->pcie_gen)
+                       pi->pcie_gen_performance.max = pl->pcie_gen;
+               if (pi->pcie_gen_performance.min > pl->pcie_gen)
+                       pi->pcie_gen_performance.min = pl->pcie_gen;
+               if (pi->pcie_lane_performance.max < pl->pcie_lane)
+                       pi->pcie_lane_performance.max = pl->pcie_lane;
+               if (pi->pcie_lane_performance.min > pl->pcie_lane)
+                       pi->pcie_lane_performance.min = pl->pcie_lane;
+               break;
+       default:
+               break;
+       }
+}
+
+static int ci_parse_power_table(struct radeon_device *rdev)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
+       union pplib_power_state *power_state;
+       int i, j, k, non_clock_array_index, clock_array_index;
+       union pplib_clock_info *clock_info;
+       struct _StateArray *state_array;
+       struct _ClockInfoArray *clock_info_array;
+       struct _NonClockInfoArray *non_clock_info_array;
+       union power_info *power_info;
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+        u16 data_offset;
+       u8 frev, crev;
+       u8 *power_state_offset;
+       struct ci_ps *ps;
+
+       if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset))
+               return -EINVAL;
+       power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
+
+       state_array = (struct _StateArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usStateArrayOffset));
+       clock_info_array = (struct _ClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
+       non_clock_info_array = (struct _NonClockInfoArray *)
+               (mode_info->atom_context->bios + data_offset +
+                le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
+
+       rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
+                                 state_array->ucNumEntries, GFP_KERNEL);
+       if (!rdev->pm.dpm.ps)
+               return -ENOMEM;
+       power_state_offset = (u8 *)state_array->states;
+       rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
+       rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
+       rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
+       for (i = 0; i < state_array->ucNumEntries; i++) {
+               power_state = (union pplib_power_state *)power_state_offset;
+               non_clock_array_index = power_state->v2.nonClockInfoIndex;
+               non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
+                       &non_clock_info_array->nonClockInfo[non_clock_array_index];
+               if (!rdev->pm.power_state[i].clock_info)
+                       return -EINVAL;
+               ps = kzalloc(sizeof(struct ci_ps), GFP_KERNEL);
+               if (ps == NULL) {
+                       kfree(rdev->pm.dpm.ps);
+                       return -ENOMEM;
+               }
+               rdev->pm.dpm.ps[i].ps_priv = ps;
+               ci_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
+                                             non_clock_info,
+                                             non_clock_info_array->ucEntrySize);
+               k = 0;
+               for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+                       clock_array_index = power_state->v2.clockInfoIndex[j];
+                       if (clock_array_index >= clock_info_array->ucNumEntries)
+                               continue;
+                       if (k >= CISLANDS_MAX_HARDWARE_POWERLEVELS)
+                               break;
+                       clock_info = (union pplib_clock_info *)
+                               &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+                       ci_parse_pplib_clock_info(rdev,
+                                                 &rdev->pm.dpm.ps[i], k,
+                                                 clock_info);
+                       k++;
+               }
+               power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+       }
+       rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+       return 0;
+}
+
+int ci_get_vbios_boot_values(struct radeon_device *rdev,
+                            struct ci_vbios_boot_state *boot_state)
+{
+       struct radeon_mode_info *mode_info = &rdev->mode_info;
+       int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
+       ATOM_FIRMWARE_INFO_V2_2 *firmware_info;
+       u8 frev, crev;
+       u16 data_offset;
+
+       if (atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset)) {
+               firmware_info =
+                       (ATOM_FIRMWARE_INFO_V2_2 *)(mode_info->atom_context->bios +
+                                                   data_offset);
+               boot_state->mvdd_bootup_value = le16_to_cpu(firmware_info->usBootUpMVDDCVoltage);
+               boot_state->vddc_bootup_value = le16_to_cpu(firmware_info->usBootUpVDDCVoltage);
+               boot_state->vddci_bootup_value = le16_to_cpu(firmware_info->usBootUpVDDCIVoltage);
+               boot_state->pcie_gen_bootup_value = ci_get_current_pcie_speed(rdev);
+               boot_state->pcie_lane_bootup_value = ci_get_current_pcie_lane_number(rdev);
+               boot_state->sclk_bootup_value = le32_to_cpu(firmware_info->ulDefaultEngineClock);
+               boot_state->mclk_bootup_value = le32_to_cpu(firmware_info->ulDefaultMemoryClock);
+
+               return 0;
+       }
+       return -EINVAL;
+}
+
+void ci_dpm_fini(struct radeon_device *rdev)
+{
+       int i;
+
+       for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
+               kfree(rdev->pm.dpm.ps[i].ps_priv);
+       }
+       kfree(rdev->pm.dpm.ps);
+       kfree(rdev->pm.dpm.priv);
+       kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries);
+       r600_free_extended_power_table(rdev);
+}
+
+int ci_dpm_init(struct radeon_device *rdev)
+{
+       int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
+       u16 data_offset, size;
+       u8 frev, crev;
+       struct ci_power_info *pi;
+       int ret;
+       u32 mask;
+
+       pi = kzalloc(sizeof(struct ci_power_info), GFP_KERNEL);
+       if (pi == NULL)
+               return -ENOMEM;
+       rdev->pm.dpm.priv = pi;
+
+       ret = drm_pcie_get_speed_cap_mask(rdev->ddev, &mask);
+       if (ret)
+               pi->sys_pcie_mask = 0;
+       else
+               pi->sys_pcie_mask = mask;
+       pi->force_pcie_gen = RADEON_PCIE_GEN_INVALID;
+
+       pi->pcie_gen_performance.max = RADEON_PCIE_GEN1;
+       pi->pcie_gen_performance.min = RADEON_PCIE_GEN3;
+       pi->pcie_gen_powersaving.max = RADEON_PCIE_GEN1;
+       pi->pcie_gen_powersaving.min = RADEON_PCIE_GEN3;
+
+       pi->pcie_lane_performance.max = 0;
+       pi->pcie_lane_performance.min = 16;
+       pi->pcie_lane_powersaving.max = 0;
+       pi->pcie_lane_powersaving.min = 16;
+
+       ret = ci_get_vbios_boot_values(rdev, &pi->vbios_boot_state);
+       if (ret) {
+               ci_dpm_fini(rdev);
+               return ret;
+       }
+       ret = ci_parse_power_table(rdev);
+       if (ret) {
+               ci_dpm_fini(rdev);
+               return ret;
+       }
+       ret = r600_parse_extended_power_table(rdev);
+       if (ret) {
+               ci_dpm_fini(rdev);
+               return ret;
+       }
+
+        pi->dll_default_on = false;
+        pi->sram_end = SMC_RAM_END;
+
+       pi->activity_target[0] = CISLAND_TARGETACTIVITY_DFLT;
+       pi->activity_target[1] = CISLAND_TARGETACTIVITY_DFLT;
+       pi->activity_target[2] = CISLAND_TARGETACTIVITY_DFLT;
+       pi->activity_target[3] = CISLAND_TARGETACTIVITY_DFLT;
+       pi->activity_target[4] = CISLAND_TARGETACTIVITY_DFLT;
+       pi->activity_target[5] = CISLAND_TARGETACTIVITY_DFLT;
+       pi->activity_target[6] = CISLAND_TARGETACTIVITY_DFLT;
+       pi->activity_target[7] = CISLAND_TARGETACTIVITY_DFLT;
+
+       pi->mclk_activity_target = CISLAND_MCLK_TARGETACTIVITY_DFLT;
+
+       pi->sclk_dpm_key_disabled = 0;
+       pi->mclk_dpm_key_disabled = 0;
+       pi->pcie_dpm_key_disabled = 0;
+
+       pi->caps_sclk_ds = true;
+
+       pi->mclk_strobe_mode_threshold = 40000;
+       pi->mclk_stutter_mode_threshold = 40000;
+       pi->mclk_edc_enable_threshold = 40000;
+       pi->mclk_edc_wr_enable_threshold = 40000;
+
+       ci_initialize_powertune_defaults(rdev);
+
+       pi->caps_fps = false;
+
+       pi->caps_sclk_throttle_low_notification = false;
+
+        ci_get_leakage_voltages(rdev);
+        ci_patch_dependency_tables_with_leakage(rdev);
+        ci_set_private_data_variables_based_on_pptable(rdev);
+
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries =
+               kzalloc(4 * sizeof(struct radeon_clock_voltage_dependency_entry), GFP_KERNEL);
+       if (!rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) {
+               ci_dpm_fini(rdev);
+               return -ENOMEM;
+       }
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count = 4;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].clk = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].v = 0;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].clk = 36000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].v = 720;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].clk = 54000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].v = 810;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].clk = 72000;
+       rdev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].v = 900;
+
+       rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 4;
+       rdev->pm.dpm.dyn_state.sclk_mclk_delta = 15000;
+       rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
+
+       rdev->pm.dpm.dyn_state.valid_sclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_sclk_values.values = NULL;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
+       rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
+
+       pi->thermal_temp_setting.temperature_low = 99500;
+       pi->thermal_temp_setting.temperature_high = 100000;
+       pi->thermal_temp_setting.temperature_shutdown = 104000;
+
+       pi->uvd_enabled = false;
+
+       pi->voltage_control = CISLANDS_VOLTAGE_CONTROL_NONE;
+       pi->vddci_control = CISLANDS_VOLTAGE_CONTROL_NONE;
+       pi->mvdd_control = CISLANDS_VOLTAGE_CONTROL_NONE;
+       if (radeon_atom_is_voltage_gpio(rdev, VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_GPIO_LUT))
+               pi->voltage_control = CISLANDS_VOLTAGE_CONTROL_BY_GPIO;
+       else if (radeon_atom_is_voltage_gpio(rdev, VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2))
+               pi->voltage_control = CISLANDS_VOLTAGE_CONTROL_BY_SVID2;
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_VDDCI_CONTROL) {
+               if (radeon_atom_is_voltage_gpio(rdev, VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
+                       pi->vddci_control = CISLANDS_VOLTAGE_CONTROL_BY_GPIO;
+               else if (radeon_atom_is_voltage_gpio(rdev, VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_SVID2))
+                       pi->vddci_control = CISLANDS_VOLTAGE_CONTROL_BY_SVID2;
+               else
+                       rdev->pm.dpm.platform_caps &= ~ATOM_PP_PLATFORM_CAP_VDDCI_CONTROL;
+        }
+
+       if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_MVDDCONTROL) {
+               if (radeon_atom_is_voltage_gpio(rdev, VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT))
+                       pi->mvdd_control = CISLANDS_VOLTAGE_CONTROL_BY_GPIO;
+               else if (radeon_atom_is_voltage_gpio(rdev, VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2))
+                       pi->mvdd_control = CISLANDS_VOLTAGE_CONTROL_BY_SVID2;
+               else
+                       rdev->pm.dpm.platform_caps &= ~ATOM_PP_PLATFORM_CAP_MVDDCONTROL;
+       }
+
+       pi->vddc_phase_shed_control = true;
+
+#if defined(CONFIG_ACPI)
+       pi->pcie_performance_request =
+               radeon_acpi_is_pcie_performance_request_supported(rdev);
+#else
+       pi->pcie_performance_request = false;
+#endif
+
+       if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+                                   &frev, &crev, &data_offset)) {
+               pi->caps_sclk_ss_support = true;
+               pi->caps_mclk_ss_support = true;
+               pi->dynamic_ss = true;
+       } else {
+               pi->caps_sclk_ss_support = false;
+               pi->caps_mclk_ss_support = false;
+               pi->dynamic_ss = true;
+       }
+
+       if (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE)
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       pi->caps_dynamic_ac_timing = true;
+
+       return 0;
+}
+
+void ci_dpm_print_power_state(struct radeon_device *rdev,
+                             struct radeon_ps *rps)
+{
+       struct ci_ps *ps = ci_get_ps(rps);
+       struct ci_pl *pl;
+       int i;
+
+       r600_dpm_print_class_info(rps->class, rps->class2);
+       r600_dpm_print_cap_info(rps->caps);
+       printk("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       for (i = 0; i < ps->performance_level_count; i++) {
+               pl = &ps->performance_levels[i];
+               printk("\t\tpower level %d    sclk: %u mclk: %u pcie gen: %u pcie lanes: %u\n",
+                      i, pl->sclk, pl->mclk, pl->pcie_gen + 1, pl->pcie_lane);
+       }
+       r600_dpm_print_ps_status(rdev, rps);
+}
+
+u32 ci_dpm_get_sclk(struct radeon_device *rdev, bool low)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_ps *requested_state = ci_get_ps(&pi->requested_rps);
+
+       if (low)
+               return requested_state->performance_levels[0].sclk;
+       else
+               return requested_state->performance_levels[requested_state->performance_level_count - 1].sclk;
+}
+
+u32 ci_dpm_get_mclk(struct radeon_device *rdev, bool low)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       struct ci_ps *requested_state = ci_get_ps(&pi->requested_rps);
+
+       if (low)
+               return requested_state->performance_levels[0].mclk;
+       else
+               return requested_state->performance_levels[requested_state->performance_level_count - 1].mclk;
+}
diff --git a/drivers/gpu/drm/radeon/ci_dpm.h b/drivers/gpu/drm/radeon/ci_dpm.h
new file mode 100644 (file)
index 0000000..de504b5
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2013 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.
+ *
+ */
+#ifndef __CI_DPM_H__
+#define __CI_DPM_H__
+
+#include "ppsmc.h"
+
+#define SMU__NUM_SCLK_DPM_STATE  8
+#define SMU__NUM_MCLK_DPM_LEVELS 6
+#define SMU__NUM_LCLK_DPM_LEVELS 8
+#define SMU__NUM_PCIE_DPM_LEVELS 8
+#include "smu7_discrete.h"
+
+#define CISLANDS_MAX_HARDWARE_POWERLEVELS 2
+
+struct ci_pl {
+       u32 mclk;
+       u32 sclk;
+       enum radeon_pcie_gen pcie_gen;
+       u16 pcie_lane;
+};
+
+struct ci_ps {
+       u16 performance_level_count;
+       bool dc_compatible;
+       u32 sclk_t;
+       struct ci_pl performance_levels[CISLANDS_MAX_HARDWARE_POWERLEVELS];
+};
+
+struct ci_dpm_level {
+       bool enabled;
+       u32 value;
+       u32 param1;
+};
+
+#define CISLAND_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define MAX_REGULAR_DPM_NUMBER 8
+#define CISLAND_MINIMUM_ENGINE_CLOCK 800
+
+struct ci_single_dpm_table {
+       u32 count;
+       struct ci_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct ci_dpm_table {
+       struct ci_single_dpm_table sclk_table;
+       struct ci_single_dpm_table mclk_table;
+       struct ci_single_dpm_table pcie_speed_table;
+       struct ci_single_dpm_table vddc_table;
+       struct ci_single_dpm_table vddci_table;
+       struct ci_single_dpm_table mvdd_table;
+};
+
+struct ci_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct ci_mc_reg_table {
+       u8 last;
+       u8 num_entries;
+       u16 valid_flag;
+       struct ci_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMU7_Discrete_MCRegisterAddress mc_reg_address[SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct ci_ulv_parm
+{
+       bool supported;
+       u32 cg_ulv_parameter;
+       u32 volt_change_delay;
+       struct ci_pl pl;
+};
+
+#define CISLANDS_MAX_LEAKAGE_COUNT  8
+
+struct ci_leakage_voltage {
+       u16 count;
+       u16 leakage_id[CISLANDS_MAX_LEAKAGE_COUNT];
+       u16 actual_voltage[CISLANDS_MAX_LEAKAGE_COUNT];
+};
+
+struct ci_dpm_level_enable_mask {
+       u32 uvd_dpm_enable_mask;
+       u32 vce_dpm_enable_mask;
+       u32 acp_dpm_enable_mask;
+       u32 samu_dpm_enable_mask;
+       u32 sclk_dpm_enable_mask;
+       u32 mclk_dpm_enable_mask;
+       u32 pcie_dpm_enable_mask;
+};
+
+struct ci_vbios_boot_state
+{
+       u16 mvdd_bootup_value;
+       u16 vddc_bootup_value;
+       u16 vddci_bootup_value;
+       u32 sclk_bootup_value;
+       u32 mclk_bootup_value;
+       u16 pcie_gen_bootup_value;
+       u16 pcie_lane_bootup_value;
+};
+
+struct ci_clock_registers {
+       u32 cg_spll_func_cntl;
+       u32 cg_spll_func_cntl_2;
+       u32 cg_spll_func_cntl_3;
+       u32 cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2;
+       u32 dll_cntl;
+       u32 mclk_pwrmgt_cntl;
+       u32 mpll_ad_func_cntl;
+       u32 mpll_dq_func_cntl;
+       u32 mpll_func_cntl;
+       u32 mpll_func_cntl_1;
+       u32 mpll_func_cntl_2;
+       u32 mpll_ss1;
+       u32 mpll_ss2;
+};
+
+struct ci_thermal_temperature_setting {
+       s32 temperature_low;
+       s32 temperature_high;
+       s32 temperature_shutdown;
+};
+
+struct ci_pcie_perf_range {
+       u16 max;
+       u16 min;
+};
+
+enum ci_pt_config_reg_type {
+       CISLANDS_CONFIGREG_MMR = 0,
+       CISLANDS_CONFIGREG_SMC_IND,
+       CISLANDS_CONFIGREG_DIDT_IND,
+       CISLANDS_CONFIGREG_CACHE,
+       CISLANDS_CONFIGREG_MAX
+};
+
+#define POWERCONTAINMENT_FEATURE_BAPM            0x00000001
+#define POWERCONTAINMENT_FEATURE_TDCLimit        0x00000002
+#define POWERCONTAINMENT_FEATURE_PkgPwrLimit     0x00000004
+
+struct ci_pt_config_reg {
+       u32 offset;
+       u32 mask;
+       u32 shift;
+       u32 value;
+       enum ci_pt_config_reg_type type;
+};
+
+struct ci_pt_defaults {
+       u8 svi_load_line_en;
+       u8 svi_load_line_vddc;
+       u8 tdc_vddc_throttle_release_limit_perc;
+       u8 tdc_mawt;
+       u8 tdc_waterfall_ctl;
+       u8 dte_ambient_temp_base;
+       u32 display_cac;
+       u32 bapm_temp_gradient;
+       u16 bapmti_r[SMU7_DTE_ITERATIONS * SMU7_DTE_SOURCES * SMU7_DTE_SINKS];
+       u16 bapmti_rc[SMU7_DTE_ITERATIONS * SMU7_DTE_SOURCES * SMU7_DTE_SINKS];
+};
+
+#define DPMTABLE_OD_UPDATE_SCLK     0x00000001
+#define DPMTABLE_OD_UPDATE_MCLK     0x00000002
+#define DPMTABLE_UPDATE_SCLK        0x00000004
+#define DPMTABLE_UPDATE_MCLK        0x00000008
+
+struct ci_power_info {
+       struct ci_dpm_table dpm_table;
+       u32 voltage_control;
+       u32 mvdd_control;
+       u32 vddci_control;
+       u32 active_auto_throttle_sources;
+       struct ci_clock_registers clock_registers;
+       u16 acpi_vddc;
+       u16 acpi_vddci;
+       enum radeon_pcie_gen force_pcie_gen;
+       enum radeon_pcie_gen acpi_pcie_gen;
+       struct ci_leakage_voltage vddc_leakage;
+       struct ci_leakage_voltage vddci_leakage;
+       u16 max_vddc_in_pp_table;
+       u16 min_vddc_in_pp_table;
+       u16 max_vddci_in_pp_table;
+       u16 min_vddci_in_pp_table;
+       u32 mclk_strobe_mode_threshold;
+       u32 mclk_stutter_mode_threshold;
+       u32 mclk_edc_enable_threshold;
+       u32 mclk_edc_wr_enable_threshold;
+       struct ci_vbios_boot_state vbios_boot_state;
+       /* smc offsets */
+       u32 sram_end;
+       u32 dpm_table_start;
+       u32 soft_regs_start;
+       u32 mc_reg_table_start;
+       u32 fan_table_start;
+       u32 arb_table_start;
+       /* smc tables */
+       SMU7_Discrete_DpmTable smc_state_table;
+       SMU7_Discrete_MCRegisters smc_mc_reg_table;
+       SMU7_Discrete_PmFuses smc_powertune_table;
+       /* other stuff */
+       struct ci_mc_reg_table mc_reg_table;
+       struct atom_voltage_table vddc_voltage_table;
+       struct atom_voltage_table vddci_voltage_table;
+       struct atom_voltage_table mvdd_voltage_table;
+       struct ci_ulv_parm ulv;
+       u32 power_containment_features;
+       const struct ci_pt_defaults *powertune_defaults;
+       u32 dte_tj_offset;
+       bool vddc_phase_shed_control;
+       struct ci_thermal_temperature_setting thermal_temp_setting;
+       struct ci_dpm_level_enable_mask dpm_level_enable_mask;
+       u32 need_update_smu7_dpm_table;
+       u32 sclk_dpm_key_disabled;
+       u32 mclk_dpm_key_disabled;
+       u32 pcie_dpm_key_disabled;
+       struct ci_pcie_perf_range pcie_gen_performance;
+       struct ci_pcie_perf_range pcie_lane_performance;
+       struct ci_pcie_perf_range pcie_gen_powersaving;
+       struct ci_pcie_perf_range pcie_lane_powersaving;
+       u32 activity_target[SMU7_MAX_LEVELS_GRAPHICS];
+       u32 mclk_activity_target;
+       u32 low_sclk_interrupt_t;
+       u32 last_mclk_dpm_enable_mask;
+       u32 sys_pcie_mask;
+       /* caps */
+       bool caps_power_containment;
+       bool caps_cac;
+       bool caps_sq_ramping;
+       bool caps_db_ramping;
+       bool caps_td_ramping;
+       bool caps_tcp_ramping;
+       bool caps_fps;
+       bool caps_sclk_ds;
+       bool caps_sclk_ss_support;
+       bool caps_mclk_ss_support;
+       bool caps_uvd_dpm;
+       bool caps_vce_dpm;
+       bool caps_samu_dpm;
+       bool caps_acp_dpm;
+       bool caps_automatic_dc_transition;
+       bool caps_sclk_throttle_low_notification;
+       bool caps_dynamic_ac_timing;
+       /* flags */
+       bool thermal_protection;
+       bool pcie_performance_request;
+       bool dynamic_ss;
+       bool dll_default_on;
+       bool cac_enabled;
+       bool uvd_enabled;
+       bool battery_state;
+       bool pspp_notify_required;
+       bool mem_gddr5;
+       bool enable_bapm_feature;
+       bool enable_tdc_limit_feature;
+       bool enable_pkg_pwr_tracking_feature;
+       bool use_pcie_performance_levels;
+       bool use_pcie_powersaving_levels;
+       /* driver states */
+       struct radeon_ps current_rps;
+       struct ci_ps current_ps;
+       struct radeon_ps requested_rps;
+       struct ci_ps requested_ps;
+};
+
+#define CISLANDS_VOLTAGE_CONTROL_NONE                   0x0
+#define CISLANDS_VOLTAGE_CONTROL_BY_GPIO                0x1
+#define CISLANDS_VOLTAGE_CONTROL_BY_SVID2               0x2
+
+#define CISLANDS_Q88_FORMAT_CONVERSION_UNIT             256
+
+#define CISLANDS_VRC_DFLT0                              0x3FFFC000
+#define CISLANDS_VRC_DFLT1                              0x000400
+#define CISLANDS_VRC_DFLT2                              0xC00080
+#define CISLANDS_VRC_DFLT3                              0xC00200
+#define CISLANDS_VRC_DFLT4                              0xC01680
+#define CISLANDS_VRC_DFLT5                              0xC00033
+#define CISLANDS_VRC_DFLT6                              0xC00033
+#define CISLANDS_VRC_DFLT7                              0x3FFFC000
+
+#define CISLANDS_CGULVPARAMETER_DFLT                    0x00040035
+#define CISLAND_TARGETACTIVITY_DFLT                     30
+#define CISLAND_MCLK_TARGETACTIVITY_DFLT                10
+
+#define PCIE_PERF_REQ_REMOVE_REGISTRY   0
+#define PCIE_PERF_REQ_FORCE_LOWPOWER    1
+#define PCIE_PERF_REQ_PECI_GEN1         2
+#define PCIE_PERF_REQ_PECI_GEN2         3
+#define PCIE_PERF_REQ_PECI_GEN3         4
+
+int ci_copy_bytes_to_smc(struct radeon_device *rdev,
+                        u32 smc_start_address,
+                        const u8 *src, u32 byte_count, u32 limit);
+void ci_start_smc(struct radeon_device *rdev);
+void ci_reset_smc(struct radeon_device *rdev);
+int ci_program_jump_on_start(struct radeon_device *rdev);
+void ci_stop_smc_clock(struct radeon_device *rdev);
+void ci_start_smc_clock(struct radeon_device *rdev);
+bool ci_is_smc_running(struct radeon_device *rdev);
+PPSMC_Result ci_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg);
+PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev);
+int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit);
+int ci_read_smc_sram_dword(struct radeon_device *rdev,
+                          u32 smc_address, u32 *value, u32 limit);
+int ci_write_smc_sram_dword(struct radeon_device *rdev,
+                           u32 smc_address, u32 value, u32 limit);
+
+#endif
diff --git a/drivers/gpu/drm/radeon/ci_smc.c b/drivers/gpu/drm/radeon/ci_smc.c
new file mode 100644 (file)
index 0000000..53b43dd
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2011 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: Alex Deucher
+ */
+
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "radeon.h"
+#include "cikd.h"
+#include "ppsmc.h"
+#include "radeon_ucode.h"
+
+static int ci_set_smc_sram_address(struct radeon_device *rdev,
+                                  u32 smc_address, u32 limit)
+{
+       if (smc_address & 3)
+               return -EINVAL;
+       if ((smc_address + 3) > limit)
+               return -EINVAL;
+
+       WREG32(SMC_IND_INDEX_0, smc_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+       return 0;
+}
+
+int ci_copy_bytes_to_smc(struct radeon_device *rdev,
+                        u32 smc_start_address,
+                        const u8 *src, u32 byte_count, u32 limit)
+{
+       u32 data, original_data;
+       u32 addr;
+       u32 extra_shift;
+       int ret;
+
+       if (smc_start_address & 3)
+               return -EINVAL;
+       if ((smc_start_address + byte_count) > limit)
+               return -EINVAL;
+
+       addr = smc_start_address;
+
+       while (byte_count >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               ret = ci_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               byte_count -= 4;
+               addr += 4;
+       }
+
+       /* RMW for the final bytes */
+       if (byte_count > 0) {
+               data = 0;
+
+               ret = ci_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               original_data = RREG32(SMC_IND_DATA_0);
+
+               extra_shift = 8 * (4 - byte_count);
+
+               while (byte_count > 0) {
+                       data = (data << 8) + *src++;
+                       byte_count--;
+               }
+
+               data <<= extra_shift;
+
+               data |= (original_data & ~((~0UL) << extra_shift));
+
+               ret = ci_set_smc_sram_address(rdev, addr, limit);
+               if (ret)
+                       return ret;
+
+               WREG32(SMC_IND_DATA_0, data);
+       }
+       return 0;
+}
+
+void ci_start_smc(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+
+       tmp &= ~RST_REG;
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+void ci_reset_smc(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+
+       tmp |= RST_REG;
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+int ci_program_jump_on_start(struct radeon_device *rdev)
+{
+       static u8 data[] = { 0xE0, 0x00, 0x80, 0x40 };
+
+       return ci_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
+}
+
+void ci_stop_smc_clock(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       tmp |= CK_DISABLE;
+
+       WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+void ci_start_smc_clock(struct radeon_device *rdev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       tmp &= ~CK_DISABLE;
+
+       WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+bool ci_is_smc_running(struct radeon_device *rdev)
+{
+       u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+       u32 pc_c = RREG32_SMC(SMC_PC_C);
+
+       if (!(clk & CK_DISABLE) && (0x20100 <= pc_c))
+               return true;
+
+       return false;
+}
+
+PPSMC_Result ci_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
+{
+       u32 tmp;
+       int i;
+
+       if (!ci_is_smc_running(rdev))
+               return PPSMC_Result_Failed;
+
+       WREG32(SMC_MESSAGE_0, msg);
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+               tmp = RREG32(SMC_RESP_0);
+               if (tmp != 0)
+                       break;
+               udelay(1);
+       }
+       tmp = RREG32(SMC_RESP_0);
+
+       return (PPSMC_Result)tmp;
+}
+
+PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev)
+{
+       u32 tmp;
+       int i;
+
+       if (!ci_is_smc_running(rdev))
+               return PPSMC_Result_OK;
+
+       for (i = 0; i < rdev->usec_timeout; i++) {
+                tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+                if ((tmp & CKEN) == 0)
+                       break;
+                udelay(1);
+        }
+
+       return PPSMC_Result_OK;
+}
+
+int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
+{
+       u32 ucode_start_address;
+       u32 ucode_size;
+       const u8 *src;
+       u32 data;
+
+       if (!rdev->smc_fw)
+               return -EINVAL;
+
+       switch (rdev->family) {
+       case CHIP_BONAIRE:
+               ucode_start_address = BONAIRE_SMC_UCODE_START;
+               ucode_size = BONAIRE_SMC_UCODE_SIZE;
+               break;
+       default:
+               DRM_ERROR("unknown asic in smc ucode loader\n");
+               BUG();
+       }
+
+       if (ucode_size & 3)
+               return -EINVAL;
+
+       src = (const u8 *)rdev->smc_fw->data;
+       WREG32(SMC_IND_INDEX_0, ucode_start_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
+       while (ucode_size >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               ucode_size -= 4;
+       }
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+       return 0;
+}
+
+int ci_read_smc_sram_dword(struct radeon_device *rdev,
+                          u32 smc_address, u32 *value, u32 limit)
+{
+       int ret;
+
+       ret = ci_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       *value = RREG32(SMC_IND_DATA_0);
+       return 0;
+}
+
+int ci_write_smc_sram_dword(struct radeon_device *rdev,
+                           u32 smc_address, u32 value, u32 limit)
+{
+       int ret;
+
+       ret = ci_set_smc_sram_address(rdev, smc_address, limit);
+       if (ret)
+               return ret;
+
+       WREG32(SMC_IND_DATA_0, value);
+       return 0;
+}
index 87e5aeed6e888b66c2edefd21b600f3e6e7b087f..736a416b51a7f4c2992810fef689ca99cbe9ae6f 100644 (file)
@@ -40,6 +40,7 @@ MODULE_FIRMWARE("radeon/BONAIRE_mec.bin");
 MODULE_FIRMWARE("radeon/BONAIRE_mc.bin");
 MODULE_FIRMWARE("radeon/BONAIRE_rlc.bin");
 MODULE_FIRMWARE("radeon/BONAIRE_sdma.bin");
+MODULE_FIRMWARE("radeon/BONAIRE_smc.bin");
 MODULE_FIRMWARE("radeon/KAVERI_pfp.bin");
 MODULE_FIRMWARE("radeon/KAVERI_me.bin");
 MODULE_FIRMWARE("radeon/KAVERI_ce.bin");
@@ -1545,7 +1546,7 @@ static int cik_init_microcode(struct radeon_device *rdev)
        const char *chip_name;
        size_t pfp_req_size, me_req_size, ce_req_size,
                mec_req_size, rlc_req_size, mc_req_size,
-               sdma_req_size;
+               sdma_req_size, smc_req_size;
        char fw_name[30];
        int err;
 
@@ -1561,6 +1562,7 @@ static int cik_init_microcode(struct radeon_device *rdev)
                rlc_req_size = BONAIRE_RLC_UCODE_SIZE * 4;
                mc_req_size = CIK_MC_UCODE_SIZE * 4;
                sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+               smc_req_size = ALIGN(BONAIRE_SMC_UCODE_SIZE, 4);
                break;
        case CHIP_KAVERI:
                chip_name = "KAVERI";
@@ -1652,7 +1654,7 @@ static int cik_init_microcode(struct radeon_device *rdev)
                err = -EINVAL;
        }
 
-       /* No MC ucode on APUs */
+       /* No SMC, MC ucode on APUs */
        if (!(rdev->flags & RADEON_IS_IGP)) {
                snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
                err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
@@ -1664,6 +1666,21 @@ static int cik_init_microcode(struct radeon_device *rdev)
                               rdev->mc_fw->size, fw_name);
                        err = -EINVAL;
                }
+
+               snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+               err = request_firmware(&rdev->smc_fw, fw_name, rdev->dev);
+               if (err) {
+                       printk(KERN_ERR
+                              "smc: error loading firmware \"%s\"\n",
+                              fw_name);
+                       release_firmware(rdev->smc_fw);
+                       rdev->smc_fw = NULL;
+               } else if (rdev->smc_fw->size != smc_req_size) {
+                       printk(KERN_ERR
+                              "cik_smc: Bogus length %zu in firmware \"%s\"\n",
+                              rdev->smc_fw->size, fw_name);
+                       err = -EINVAL;
+               }
        }
 
 out:
@@ -1682,6 +1699,8 @@ out:
                rdev->rlc_fw = NULL;
                release_firmware(rdev->mc_fw);
                rdev->mc_fw = NULL;
+               release_firmware(rdev->smc_fw);
+               rdev->smc_fw = NULL;
        }
        return err;
 }
@@ -6626,8 +6645,12 @@ int cik_irq_set(struct radeon_device *rdev)
        cp_m2p2 = RREG32(CP_ME2_PIPE2_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
        cp_m2p3 = RREG32(CP_ME2_PIPE3_INT_CNTL) & ~TIME_STAMP_INT_ENABLE;
 
-       thermal_int = RREG32_SMC(CG_THERMAL_INT_CTRL) &
-               ~(THERM_INTH_MASK | THERM_INTL_MASK);
+       if (rdev->flags & RADEON_IS_IGP)
+               thermal_int = RREG32_SMC(CG_THERMAL_INT_CTRL) &
+                       ~(THERM_INTH_MASK | THERM_INTL_MASK);
+       else
+               thermal_int = RREG32_SMC(CG_THERMAL_INT) &
+                       ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
 
        /* enable CP interrupts on all rings */
        if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) {
@@ -6788,7 +6811,10 @@ int cik_irq_set(struct radeon_device *rdev)
 
        if (rdev->irq.dpm_thermal) {
                DRM_DEBUG("dpm thermal\n");
-               thermal_int |= THERM_INTH_MASK | THERM_INTL_MASK;
+               if (rdev->flags & RADEON_IS_IGP)
+                       thermal_int |= THERM_INTH_MASK | THERM_INTL_MASK;
+               else
+                       thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
        }
 
        WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
@@ -6825,7 +6851,10 @@ int cik_irq_set(struct radeon_device *rdev)
        WREG32(DC_HPD5_INT_CONTROL, hpd5);
        WREG32(DC_HPD6_INT_CONTROL, hpd6);
 
-       WREG32_SMC(CG_THERMAL_INT_CTRL, thermal_int);
+       if (rdev->flags & RADEON_IS_IGP)
+               WREG32_SMC(CG_THERMAL_INT_CTRL, thermal_int);
+       else
+               WREG32_SMC(CG_THERMAL_INT, thermal_int);
 
        return 0;
 }
index 179ca3625ae4d75c16aa1c99e8e81f7bf4697176..861fb3ec161ccf4ca1610eb1fd7a4c64113121c5 100644 (file)
 #define DIDT_TCP_CTRL0                                    0x60
 
 /* SMC IND registers */
+#define DPM_TABLE_475                                     0x3F768
+#       define SamuBootLevel(x)                           ((x) << 0)
+#       define SamuBootLevel_MASK                         0x000000ff
+#       define SamuBootLevel_SHIFT                        0
+#       define AcpBootLevel(x)                            ((x) << 8)
+#       define AcpBootLevel_MASK                          0x0000ff00
+#       define AcpBootLevel_SHIFT                         8
+#       define VceBootLevel(x)                            ((x) << 16)
+#       define VceBootLevel_MASK                          0x00ff0000
+#       define VceBootLevel_SHIFT                         16
+#       define UvdBootLevel(x)                            ((x) << 24)
+#       define UvdBootLevel_MASK                          0xff000000
+#       define UvdBootLevel_SHIFT                         24
+
+#define FIRMWARE_FLAGS                                    0x3F800
+#       define INTERRUPTS_ENABLED                         (1 << 0)
+
 #define NB_DPM_CONFIG_1                                   0x3F9E8
 #       define Dpm0PgNbPsLo(x)                            ((x) << 0)
 #       define Dpm0PgNbPsLo_MASK                          0x000000ff
 #       define DpmXNbPsHi_MASK                            0xff000000
 #       define DpmXNbPsHi_SHIFT                           24
 
+#define        SMC_SYSCON_RESET_CNTL                           0x80000000
+#       define RST_REG                                  (1 << 0)
+#define        SMC_SYSCON_CLOCK_CNTL_0                         0x80000004
+#       define CK_DISABLE                               (1 << 0)
+#       define CKEN                                     (1 << 24)
+
+#define        SMC_SYSCON_MISC_CNTL                            0x80000010
+
 #define SMC_SYSCON_MSG_ARG_0                              0x80000068
 
+#define SMC_PC_C                                          0x80000370
+
+#define SMC_SCRATCH9                                      0x80000424
+
+#define RCU_UC_EVENTS                                     0xC0000004
+#       define BOOT_SEQ_DONE                              (1 << 7)
+
 #define GENERAL_PWRMGT                                    0xC0200000
 #       define GLOBAL_PWRMGT_EN                           (1 << 0)
+#       define STATIC_PM_EN                               (1 << 1)
+#       define THERMAL_PROTECTION_DIS                     (1 << 2)
+#       define THERMAL_PROTECTION_TYPE                    (1 << 3)
+#       define SW_SMIO_INDEX(x)                           ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                         (1 << 6)
+#       define SW_SMIO_INDEX_SHIFT                        6
+#       define VOLT_PWRMGT_EN                             (1 << 10)
 #       define GPU_COUNTER_CLK                            (1 << 15)
+#       define DYN_SPREAD_SPECTRUM_EN                     (1 << 23)
+
+#define CNB_PWRMGT_CNTL                                   0xC0200004
+#       define GNB_SLOW_MODE(x)                           ((x) << 0)
+#       define GNB_SLOW_MODE_MASK                         (3 << 0)
+#       define GNB_SLOW_MODE_SHIFT                        0
+#       define GNB_SLOW                                   (1 << 2)
+#       define FORCE_NB_PS1                               (1 << 3)
+#       define DPM_ENABLED                                (1 << 4)
 
 #define SCLK_PWRMGT_CNTL                                  0xC0200008
+#       define SCLK_PWRMGT_OFF                            (1 << 0)
 #       define RESET_BUSY_CNT                             (1 << 4)
 #       define RESET_SCLK_CNT                             (1 << 5)
 #       define DYNAMIC_PM_EN                              (1 << 21)
 
+#define CG_SSP                                            0xC0200044
+#       define SST(x)                                     ((x) << 0)
+#       define SST_MASK                                   (0xffff << 0)
+#       define SSTU(x)                                    ((x) << 16)
+#       define SSTU_MASK                                  (0xf << 16)
+
+#define CG_DISPLAY_GAP_CNTL                               0xC0200060
+#       define DISP_GAP(x)                                ((x) << 0)
+#       define DISP_GAP_MASK                              (3 << 0)
+#       define VBI_TIMER_COUNT(x)                         ((x) << 4)
+#       define VBI_TIMER_COUNT_MASK                       (0x3fff << 4)
+#       define VBI_TIMER_UNIT(x)                          ((x) << 20)
+#       define VBI_TIMER_UNIT_MASK                        (7 << 20)
+#       define DISP_GAP_MCHG(x)                           ((x) << 24)
+#       define DISP_GAP_MCHG_MASK                         (3 << 24)
+
+#define CG_ULV_PARAMETER                                  0xC0200158
+
 #define CG_FTV_0                                          0xC02001A8
+#define CG_FTV_1                                          0xC02001AC
+#define CG_FTV_2                                          0xC02001B0
+#define CG_FTV_3                                          0xC02001B4
+#define CG_FTV_4                                          0xC02001B8
+#define CG_FTV_5                                          0xC02001BC
+#define CG_FTV_6                                          0xC02001C0
+#define CG_FTV_7                                          0xC02001C4
+
+#define CG_DISPLAY_GAP_CNTL2                              0xC0200230
 
 #define LCAC_SX0_OVR_SEL                                  0xC0400D04
 #define LCAC_SX0_OVR_VAL                                  0xC0400D08
 
+#define LCAC_MC0_CNTL                                     0xC0400D30
 #define LCAC_MC0_OVR_SEL                                  0xC0400D34
 #define LCAC_MC0_OVR_VAL                                  0xC0400D38
-
+#define LCAC_MC1_CNTL                                     0xC0400D3C
 #define LCAC_MC1_OVR_SEL                                  0xC0400D40
 #define LCAC_MC1_OVR_VAL                                  0xC0400D44
 
 #define LCAC_MC3_OVR_SEL                                  0xC0400D58
 #define LCAC_MC3_OVR_VAL                                  0xC0400D5C
 
+#define LCAC_CPL_CNTL                                     0xC0400D80
 #define LCAC_CPL_OVR_SEL                                  0xC0400D84
 #define LCAC_CPL_OVR_VAL                                  0xC0400D88
 
+/* dGPU */
+#define        CG_THERMAL_CTRL                                 0xC0300004
+#define        DPM_EVENT_SRC(x)                        ((x) << 0)
+#define        DPM_EVENT_SRC_MASK                      (7 << 0)
+#define                DIG_THERM_DPM(x)                        ((x) << 14)
+#define                DIG_THERM_DPM_MASK                      0x003FC000
+#define                DIG_THERM_DPM_SHIFT                     14
+
+#define        CG_THERMAL_INT                                  0xC030000C
+#define                CI_DIG_THERM_INTH(x)                    ((x) << 8)
+#define                CI_DIG_THERM_INTH_MASK                  0x0000FF00
+#define                CI_DIG_THERM_INTH_SHIFT                 8
+#define                CI_DIG_THERM_INTL(x)                    ((x) << 16)
+#define                CI_DIG_THERM_INTL_MASK                  0x00FF0000
+#define                CI_DIG_THERM_INTL_SHIFT                 16
+#define        THERM_INT_MASK_HIGH                     (1 << 24)
+#define        THERM_INT_MASK_LOW                      (1 << 25)
+
 #define        CG_MULT_THERMAL_STATUS                          0xC0300014
 #define                ASIC_MAX_TEMP(x)                        ((x) << 0)
 #define                ASIC_MAX_TEMP_MASK                      0x000001ff
 #define                CTF_TEMP_MASK                           0x0003fe00
 #define                CTF_TEMP_SHIFT                          9
 
+#define        CG_SPLL_FUNC_CNTL                               0xC0500140
+#define                SPLL_RESET                              (1 << 0)
+#define                SPLL_PWRON                              (1 << 1)
+#define                SPLL_BYPASS_EN                          (1 << 3)
+#define                SPLL_REF_DIV(x)                         ((x) << 5)
+#define                SPLL_REF_DIV_MASK                       (0x3f << 5)
+#define                SPLL_PDIV_A(x)                          ((x) << 20)
+#define                SPLL_PDIV_A_MASK                        (0x7f << 20)
+#define                SPLL_PDIV_A_SHIFT                       20
+#define        CG_SPLL_FUNC_CNTL_2                             0xC0500144
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define        CG_SPLL_FUNC_CNTL_3                             0xC0500148
+#define                SPLL_FB_DIV(x)                          ((x) << 0)
+#define                SPLL_FB_DIV_MASK                        (0x3ffffff << 0)
+#define                SPLL_FB_DIV_SHIFT                       0
+#define                SPLL_DITHEN                             (1 << 28)
+#define        CG_SPLL_FUNC_CNTL_4                             0xC050014C
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0xC0500164
+#define                SSEN                                    (1 << 0)
+#define                CLK_S(x)                                ((x) << 4)
+#define                CLK_S_MASK                              (0xfff << 4)
+#define                CLK_S_SHIFT                             4
+#define        CG_SPLL_SPREAD_SPECTRUM_2                       0xC0500168
+#define                CLK_V(x)                                ((x) << 0)
+#define                CLK_V_MASK                              (0x3ffffff << 0)
+#define                CLK_V_SHIFT                             0
+
 #define        MPLL_BYPASSCLK_SEL                              0xC050019C
 #      define MPLL_CLKOUT_SEL(x)                       ((x) << 8)
 #      define MPLL_CLKOUT_SEL_MASK                     0xFF00
 #      define ZCLK_SEL(x)                              ((x) << 8)
 #      define ZCLK_SEL_MASK                            0xFF00
 
+/* KV/KB */
 #define        CG_THERMAL_INT_CTRL                             0xC2100028
 #define                DIG_THERM_INTH(x)                       ((x) << 0)
 #define                DIG_THERM_INTH_MASK                     0x000000FF
 #define                NOOFGROUPS_SHIFT                                12
 #define                NOOFGROUPS_MASK                                 0x00001000
 
+#define        MC_ARB_DRAM_TIMING                              0x2774
+#define        MC_ARB_DRAM_TIMING2                             0x2778
+
+#define MC_ARB_BURST_TIME                               0x2808
+#define                STATE0(x)                               ((x) << 0)
+#define                STATE0_MASK                             (0x1f << 0)
+#define                STATE0_SHIFT                            0
+#define                STATE1(x)                               ((x) << 5)
+#define                STATE1_MASK                             (0x1f << 5)
+#define                STATE1_SHIFT                            5
+#define                STATE2(x)                               ((x) << 10)
+#define                STATE2_MASK                             (0x1f << 10)
+#define                STATE2_SHIFT                            10
+#define                STATE3(x)                               ((x) << 15)
+#define                STATE3_MASK                             (0x1f << 15)
+#define                STATE3_SHIFT                            15
+
+#define MC_SEQ_RAS_TIMING                               0x28a0
+#define MC_SEQ_CAS_TIMING                               0x28a4
+#define MC_SEQ_MISC_TIMING                              0x28a8
+#define MC_SEQ_MISC_TIMING2                             0x28ac
+#define MC_SEQ_PMG_TIMING                               0x28b0
+#define MC_SEQ_RD_CTL_D0                                0x28b4
+#define MC_SEQ_RD_CTL_D1                                0x28b8
+#define MC_SEQ_WR_CTL_D0                                0x28bc
+#define MC_SEQ_WR_CTL_D1                                0x28c0
+
 #define MC_SEQ_SUP_CNTL                                0x28c8
 #define                RUN_MASK                                (1 << 0)
 #define MC_SEQ_SUP_PGM                                 0x28cc
+#define MC_PMG_AUTO_CMD                                0x28d0
 
 #define        MC_SEQ_TRAIN_WAKEUP_CNTL                        0x28e8
 #define                TRAIN_DONE_D0                           (1 << 30)
 #define MC_IO_PAD_CNTL_D0                              0x29d0
 #define                MEM_FALL_OUT_CMD                        (1 << 8)
 
+#define MC_SEQ_MISC0                                           0x2a00
+#define        MC_SEQ_MISC0_VEN_ID_SHIFT               8
+#define        MC_SEQ_MISC0_VEN_ID_MASK                0x00000f00
+#define        MC_SEQ_MISC0_VEN_ID_VALUE               3
+#define        MC_SEQ_MISC0_REV_ID_SHIFT               12
+#define        MC_SEQ_MISC0_REV_ID_MASK                0x0000f000
+#define        MC_SEQ_MISC0_REV_ID_VALUE               1
+#define        MC_SEQ_MISC0_GDDR5_SHIFT                28
+#define        MC_SEQ_MISC0_GDDR5_MASK                 0xf0000000
+#define        MC_SEQ_MISC0_GDDR5_VALUE                5
+#define MC_SEQ_MISC1                                    0x2a04
+#define MC_SEQ_RESERVE_M                                0x2a08
+#define MC_PMG_CMD_EMRS                                 0x2a0c
+
 #define MC_SEQ_IO_DEBUG_INDEX                          0x2a44
 #define MC_SEQ_IO_DEBUG_DATA                                   0x2a48
 
+#define MC_SEQ_MISC5                                    0x2a54
+#define MC_SEQ_MISC6                                    0x2a58
+
+#define MC_SEQ_MISC7                                    0x2a64
+
+#define MC_SEQ_RAS_TIMING_LP                            0x2a6c
+#define MC_SEQ_CAS_TIMING_LP                            0x2a70
+#define MC_SEQ_MISC_TIMING_LP                           0x2a74
+#define MC_SEQ_MISC_TIMING2_LP                          0x2a78
+#define MC_SEQ_WR_CTL_D0_LP                             0x2a7c
+#define MC_SEQ_WR_CTL_D1_LP                             0x2a80
+#define MC_SEQ_PMG_CMD_EMRS_LP                          0x2a84
+#define MC_SEQ_PMG_CMD_MRS_LP                           0x2a88
+
+#define MC_PMG_CMD_MRS                                  0x2aac
+
+#define MC_SEQ_RD_CTL_D0_LP                             0x2b1c
+#define MC_SEQ_RD_CTL_D1_LP                             0x2b20
+
+#define MC_PMG_CMD_MRS1                                 0x2b44
+#define MC_SEQ_PMG_CMD_MRS1_LP                          0x2b48
+#define MC_SEQ_PMG_TIMING_LP                            0x2b4c
+
+#define MC_SEQ_WR_CTL_2                                 0x2b54
+#define MC_SEQ_WR_CTL_2_LP                              0x2b58
+#define MC_PMG_CMD_MRS2                                 0x2b5c
+#define MC_SEQ_PMG_CMD_MRS2_LP                          0x2b60
+
+#define        MCLK_PWRMGT_CNTL                                0x2ba0
+#       define DLL_SPEED(x)                            ((x) << 0)
+#       define DLL_SPEED_MASK                          (0x1f << 0)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCK0_PDNB                              (1 << 8)
+#       define MRDCK1_PDNB                              (1 << 9)
+#       define MRDCK0_RESET                             (1 << 16)
+#       define MRDCK1_RESET                             (1 << 17)
+#       define DLL_READY_READ                           (1 << 24)
+#define        DLL_CNTL                                        0x2ba4
+#       define MRDCK0_BYPASS                            (1 << 24)
+#       define MRDCK1_BYPASS                            (1 << 25)
+
+#define        MPLL_FUNC_CNTL                                  0x2bb4
+#define                BWCTRL(x)                               ((x) << 20)
+#define                BWCTRL_MASK                             (0xff << 20)
+#define        MPLL_FUNC_CNTL_1                                0x2bb8
+#define                VCO_MODE(x)                             ((x) << 0)
+#define                VCO_MODE_MASK                           (3 << 0)
+#define                CLKFRAC(x)                              ((x) << 4)
+#define                CLKFRAC_MASK                            (0xfff << 4)
+#define                CLKF(x)                                 ((x) << 16)
+#define                CLKF_MASK                               (0xfff << 16)
+#define        MPLL_FUNC_CNTL_2                                0x2bbc
+#define        MPLL_AD_FUNC_CNTL                               0x2bc0
+#define                YCLK_POST_DIV(x)                        ((x) << 0)
+#define                YCLK_POST_DIV_MASK                      (7 << 0)
+#define        MPLL_DQ_FUNC_CNTL                               0x2bc4
+#define                YCLK_SEL(x)                             ((x) << 4)
+#define                YCLK_SEL_MASK                           (1 << 4)
+
+#define        MPLL_SS1                                        0x2bcc
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        MPLL_SS2                                        0x2bd0
+#define                CLKS(x)                                 ((x) << 0)
+#define                CLKS_MASK                               (0xfff << 0)
+
 #define        HDP_HOST_PATH_CNTL                              0x2C00
 #define        CLOCK_GATING_DIS                        (1 << 23)
 #define        HDP_NONSURFACE_BASE                             0x2C04
 
 #define ATC_MISC_CG                                    0x3350
 
+#define MC_SEQ_CNTL_3                                     0x3600
+#       define CAC_EN                                     (1 << 31)
+#define MC_SEQ_G5PDX_CTRL                                 0x3604
+#define MC_SEQ_G5PDX_CTRL_LP                              0x3608
+#define MC_SEQ_G5PDX_CMD0                                 0x360c
+#define MC_SEQ_G5PDX_CMD0_LP                              0x3610
+#define MC_SEQ_G5PDX_CMD1                                 0x3614
+#define MC_SEQ_G5PDX_CMD1_LP                              0x3618
+
+#define MC_SEQ_PMG_DVS_CTL                                0x3628
+#define MC_SEQ_PMG_DVS_CTL_LP                             0x362c
+#define MC_SEQ_PMG_DVS_CMD                                0x3630
+#define MC_SEQ_PMG_DVS_CMD_LP                             0x3634
+#define MC_SEQ_DLL_STBY                                   0x3638
+#define MC_SEQ_DLL_STBY_LP                                0x363c
+
 #define IH_RB_CNTL                                        0x3e00
 #       define IH_RB_ENABLE                               (1 << 0)
 #       define IH_RB_SIZE(x)                              ((x) << 1) /* log2 */
 #       define MC_WR_CLEAN_CNT(x)                         ((x) << 20)
 #       define MC_VMID(x)                                 ((x) << 25)
 
+#define        BIF_LNCNT_RESET                                 0x5220
+#       define RESET_LNCNT_EN                           (1 << 0)
+
 #define        CONFIG_MEMSIZE                                  0x5428
 
 #define INTERRUPT_CNTL                                    0x5468
 #       define DC_HPDx_RX_INT_TIMER(x)                    ((x) << 16)
 #       define DC_HPDx_EN                                 (1 << 28)
 
+#define DPG_PIPE_STUTTER_CONTROL                          0x6cd4
+#       define STUTTER_ENABLE                             (1 << 0)
+
 #define        GRBM_CNTL                                       0x8000
 #define                GRBM_READ_TIMEOUT(x)                            ((x) << 0)
 
index 6db6e320bc7999d5224dcb1a1215ba6039ae630e..4c1ee6df09a0e470d786b9abf3affe241b58cf91 100644 (file)
@@ -99,7 +99,7 @@ typedef uint8_t PPSMC_Result;
 #define PPSMC_MSG_ThrottleOVRDSCLKDS        ((uint8_t)0x96)
 #define PPSMC_MSG_CancelThrottleOVRDSCLKDS  ((uint8_t)0x97)
 
-/* KV/KB */
+/* CI/KV/KB */
 #define PPSMC_MSG_UVDDPM_SetEnabledMask       ((uint16_t) 0x12D)
 #define PPSMC_MSG_VCEDPM_SetEnabledMask       ((uint16_t) 0x12E)
 #define PPSMC_MSG_ACPDPM_SetEnabledMask       ((uint16_t) 0x12F)
@@ -108,6 +108,7 @@ typedef uint8_t PPSMC_Result;
 #define PPSMC_MSG_MCLKDPM_NoForcedLevel       ((uint16_t) 0x132)
 #define PPSMC_MSG_Voltage_Cntl_Disable        ((uint16_t) 0x135)
 #define PPSMC_MSG_PCIeDPM_Enable              ((uint16_t) 0x136)
+#define PPSMC_MSG_PCIeDPM_Disable             ((uint16_t) 0x13d)
 #define PPSMC_MSG_ACPPowerOFF                 ((uint16_t) 0x137)
 #define PPSMC_MSG_ACPPowerON                  ((uint16_t) 0x138)
 #define PPSMC_MSG_SAMPowerOFF                 ((uint16_t) 0x139)
@@ -116,8 +117,13 @@ typedef uint8_t PPSMC_Result;
 #define PPSMC_MSG_NBDPM_Enable                ((uint16_t) 0x140)
 #define PPSMC_MSG_NBDPM_Disable               ((uint16_t) 0x141)
 #define PPSMC_MSG_SCLKDPM_SetEnabledMask      ((uint16_t) 0x145)
+#define PPSMC_MSG_MCLKDPM_SetEnabledMask      ((uint16_t) 0x146)
+#define PPSMC_MSG_PCIeDPM_ForceLevel          ((uint16_t) 0x147)
+#define PPSMC_MSG_EnableVRHotGPIOInterrupt    ((uint16_t) 0x14a)
 #define PPSMC_MSG_DPM_Enable                  ((uint16_t) 0x14e)
 #define PPSMC_MSG_DPM_Disable                 ((uint16_t) 0x14f)
+#define PPSMC_MSG_MCLKDPM_Enable              ((uint16_t) 0x150)
+#define PPSMC_MSG_MCLKDPM_Disable             ((uint16_t) 0x151)
 #define PPSMC_MSG_UVDDPM_Enable               ((uint16_t) 0x154)
 #define PPSMC_MSG_UVDDPM_Disable              ((uint16_t) 0x155)
 #define PPSMC_MSG_SAMUDPM_Enable              ((uint16_t) 0x156)
@@ -126,9 +132,25 @@ typedef uint8_t PPSMC_Result;
 #define PPSMC_MSG_ACPDPM_Disable              ((uint16_t) 0x159)
 #define PPSMC_MSG_VCEDPM_Enable               ((uint16_t) 0x15a)
 #define PPSMC_MSG_VCEDPM_Disable              ((uint16_t) 0x15b)
+#define PPSMC_MSG_VddC_Request                ((uint16_t) 0x15f)
 #define PPSMC_MSG_SCLKDPM_GetEnabledMask      ((uint16_t) 0x162)
+#define PPSMC_MSG_PCIeDPM_SetEnabledMask      ((uint16_t) 0x167)
+#define PPSMC_MSG_TDCLimitEnable              ((uint16_t) 0x169)
+#define PPSMC_MSG_TDCLimitDisable             ((uint16_t) 0x16a)
+#define PPSMC_MSG_PkgPwrLimitEnable           ((uint16_t) 0x185)
+#define PPSMC_MSG_PkgPwrLimitDisable          ((uint16_t) 0x186)
+#define PPSMC_MSG_PkgPwrSetLimit              ((uint16_t) 0x187)
+#define PPSMC_MSG_OverDriveSetTargetTdp       ((uint16_t) 0x188)
 #define PPSMC_MSG_SCLKDPM_FreezeLevel         ((uint16_t) 0x189)
 #define PPSMC_MSG_SCLKDPM_UnfreezeLevel       ((uint16_t) 0x18A)
+#define PPSMC_MSG_MCLKDPM_FreezeLevel         ((uint16_t) 0x18B)
+#define PPSMC_MSG_MCLKDPM_UnfreezeLevel       ((uint16_t) 0x18C)
+#define PPSMC_MSG_MASTER_DeepSleep_ON         ((uint16_t) 0x18F)
+#define PPSMC_MSG_MASTER_DeepSleep_OFF        ((uint16_t) 0x190)
+#define PPSMC_MSG_Remove_DC_Clamp             ((uint16_t) 0x191)
+
+#define PPSMC_MSG_API_GetSclkFrequency        ((uint16_t) 0x200)
+#define PPSMC_MSG_API_GetMclkFrequency        ((uint16_t) 0x201)
 
 /* TN */
 #define PPSMC_MSG_DPM_Config                ((uint32_t) 0x102)
index b6bac497f001b64c5b2cd40d46a37d3ff73bd940..930650ec769c057e0fe5e9a00421bbf8b2445262 100644 (file)
@@ -246,6 +246,12 @@ int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
 int radeon_atom_get_leakage_vddc_based_on_leakage_idx(struct radeon_device *rdev,
                                                      u16 *voltage,
                                                      u16 leakage_idx);
+int radeon_atom_get_leakage_id_from_vbios(struct radeon_device *rdev,
+                                         u16 *leakage_id);
+int radeon_atom_get_leakage_vddc_based_on_leakage_params(struct radeon_device *rdev,
+                                                        u16 *vddc, u16 *vddci,
+                                                        u16 virtual_voltage_id,
+                                                        u16 vbios_voltage_id);
 int radeon_atom_round_to_true_voltage(struct radeon_device *rdev,
                                      u8 voltage_type,
                                      u16 nominal_voltage,
index 9c83ecfd0eb77b19a4072e7192a7eb6ffaca12fc..c633fa53def011182a2845433969856d3cd07f5a 100644 (file)
@@ -2454,6 +2454,20 @@ static struct radeon_asic ci_asic = {
                .set_uvd_clocks = &cik_set_uvd_clocks,
                .get_temperature = &ci_get_temp,
        },
+       .dpm = {
+               .init = &ci_dpm_init,
+               .setup_asic = &ci_dpm_setup_asic,
+               .enable = &ci_dpm_enable,
+               .disable = &ci_dpm_disable,
+               .pre_set_power_state = &ci_dpm_pre_set_power_state,
+               .set_power_state = &ci_dpm_set_power_state,
+               .post_set_power_state = &ci_dpm_post_set_power_state,
+               .display_configuration_changed = &ci_dpm_display_configuration_changed,
+               .fini = &ci_dpm_fini,
+               .get_sclk = &ci_dpm_get_sclk,
+               .get_mclk = &ci_dpm_get_mclk,
+               .print_power_state = &ci_dpm_print_power_state,
+       },
        .pflip = {
                .pre_page_flip = &evergreen_pre_page_flip,
                .page_flip = &evergreen_page_flip,
index 68a1a1fb371d2d7ecea268ea5468535f425d2654..350da1704964ce1889a5d7a5df490868f82beed6 100644 (file)
@@ -750,6 +750,20 @@ void cik_compute_ring_set_wptr(struct radeon_device *rdev,
 int ci_get_temp(struct radeon_device *rdev);
 int kv_get_temp(struct radeon_device *rdev);
 
+int ci_dpm_init(struct radeon_device *rdev);
+int ci_dpm_enable(struct radeon_device *rdev);
+void ci_dpm_disable(struct radeon_device *rdev);
+int ci_dpm_pre_set_power_state(struct radeon_device *rdev);
+int ci_dpm_set_power_state(struct radeon_device *rdev);
+void ci_dpm_post_set_power_state(struct radeon_device *rdev);
+void ci_dpm_setup_asic(struct radeon_device *rdev);
+void ci_dpm_display_configuration_changed(struct radeon_device *rdev);
+void ci_dpm_fini(struct radeon_device *rdev);
+u32 ci_dpm_get_sclk(struct radeon_device *rdev, bool low);
+u32 ci_dpm_get_mclk(struct radeon_device *rdev, bool low);
+void ci_dpm_print_power_state(struct radeon_device *rdev,
+                             struct radeon_ps *ps);
+
 int kv_dpm_init(struct radeon_device *rdev);
 int kv_dpm_enable(struct radeon_device *rdev);
 void kv_dpm_disable(struct radeon_device *rdev);
index 6247b5e2d074c39eedec5dbc9e8a00314c932439..7ba439e9f30f94ce77cda5936caf7769fa82fc32 100644 (file)
@@ -211,7 +211,7 @@ void radeon_atombios_i2c_init(struct radeon_device *rdev)
 }
 
 static struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
-                                                       u8 id)
+                                                u8 id)
 {
        struct atom_context *ctx = rdev->mode_info.atom_context;
        struct radeon_gpio_rec gpio;
index 37d3d343f687ddd188dfa5140e503dffd70ded77..66b04af1694956f0dbd3bc4958b9742a282da023 100644 (file)
@@ -1202,6 +1202,7 @@ int radeon_pm_init(struct radeon_device *rdev)
        case CHIP_VERDE:
        case CHIP_OLAND:
        case CHIP_HAINAN:
+       case CHIP_BONAIRE:
        case CHIP_KABINI:
        case CHIP_KAVERI:
                /* DPM requires the RLC, RV770+ dGPU requires SMC */
index fad27c051bbfe690ba7b7af87963ba45532834f7..33858364fe89badf7fd45e3f98382d0c6d5df732 100644 (file)
 #define HAINAN_SMC_UCODE_START       0x10000
 #define HAINAN_SMC_UCODE_SIZE        0xe67C
 
+#define BONAIRE_SMC_UCODE_START      0x20000
+#define BONAIRE_SMC_UCODE_SIZE       0x1FDEC
+
 #endif
index 0f8be48c2ef467330ba7178cac117ea4450d4905..96d96f5df9e7cc9253abfde04c998a0d99ffa4e5 100644 (file)
@@ -3663,7 +3663,7 @@ static void si_clear_vc(struct radeon_device *rdev)
        WREG32(CG_FTV, 0);
 }
 
-static u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock)
+u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock)
 {
        u8 mc_para_index;
 
@@ -3676,7 +3676,7 @@ static u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock)
        return mc_para_index;
 }
 
-static u8 si_get_mclk_frequency_ratio(u32 memory_clock, bool strobe_mode)
+u8 si_get_mclk_frequency_ratio(u32 memory_clock, bool strobe_mode)
 {
        u8 mc_para_index;
 
@@ -3758,9 +3758,9 @@ static bool si_validate_phase_shedding_tables(struct radeon_device *rdev,
        return true;
 }
 
-static void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
-                                                    u32 max_voltage_steps,
-                                                    struct atom_voltage_table *voltage_table)
+void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
+                                             u32 max_voltage_steps,
+                                             struct atom_voltage_table *voltage_table)
 {
        unsigned int i, diff;
 
diff --git a/drivers/gpu/drm/radeon/smu7_discrete.h b/drivers/gpu/drm/radeon/smu7_discrete.h
new file mode 100644 (file)
index 0000000..82f70c9
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * Copyright 2013 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.
+ *
+ */
+
+#ifndef SMU7_DISCRETE_H
+#define SMU7_DISCRETE_H
+
+#include "smu7.h"
+
+#pragma pack(push, 1)
+
+#define SMU7_DTE_ITERATIONS 5
+#define SMU7_DTE_SOURCES 3
+#define SMU7_DTE_SINKS 1
+#define SMU7_NUM_CPU_TES 0
+#define SMU7_NUM_GPU_TES 1
+#define SMU7_NUM_NON_TES 2
+
+struct SMU7_SoftRegisters
+{
+    uint32_t        RefClockFrequency;
+    uint32_t        PmTimerP;
+    uint32_t        FeatureEnables;
+    uint32_t        PreVBlankGap;
+    uint32_t        VBlankTimeout;
+    uint32_t        TrainTimeGap;
+
+    uint32_t        MvddSwitchTime;
+    uint32_t        LongestAcpiTrainTime;
+    uint32_t        AcpiDelay;
+    uint32_t        G5TrainTime;
+    uint32_t        DelayMpllPwron;
+    uint32_t        VoltageChangeTimeout;
+    uint32_t        HandshakeDisables;
+
+    uint8_t         DisplayPhy1Config;
+    uint8_t         DisplayPhy2Config;
+    uint8_t         DisplayPhy3Config;
+    uint8_t         DisplayPhy4Config;
+
+    uint8_t         DisplayPhy5Config;
+    uint8_t         DisplayPhy6Config;
+    uint8_t         DisplayPhy7Config;
+    uint8_t         DisplayPhy8Config;
+
+    uint32_t        AverageGraphicsA;
+    uint32_t        AverageMemoryA;
+    uint32_t        AverageGioA;
+
+    uint8_t         SClkDpmEnabledLevels;
+    uint8_t         MClkDpmEnabledLevels;
+    uint8_t         LClkDpmEnabledLevels;
+    uint8_t         PCIeDpmEnabledLevels;
+
+    uint8_t         UVDDpmEnabledLevels;
+    uint8_t         SAMUDpmEnabledLevels;
+    uint8_t         ACPDpmEnabledLevels;
+    uint8_t         VCEDpmEnabledLevels;
+
+    uint32_t        DRAM_LOG_ADDR_H;
+    uint32_t        DRAM_LOG_ADDR_L;
+    uint32_t        DRAM_LOG_PHY_ADDR_H;
+    uint32_t        DRAM_LOG_PHY_ADDR_L;
+    uint32_t        DRAM_LOG_BUFF_SIZE;
+    uint32_t        UlvEnterC;
+    uint32_t        UlvTime;
+    uint32_t        Reserved[3];
+
+};
+
+typedef struct SMU7_SoftRegisters SMU7_SoftRegisters;
+
+struct SMU7_Discrete_VoltageLevel
+{
+    uint16_t    Voltage;
+    uint16_t    StdVoltageHiSidd;
+    uint16_t    StdVoltageLoSidd;
+    uint8_t     Smio;
+    uint8_t     padding;
+};
+
+typedef struct SMU7_Discrete_VoltageLevel SMU7_Discrete_VoltageLevel;
+
+struct SMU7_Discrete_GraphicsLevel
+{
+    uint32_t    Flags;
+    uint32_t    MinVddc;
+    uint32_t    MinVddcPhases;
+
+    uint32_t    SclkFrequency;
+
+    uint8_t     padding1[2];
+    uint16_t    ActivityLevel;
+
+    uint32_t    CgSpllFuncCntl3;
+    uint32_t    CgSpllFuncCntl4;
+    uint32_t    SpllSpreadSpectrum;
+    uint32_t    SpllSpreadSpectrum2;
+    uint32_t    CcPwrDynRm;
+    uint32_t    CcPwrDynRm1;
+    uint8_t     SclkDid;
+    uint8_t     DisplayWatermark;
+    uint8_t     EnabledForActivity;
+    uint8_t     EnabledForThrottle;
+    uint8_t     UpH;
+    uint8_t     DownH;
+    uint8_t     VoltageDownH;
+    uint8_t     PowerThrottle;
+    uint8_t     DeepSleepDivId;
+    uint8_t     padding[3];
+};
+
+typedef struct SMU7_Discrete_GraphicsLevel SMU7_Discrete_GraphicsLevel;
+
+struct SMU7_Discrete_ACPILevel
+{
+    uint32_t    Flags;
+    uint32_t    MinVddc;
+    uint32_t    MinVddcPhases;
+    uint32_t    SclkFrequency;
+    uint8_t     SclkDid;
+    uint8_t     DisplayWatermark;
+    uint8_t     DeepSleepDivId;
+    uint8_t     padding;
+    uint32_t    CgSpllFuncCntl;
+    uint32_t    CgSpllFuncCntl2;
+    uint32_t    CgSpllFuncCntl3;
+    uint32_t    CgSpllFuncCntl4;
+    uint32_t    SpllSpreadSpectrum;
+    uint32_t    SpllSpreadSpectrum2;
+    uint32_t    CcPwrDynRm;
+    uint32_t    CcPwrDynRm1;
+};
+
+typedef struct SMU7_Discrete_ACPILevel SMU7_Discrete_ACPILevel;
+
+struct SMU7_Discrete_Ulv
+{
+    uint32_t    CcPwrDynRm;
+    uint32_t    CcPwrDynRm1;
+    uint16_t    VddcOffset;
+    uint8_t     VddcOffsetVid;
+    uint8_t     VddcPhase;
+    uint32_t    Reserved;
+};
+
+typedef struct SMU7_Discrete_Ulv SMU7_Discrete_Ulv;
+
+struct SMU7_Discrete_MemoryLevel
+{
+    uint32_t    MinVddc;
+    uint32_t    MinVddcPhases;
+    uint32_t    MinVddci;
+    uint32_t    MinMvdd;
+
+    uint32_t    MclkFrequency;
+
+    uint8_t     EdcReadEnable;
+    uint8_t     EdcWriteEnable;
+    uint8_t     RttEnable;
+    uint8_t     StutterEnable;
+
+    uint8_t     StrobeEnable;
+    uint8_t     StrobeRatio;
+    uint8_t     EnabledForThrottle;
+    uint8_t     EnabledForActivity;
+
+    uint8_t     UpH;
+    uint8_t     DownH;
+    uint8_t     VoltageDownH;
+    uint8_t     padding;
+
+    uint16_t    ActivityLevel;
+    uint8_t     DisplayWatermark;
+    uint8_t     padding1;
+
+    uint32_t    MpllFuncCntl;
+    uint32_t    MpllFuncCntl_1;
+    uint32_t    MpllFuncCntl_2;
+    uint32_t    MpllAdFuncCntl;
+    uint32_t    MpllDqFuncCntl;
+    uint32_t    MclkPwrmgtCntl;
+    uint32_t    DllCntl;
+    uint32_t    MpllSs1;
+    uint32_t    MpllSs2;
+};
+
+typedef struct SMU7_Discrete_MemoryLevel SMU7_Discrete_MemoryLevel;
+
+struct SMU7_Discrete_LinkLevel
+{
+    uint8_t     PcieGenSpeed;
+    uint8_t     PcieLaneCount;
+    uint8_t     EnabledForActivity;
+    uint8_t     Padding;
+    uint32_t    DownT;
+    uint32_t    UpT;
+    uint32_t    Reserved;
+};
+
+typedef struct SMU7_Discrete_LinkLevel SMU7_Discrete_LinkLevel;
+
+
+struct SMU7_Discrete_MCArbDramTimingTableEntry
+{
+    uint32_t McArbDramTiming;
+    uint32_t McArbDramTiming2;
+    uint8_t  McArbBurstTime;
+    uint8_t  padding[3];
+};
+
+typedef struct SMU7_Discrete_MCArbDramTimingTableEntry SMU7_Discrete_MCArbDramTimingTableEntry;
+
+struct SMU7_Discrete_MCArbDramTimingTable
+{
+    SMU7_Discrete_MCArbDramTimingTableEntry entries[SMU__NUM_SCLK_DPM_STATE][SMU__NUM_MCLK_DPM_LEVELS];
+};
+
+typedef struct SMU7_Discrete_MCArbDramTimingTable SMU7_Discrete_MCArbDramTimingTable;
+
+struct SMU7_Discrete_UvdLevel
+{
+    uint32_t VclkFrequency;
+    uint32_t DclkFrequency;
+    uint16_t MinVddc;
+    uint8_t  MinVddcPhases;
+    uint8_t  VclkDivider;
+    uint8_t  DclkDivider;
+    uint8_t  padding[3];
+};
+
+typedef struct SMU7_Discrete_UvdLevel SMU7_Discrete_UvdLevel;
+
+struct SMU7_Discrete_ExtClkLevel
+{
+    uint32_t Frequency;
+    uint16_t MinVoltage;
+    uint8_t  MinPhases;
+    uint8_t  Divider;
+};
+
+typedef struct SMU7_Discrete_ExtClkLevel SMU7_Discrete_ExtClkLevel;
+
+struct SMU7_Discrete_StateInfo
+{
+    uint32_t SclkFrequency;
+    uint32_t MclkFrequency;
+    uint32_t VclkFrequency;
+    uint32_t DclkFrequency;
+    uint32_t SamclkFrequency;
+    uint32_t AclkFrequency;
+    uint32_t EclkFrequency;
+    uint16_t MvddVoltage;
+    uint16_t padding16;
+    uint8_t  DisplayWatermark;
+    uint8_t  McArbIndex;
+    uint8_t  McRegIndex;
+    uint8_t  SeqIndex;
+    uint8_t  SclkDid;
+    int8_t   SclkIndex;
+    int8_t   MclkIndex;
+    uint8_t  PCIeGen;
+
+};
+
+typedef struct SMU7_Discrete_StateInfo SMU7_Discrete_StateInfo;
+
+
+struct SMU7_Discrete_DpmTable
+{
+    SMU7_PIDController                  GraphicsPIDController;
+    SMU7_PIDController                  MemoryPIDController;
+    SMU7_PIDController                  LinkPIDController;
+
+    uint32_t                            SystemFlags;
+
+
+    uint32_t                            SmioMaskVddcVid;
+    uint32_t                            SmioMaskVddcPhase;
+    uint32_t                            SmioMaskVddciVid;
+    uint32_t                            SmioMaskMvddVid;
+
+    uint32_t                            VddcLevelCount;
+    uint32_t                            VddciLevelCount;
+    uint32_t                            MvddLevelCount;
+
+    SMU7_Discrete_VoltageLevel          VddcLevel               [SMU7_MAX_LEVELS_VDDC];
+//    SMU7_Discrete_VoltageLevel          VddcStandardReference   [SMU7_MAX_LEVELS_VDDC];
+    SMU7_Discrete_VoltageLevel          VddciLevel              [SMU7_MAX_LEVELS_VDDCI];
+    SMU7_Discrete_VoltageLevel          MvddLevel               [SMU7_MAX_LEVELS_MVDD];
+
+    uint8_t                             GraphicsDpmLevelCount;
+    uint8_t                             MemoryDpmLevelCount;
+    uint8_t                             LinkLevelCount;
+    uint8_t                             UvdLevelCount;
+    uint8_t                             VceLevelCount;
+    uint8_t                             AcpLevelCount;
+    uint8_t                             SamuLevelCount;
+    uint8_t                             MasterDeepSleepControl;
+    uint32_t                            Reserved[5];
+//    uint32_t                            SamuDefaultLevel;
+
+    SMU7_Discrete_GraphicsLevel         GraphicsLevel           [SMU7_MAX_LEVELS_GRAPHICS];
+    SMU7_Discrete_MemoryLevel           MemoryACPILevel;
+    SMU7_Discrete_MemoryLevel           MemoryLevel             [SMU7_MAX_LEVELS_MEMORY];
+    SMU7_Discrete_LinkLevel             LinkLevel               [SMU7_MAX_LEVELS_LINK];
+    SMU7_Discrete_ACPILevel             ACPILevel;
+    SMU7_Discrete_UvdLevel              UvdLevel                [SMU7_MAX_LEVELS_UVD];
+    SMU7_Discrete_ExtClkLevel           VceLevel                [SMU7_MAX_LEVELS_VCE];
+    SMU7_Discrete_ExtClkLevel           AcpLevel                [SMU7_MAX_LEVELS_ACP];
+    SMU7_Discrete_ExtClkLevel           SamuLevel               [SMU7_MAX_LEVELS_SAMU];
+    SMU7_Discrete_Ulv                   Ulv;
+
+    uint32_t                            SclkStepSize;
+    uint32_t                            Smio                    [SMU7_MAX_ENTRIES_SMIO];
+
+    uint8_t                             UvdBootLevel;
+    uint8_t                             VceBootLevel;
+    uint8_t                             AcpBootLevel;
+    uint8_t                             SamuBootLevel;
+
+    uint8_t                             UVDInterval;
+    uint8_t                             VCEInterval;
+    uint8_t                             ACPInterval;
+    uint8_t                             SAMUInterval;
+
+    uint8_t                             GraphicsBootLevel;
+    uint8_t                             GraphicsVoltageChangeEnable;
+    uint8_t                             GraphicsThermThrottleEnable;
+    uint8_t                             GraphicsInterval;
+
+    uint8_t                             VoltageInterval;
+    uint8_t                             ThermalInterval;
+    uint16_t                            TemperatureLimitHigh;
+
+    uint16_t                            TemperatureLimitLow;
+    uint8_t                             MemoryBootLevel;
+    uint8_t                             MemoryVoltageChangeEnable;
+
+    uint8_t                             MemoryInterval;
+    uint8_t                             MemoryThermThrottleEnable;
+    uint16_t                            VddcVddciDelta;
+
+    uint16_t                            VoltageResponseTime;
+    uint16_t                            PhaseResponseTime;
+
+    uint8_t                             PCIeBootLinkLevel;
+    uint8_t                             PCIeGenInterval;
+    uint8_t                             DTEInterval;
+    uint8_t                             DTEMode;
+
+    uint8_t                             SVI2Enable;
+    uint8_t                             VRHotGpio;
+    uint8_t                             AcDcGpio;
+    uint8_t                             ThermGpio;
+
+    uint16_t                            PPM_PkgPwrLimit;
+    uint16_t                            PPM_TemperatureLimit;
+
+    uint16_t                            DefaultTdp;
+    uint16_t                            TargetTdp;
+
+    uint16_t                            FpsHighT;
+    uint16_t                            FpsLowT;
+
+    uint16_t                            BAPMTI_R  [SMU7_DTE_ITERATIONS][SMU7_DTE_SOURCES][SMU7_DTE_SINKS];
+    uint16_t                            BAPMTI_RC [SMU7_DTE_ITERATIONS][SMU7_DTE_SOURCES][SMU7_DTE_SINKS];
+
+    uint8_t                             DTEAmbientTempBase;
+    uint8_t                             DTETjOffset;
+    uint8_t                             GpuTjMax;
+    uint8_t                             GpuTjHyst;
+
+    uint16_t                            BootVddc;
+    uint16_t                            BootVddci;
+
+    uint16_t                            BootMVdd;
+    uint16_t                            padding;
+
+    uint32_t                            BAPM_TEMP_GRADIENT;
+
+    uint32_t                            LowSclkInterruptT;
+};
+
+typedef struct SMU7_Discrete_DpmTable SMU7_Discrete_DpmTable;
+
+#define SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE 16
+#define SMU7_DISCRETE_MC_REGISTER_ARRAY_SET_COUNT SMU7_MAX_LEVELS_MEMORY
+
+struct SMU7_Discrete_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMU7_Discrete_MCRegisterAddress SMU7_Discrete_MCRegisterAddress;
+
+struct SMU7_Discrete_MCRegisterSet
+{
+    uint32_t value[SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMU7_Discrete_MCRegisterSet SMU7_Discrete_MCRegisterSet;
+
+struct SMU7_Discrete_MCRegisters
+{
+    uint8_t                             last;
+    uint8_t                             reserved[3];
+    SMU7_Discrete_MCRegisterAddress     address[SMU7_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+    SMU7_Discrete_MCRegisterSet         data[SMU7_DISCRETE_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMU7_Discrete_MCRegisters SMU7_Discrete_MCRegisters;
+
+struct SMU7_Discrete_PmFuses {
+  // dw0-dw1
+  uint8_t BapmVddCVidHiSidd[8];
+
+  // dw2-dw3
+  uint8_t BapmVddCVidLoSidd[8];
+
+  // dw4-dw5
+  uint8_t VddCVid[8];
+
+  // dw6
+  uint8_t SviLoadLineEn;
+  uint8_t SviLoadLineVddC;
+  uint8_t SviLoadLineTrimVddC;
+  uint8_t SviLoadLineOffsetVddC;
+
+  // dw7
+  uint16_t TDC_VDDC_PkgLimit;
+  uint8_t TDC_VDDC_ThrottleReleaseLimitPerc;
+  uint8_t TDC_MAWt;
+
+  // dw8
+  uint8_t TdcWaterfallCtl;
+  uint8_t LPMLTemperatureMin;
+  uint8_t LPMLTemperatureMax;
+  uint8_t Reserved;
+
+  // dw9-dw10
+  uint8_t BapmVddCVidHiSidd2[8];
+
+  // dw11-dw12
+  uint32_t Reserved6[2];
+
+  // dw13-dw16
+  uint8_t GnbLPML[16];
+
+  // dw17
+  uint8_t GnbLPMLMaxVid;
+  uint8_t GnbLPMLMinVid;
+  uint8_t Reserved1[2];
+
+  // dw18
+  uint16_t BapmVddCBaseLeakageHiSidd;
+  uint16_t BapmVddCBaseLeakageLoSidd;
+};
+
+typedef struct SMU7_Discrete_PmFuses SMU7_Discrete_PmFuses;
+
+
+#pragma pack(pop)
+
+#endif
+