drm/radeon: Partial update to Linux 3.12
authorzrj <rimvydas.jasinskas@gmail.com>
Fri, 7 Aug 2015 05:31:18 +0000 (08:31 +0300)
committerFran├žois Tigeot <ftigeot@wolfpond.org>
Sun, 9 Aug 2015 07:08:07 +0000 (09:08 +0200)
  This brings new radeon features (mostly code refactoring):
    DMA functions seperated from asics code;
    UVD block separation;
    Improvements to DPMS (by default disabled);
    Better audio handling.

  Notes:
    drm/ttm layer is put into transitional state pending memory functions rewrite.
    Currently VRAM handling capabilities are limited,
      still enough for light gaming and watching videos.
    X acceleration on radeon SI cards requires updated xorg+mesa(glamour support).

113 files changed:
sys/dev/drm/drm_bufs.c
sys/dev/drm/include/drm/drmP.h
sys/dev/drm/include/drm/ttm/ttm_bo_api.h
sys/dev/drm/include/drm/ttm/ttm_bo_driver.h
sys/dev/drm/include/drm/ttm/ttm_execbuf_util.h
sys/dev/drm/include/linux/reservation.h [new file with mode: 0644]
sys/dev/drm/include/uapi_drm/radeon_drm.h
sys/dev/drm/radeon/Makefile
sys/dev/drm/radeon/atombios.h
sys/dev/drm/radeon/atombios_crtc.c
sys/dev/drm/radeon/atombios_dp.c
sys/dev/drm/radeon/atombios_encoders.c
sys/dev/drm/radeon/atombios_i2c.c
sys/dev/drm/radeon/btc_dpm.c
sys/dev/drm/radeon/btc_dpm.h
sys/dev/drm/radeon/cayman_blit_shaders.c
sys/dev/drm/radeon/ci_dpm.c [new file with mode: 0644]
sys/dev/drm/radeon/ci_dpm.h [new file with mode: 0644]
sys/dev/drm/radeon/ci_smc.c [new file with mode: 0644]
sys/dev/drm/radeon/cik.c
sys/dev/drm/radeon/cik_reg.h
sys/dev/drm/radeon/cik_sdma.c [new file with mode: 0644]
sys/dev/drm/radeon/cikd.h
sys/dev/drm/radeon/clearstate_cayman.h
sys/dev/drm/radeon/clearstate_ci.h [copied from sys/dev/drm/radeon/clearstate_cayman.h with 57% similarity]
sys/dev/drm/radeon/clearstate_evergreen.h
sys/dev/drm/radeon/cypress_dpm.c
sys/dev/drm/radeon/dce6_afmt.c [new file with mode: 0644]
sys/dev/drm/radeon/evergreen.c
sys/dev/drm/radeon/evergreen_blit_kms.c [deleted file]
sys/dev/drm/radeon/evergreen_blit_shaders.c
sys/dev/drm/radeon/evergreen_cs.c
sys/dev/drm/radeon/evergreen_dma.c [new file with mode: 0644]
sys/dev/drm/radeon/evergreen_hdmi.c
sys/dev/drm/radeon/evergreend.h
sys/dev/drm/radeon/kv_dpm.c [new file with mode: 0644]
sys/dev/drm/radeon/kv_dpm.h [new file with mode: 0644]
sys/dev/drm/radeon/kv_smc.c [new file with mode: 0644]
sys/dev/drm/radeon/ni.c
sys/dev/drm/radeon/ni_dma.c [new file with mode: 0644]
sys/dev/drm/radeon/ni_dpm.c
sys/dev/drm/radeon/ppsmc.h
sys/dev/drm/radeon/pptable.h [new file with mode: 0644]
sys/dev/drm/radeon/r100.c
sys/dev/drm/radeon/r300.c
sys/dev/drm/radeon/r600.c
sys/dev/drm/radeon/r600_audio.c
sys/dev/drm/radeon/r600_blit.c
sys/dev/drm/radeon/r600_blit_kms.c [deleted file]
sys/dev/drm/radeon/r600_blit_shaders.h
sys/dev/drm/radeon/r600_cp.c
sys/dev/drm/radeon/r600_cs.c
sys/dev/drm/radeon/r600_dma.c [new file with mode: 0644]
sys/dev/drm/radeon/r600_dpm.c
sys/dev/drm/radeon/r600_dpm.h
sys/dev/drm/radeon/r600_hdmi.c
sys/dev/drm/radeon/r600d.h
sys/dev/drm/radeon/radeon.h
sys/dev/drm/radeon/radeon_agp.c
sys/dev/drm/radeon/radeon_asic.c
sys/dev/drm/radeon/radeon_asic.h
sys/dev/drm/radeon/radeon_atombios.c
sys/dev/drm/radeon/radeon_blit_common.h [deleted file]
sys/dev/drm/radeon/radeon_combios.c
sys/dev/drm/radeon/radeon_connectors.c
sys/dev/drm/radeon/radeon_cp.c
sys/dev/drm/radeon/radeon_cs.c
sys/dev/drm/radeon/radeon_device.c
sys/dev/drm/radeon/radeon_display.c
sys/dev/drm/radeon/radeon_drv.c
sys/dev/drm/radeon/radeon_fence.c
sys/dev/drm/radeon/radeon_gem.c
sys/dev/drm/radeon/radeon_irq_kms.c
sys/dev/drm/radeon/radeon_kms.c
sys/dev/drm/radeon/radeon_kms.h
sys/dev/drm/radeon/radeon_mode.h
sys/dev/drm/radeon/radeon_object.c
sys/dev/drm/radeon/radeon_object.h
sys/dev/drm/radeon/radeon_pm.c
sys/dev/drm/radeon/radeon_prime.c
sys/dev/drm/radeon/radeon_ring.c
sys/dev/drm/radeon/radeon_test.c
sys/dev/drm/radeon/radeon_trace.h
sys/dev/drm/radeon/radeon_ucode.h
sys/dev/drm/radeon/radeon_uvd.c
sys/dev/drm/radeon/rs400.c
sys/dev/drm/radeon/rs780_dpm.c
sys/dev/drm/radeon/rv6xx_dpm.c
sys/dev/drm/radeon/rv770.c
sys/dev/drm/radeon/rv770_dma.c [new file with mode: 0644]
sys/dev/drm/radeon/rv770_dpm.c
sys/dev/drm/radeon/rv770d.h
sys/dev/drm/radeon/si.c
sys/dev/drm/radeon/si_dma.c [new file with mode: 0644]
sys/dev/drm/radeon/si_dpm.c
sys/dev/drm/radeon/sid.h
sys/dev/drm/radeon/smu7.h [new file with mode: 0644]
sys/dev/drm/radeon/smu7_discrete.h [new file with mode: 0644]
sys/dev/drm/radeon/smu7_fusion.h [new file with mode: 0644]
sys/dev/drm/radeon/sumo_dpm.c
sys/dev/drm/radeon/sumo_dpm.h
sys/dev/drm/radeon/trinity_dpm.c
sys/dev/drm/radeon/trinity_dpm.h
sys/dev/drm/radeon/trinity_smc.c
sys/dev/drm/radeon/uvd_v1_0.c [new file with mode: 0644]
sys/dev/drm/radeon/uvd_v2_2.c [new file with mode: 0644]
sys/dev/drm/radeon/uvd_v3_1.c [new file with mode: 0644]
sys/dev/drm/radeon/uvd_v4_2.c [new file with mode: 0644]
sys/dev/drm/ttm/ttm_bo.c
sys/dev/drm/ttm/ttm_bo_manager.c
sys/dev/drm/ttm/ttm_bo_util.c
sys/dev/drm/ttm/ttm_bo_vm.c
sys/dev/drm/ttm/ttm_execbuf_util.c

index d3ef94b..882194c 100644 (file)
@@ -172,7 +172,7 @@ int drm_addmap(struct drm_device * dev, resource_size_t offset,
        case _DRM_SHM:
                map->handle = kmalloc(map->size, M_DRM, M_WAITOK | M_NULLOK);
                DRM_DEBUG("%lu %d %p\n",
-                   map->size, drm_order(map->size), map->handle);
+                         map->size, order_base_2(map->size), map->handle);
                if (!map->handle) {
                        drm_free(map, M_DRM);
                        return ENOMEM;
@@ -460,7 +460,7 @@ static int drm_do_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *reque
        drm_buf_t **temp_buflist;
 
        count = request->count;
-       order = drm_order(request->size);
+       order = order_base_2(request->size);
        size = 1 << order;
 
        alignment  = (request->flags & _DRM_PAGE_ALIGN)
@@ -591,7 +591,7 @@ static int drm_do_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *reque
        drm_buf_t **temp_buflist;
 
        count = request->count;
-       order = drm_order(request->size);
+       order = order_base_2(request->size);
        size = 1 << order;
 
        DRM_DEBUG("count=%d, size=%d (%d), order=%d\n",
@@ -743,7 +743,7 @@ static int drm_do_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *reques
        drm_buf_t **temp_buflist;
 
        count = request->count;
-       order = drm_order(request->size);
+       order = order_base_2(request->size);
        size = 1 << order;
 
        alignment  = (request->flags & _DRM_PAGE_ALIGN)
@@ -854,7 +854,7 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
        if (request->count < 0 || request->count > 4096)
                return EINVAL;
        
-       order = drm_order(request->size);
+       order = order_base_2(request->size);
        if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
                return EINVAL;
 
@@ -888,7 +888,7 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
        if (request->count < 0 || request->count > 4096)
                return EINVAL;
 
-       order = drm_order(request->size);
+       order = order_base_2(request->size);
        if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
                return EINVAL;
 
@@ -922,7 +922,7 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
        if (request->count < 0 || request->count > 4096)
                return EINVAL;
 
-       order = drm_order(request->size);
+       order = order_base_2(request->size);
        if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
                return EINVAL;
 
@@ -1066,9 +1066,7 @@ int drm_markbufs(struct drm_device *dev, void *data,
 
        DRM_DEBUG("%d, %d, %d\n",
                  request->size, request->low_mark, request->high_mark);
-       
-
-       order = drm_order(request->size);       
+       order = order_base_2(request->size);
        if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ||
            request->low_mark < 0 || request->high_mark < 0) {
                return EINVAL;
@@ -1223,34 +1221,9 @@ int drm_mapbufs(struct drm_device *dev, void *data,
                        goto done;
                }
        }
-
- done:
+      done:
        request->count = dma->buf_count;
-
        DRM_DEBUG("%d buffers, retcode = %d\n", request->count, retcode);
 
        return retcode;
 }
-
-/**
- * Compute size order.  Returns the exponent of the smaller power of two which
- * is greater or equal to given number.
- *
- * \param size size.
- * \return order.
- *
- * \todo Can be made faster.
- */
-int drm_order(unsigned long size)
-{
-       int order;
-       unsigned long tmp;
-
-       for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++) ;
-
-       if (size & (size - 1))
-               ++order;
-
-       return order;
-}
-EXPORT_SYMBOL(drm_order);
index f9d85d7..0b8f118 100644 (file)
@@ -1386,7 +1386,6 @@ unsigned long drm_get_resource_start(struct drm_device *dev,
 unsigned long drm_get_resource_len(struct drm_device *dev,
                                   unsigned int resource);
 void   drm_rmmap(struct drm_device *dev, drm_local_map_t *map);
-int    drm_order(unsigned long size);
 int    drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request);
 int    drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request);
 extern int drm_addmap(struct drm_device *dev, resource_size_t offset,
index 0629356..f5c48f7 100644 (file)
@@ -37,6 +37,8 @@
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/reservation.h>
 
 struct ttm_bo_device;
 
@@ -233,7 +235,7 @@ struct ttm_buffer_object {
        struct list_head ddestroy;
        struct list_head swap;
        struct list_head io_reserve_lru;
-       uint32_t val_seq;
+       unsigned long val_seq;
        bool seq_valid;
 
        /**
index 37d2bc7..5731262 100644 (file)
@@ -38,6 +38,7 @@
 #include <drm/drm_global.h>
 #include <sys/tree.h>
 #include <linux/workqueue.h>
+#include <linux/reservation.h>
 
 /* XXX nasty hack, but does the job */
 #undef RB_ROOT
@@ -784,7 +785,7 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man);
  * @bo: A pointer to a struct ttm_buffer_object.
  * @interruptible: Sleep interruptible if waiting.
  * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY.
- * @use_sequence: If @bo is already reserved, Only sleep waiting for
+ * @use_ticket: If @bo is already reserved, Only sleep waiting for
  * it to become unreserved if @sequence < (@bo)->sequence.
  *
  * Locks a buffer object for validation. (Or prevents other processes from
@@ -825,7 +826,8 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man);
  */
 extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
                          bool interruptible,
-                         bool no_wait, bool use_sequence, uint32_t sequence);
+                         bool no_wait, bool use_ticket,
+                         struct ww_acquire_ctx *ticket);
 
 /**
  * ttm_bo_reserve_slowpath_nolru:
@@ -842,7 +844,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
  */
 extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo,
                                         bool interruptible,
-                                        uint32_t sequence);
+                                        struct ww_acquire_ctx *ticket);
 
 
 /**
@@ -856,7 +858,8 @@ extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo,
  * held by us, this function cannot deadlock any more.
  */
 extern int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo,
-                                  bool interruptible, uint32_t sequence);
+                                  bool interruptible,
+                                  struct ww_acquire_ctx *ticket);
 
 /**
  * ttm_bo_reserve_nolru:
@@ -882,8 +885,8 @@ extern int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo,
  */
 extern int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo,
                                 bool interruptible,
-                                bool no_wait, bool use_sequence,
-                                uint32_t sequence);
+                                bool no_wait, bool use_ticket,
+                                struct ww_acquire_ctx *ticket);
 
 /**
  * ttm_bo_unreserve
@@ -895,19 +898,34 @@ extern int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo,
 extern void ttm_bo_unreserve(struct ttm_buffer_object *bo);
 
 /**
- * ttm_bo_unreserve_locked
+ * ttm_bo_unreserve_ticket
+ * @bo: A pointer to a struct ttm_buffer_object.
+ * @ticket: ww_acquire_ctx used for reserving
  *
+ * Unreserve a previous reservation of @bo made with @ticket.
+ */
+extern void ttm_bo_unreserve_ticket(struct ttm_buffer_object *bo,
+                                   struct ww_acquire_ctx *ticket);
+
+/**
+ * ttm_bo_unreserve_locked
  * @bo: A pointer to a struct ttm_buffer_object.
+ * @ticket: ww_acquire_ctx used for reserving, or NULL
  *
- * Unreserve a previous reservation of @bo.
+ * Unreserve a previous reservation of @bo made with @ticket.
  * Needs to be called with struct ttm_bo_global::lru_lock held.
  */
-extern void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo);
+extern void ttm_bo_unreserve_ticket_locked(struct ttm_buffer_object *bo,
+                                          struct ww_acquire_ctx *ticket);
 
 /*
  * ttm_bo_util.c
  */
 
+int ttm_mem_io_reserve(struct ttm_bo_device *bdev,
+                      struct ttm_mem_reg *mem);
+void ttm_mem_io_free(struct ttm_bo_device *bdev,
+                    struct ttm_mem_reg *mem);
 /**
  * ttm_bo_move_ttm
  *
index 8b6dbf5..d84087a 100644 (file)
@@ -33,6 +33,7 @@
 #define _TTM_EXECBUF_UTIL_H_
 
 #include <drm/ttm/ttm_bo_api.h>
+#include <linux/reservation.h>
 
 /**
  * struct ttm_validate_buffer
@@ -57,17 +58,20 @@ struct ttm_validate_buffer {
 /**
  * function ttm_eu_backoff_reservation
  *
+ * @ticket:   ww_acquire_ctx from reserve call
  * @list:     thread private list of ttm_validate_buffer structs.
  *
  * Undoes all buffer validation reservations for bos pointed to by
  * the list entries.
  */
 
-extern void ttm_eu_backoff_reservation(struct list_head *list);
+extern void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
+                                      struct list_head *list);
 
 /**
  * function ttm_eu_reserve_buffers
  *
+ * @ticket:  [out] ww_acquire_ctx returned by call.
  * @list:    thread private list of ttm_validate_buffer structs.
  *
  * Tries to reserve bos pointed to by the list entries for validation.
@@ -90,11 +94,13 @@ extern void ttm_eu_backoff_reservation(struct list_head *list);
  * has failed.
  */
 
-extern int ttm_eu_reserve_buffers(struct list_head *list);
+extern int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
+                                 struct list_head *list);
 
 /**
  * function ttm_eu_fence_buffer_objects.
  *
+ * @ticket:      ww_acquire_ctx from reserve call
  * @list:        thread private list of ttm_validate_buffer structs.
  * @sync_obj:    The new sync object for the buffers.
  *
@@ -104,6 +110,7 @@ extern int ttm_eu_reserve_buffers(struct list_head *list);
  *
  */
 
-extern void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj);
+extern void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
+                                       struct list_head *list, void *sync_obj);
 
 #endif
diff --git a/sys/dev/drm/include/linux/reservation.h b/sys/dev/drm/include/linux/reservation.h
new file mode 100644 (file)
index 0000000..b5a1a55
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Header file for reservations for dma-buf and ttm
+ *
+ * Copyright(C) 2011 Linaro Limited. All rights reserved.
+ * Copyright (C) 2012-2013 Canonical Ltd
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * Authors:
+ * Rob Clark <rob.clark@linaro.org>
+ * Maarten Lankhorst <maarten.lankhorst@canonical.com>
+ * Thomas Hellstrom <thellstrom-at-vmware-dot-com>
+ *
+ * Based on bo.c which bears the following copyright notice,
+ * but is dual licensed:
+ *
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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 _LINUX_RESERVATION_H
+#define _LINUX_RESERVATION_H
+
+#include <linux/ww_mutex.h>
+
+extern struct ww_class reservation_ww_class;
+
+struct reservation_object {
+       struct ww_mutex lock;
+};
+
+#endif /* _LINUX_RESERVATION_H */
index ca82852..7120fb1 100644 (file)
@@ -979,6 +979,8 @@ struct drm_radeon_cs {
 #define RADEON_INFO_RING_WORKING       0x15
 /* SI tile mode array */
 #define RADEON_INFO_SI_TILE_MODE_ARRAY 0x16
+/* query if CP DMA is supported on the compute ring */
+#define RADEON_INFO_SI_CP_DMA_COMPUTE  0x17
 
 
 struct drm_radeon_info {
index 77acde2..96a7e5c 100644 (file)
@@ -61,13 +61,11 @@ SRCS        +=                                                              \
        r520.c                                                          \
        r600.c                                                          \
        r600_audio.c                                                    \
-       r600_blit_kms.c                                                 \
        r600_blit_shaders.c                                             \
        r600_cs.c                                                       \
        r600_hdmi.c                                                     \
        rv770.c                                                         \
        evergreen.c                                                     \
-       evergreen_blit_kms.c                                            \
        evergreen_blit_shaders.c                                        \
        evergreen_cs.c                                                  \
        evergreen_hdmi.c                                                \
@@ -75,7 +73,6 @@ SRCS  +=                                                              \
        ni.c                                                            \
        si.c                                                            \
        si_blit_shaders.c                                               \
-       radeon_uvd.c                                                    \
        cik.c                                                           \
        cik_blit_shaders.c                                              \
        r600_dpm.c                                                      \
@@ -93,8 +90,29 @@ SRCS +=                                                              \
        trinity_smc.c                                                   \
        ni_dpm.c                                                        \
        si_smc.c                                                        \
-       si_dpm.c
+       si_dpm.c                                                        \
+       kv_smc.c                                                        \
+       kv_dpm.c                                                        \
+       ci_smc.c                                                        \
+       ci_dpm.c                                                        \
+       dce6_afmt.c
+
+# async DMA block
+SRCS   +=                                                              \
+       r600_dma.c                                                      \
+       rv770_dma.c                                                     \
+       evergreen_dma.c                                                 \
+       ni_dma.c                                                        \
+       si_dma.c                                                        \
+       cik_sdma.c
 
+# UVD block
+SRCS   +=                                                              \
+       radeon_uvd.c                                                    \
+       uvd_v1_0.c                                                      \
+       uvd_v2_2.c                                                      \
+       uvd_v3_1.c                                                      \
+       uvd_v4_2.c
 
 # UMS driver
 #SRCS  +=                                                              \
index 9105d2c..a91fa5d 100644 (file)
@@ -7661,618 +7661,6 @@ typedef struct  _ATOM_POWERPLAY_INFO_V3
   ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
 }ATOM_POWERPLAY_INFO_V3;
 
-/* New PPlib */
-/**************************************************************************/
-typedef struct _ATOM_PPLIB_THERMALCONTROLLER
-
-{
-    UCHAR ucType;           // one of ATOM_PP_THERMALCONTROLLER_*
-    UCHAR ucI2cLine;        // as interpreted by DAL I2C
-    UCHAR ucI2cAddress;
-    UCHAR ucFanParameters;  // Fan Control Parameters.
-    UCHAR ucFanMinRPM;      // Fan Minimum RPM (hundreds) -- for display purposes only.
-    UCHAR ucFanMaxRPM;      // Fan Maximum RPM (hundreds) -- for display purposes only.
-    UCHAR ucReserved;       // ----
-    UCHAR ucFlags;          // to be defined
-} ATOM_PPLIB_THERMALCONTROLLER;
-
-#define ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f
-#define ATOM_PP_FANPARAMETERS_NOFAN                                 0x80    // No fan is connected to this controller.
-
-#define ATOM_PP_THERMALCONTROLLER_NONE      0
-#define ATOM_PP_THERMALCONTROLLER_LM63      1  // Not used by PPLib
-#define ATOM_PP_THERMALCONTROLLER_ADM1032   2  // Not used by PPLib
-#define ATOM_PP_THERMALCONTROLLER_ADM1030   3  // Not used by PPLib
-#define ATOM_PP_THERMALCONTROLLER_MUA6649   4  // Not used by PPLib
-#define ATOM_PP_THERMALCONTROLLER_LM64      5
-#define ATOM_PP_THERMALCONTROLLER_F75375    6  // Not used by PPLib
-#define ATOM_PP_THERMALCONTROLLER_RV6xx     7
-#define ATOM_PP_THERMALCONTROLLER_RV770     8
-#define ATOM_PP_THERMALCONTROLLER_ADT7473   9
-#define ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO     11
-#define ATOM_PP_THERMALCONTROLLER_EVERGREEN 12
-#define ATOM_PP_THERMALCONTROLLER_EMC2103   13  /* 0x0D */ // Only fan control will be implemented, do NOT show this in PPGen.
-#define ATOM_PP_THERMALCONTROLLER_SUMO      14  /* 0x0E */ // Sumo type, used internally
-#define ATOM_PP_THERMALCONTROLLER_NISLANDS  15
-#define ATOM_PP_THERMALCONTROLLER_SISLANDS  16
-#define ATOM_PP_THERMALCONTROLLER_LM96163   17
-#define ATOM_PP_THERMALCONTROLLER_CISLANDS  18
-
-// Thermal controller 'combo type' to use an external controller for Fan control and an internal controller for thermal.
-// We probably should reserve the bit 0x80 for this use.
-// To keep the number of these types low we should also use the same code for all ASICs (i.e. do not distinguish RV6xx and RV7xx Internal here).
-// The driver can pick the correct internal controller based on the ASIC.
-
-#define ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL   0x89    // ADT7473 Fan Control + Internal Thermal Controller
-#define ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL   0x8D    // EMC2103 Fan Control + Internal Thermal Controller
-
-typedef struct _ATOM_PPLIB_STATE
-{
-    UCHAR ucNonClockStateIndex;
-    UCHAR ucClockStateIndices[1]; // variable-sized
-} ATOM_PPLIB_STATE;
-
-
-typedef struct _ATOM_PPLIB_FANTABLE
-{
-    UCHAR   ucFanTableFormat;                // Change this if the table format changes or version changes so that the other fields are not the same.
-    UCHAR   ucTHyst;                         // Temperature hysteresis. Integer.
-    USHORT  usTMin;                          // The temperature, in 0.01 centigrades, below which we just run at a minimal PWM.
-    USHORT  usTMed;                          // The middle temperature where we change slopes.
-    USHORT  usTHigh;                         // The high point above TMed for adjusting the second slope.
-    USHORT  usPWMMin;                        // The minimum PWM value in percent (0.01% increments).
-    USHORT  usPWMMed;                        // The PWM value (in percent) at TMed.
-    USHORT  usPWMHigh;                       // The PWM value at THigh.
-} ATOM_PPLIB_FANTABLE;
-
-typedef struct _ATOM_PPLIB_FANTABLE2
-{
-    ATOM_PPLIB_FANTABLE basicTable;
-    USHORT  usTMax;                          // The max temperature
-} ATOM_PPLIB_FANTABLE2;
-
-typedef struct _ATOM_PPLIB_EXTENDEDHEADER
-{
-    USHORT  usSize;
-    ULONG   ulMaxEngineClock;   // For Overdrive.
-    ULONG   ulMaxMemoryClock;   // For Overdrive.
-    // Add extra system parameters here, always adjust size to include all fields.
-    USHORT  usVCETableOffset; //points to ATOM_PPLIB_VCE_Table
-    USHORT  usUVDTableOffset;   //points to ATOM_PPLIB_UVD_Table
-    USHORT  usSAMUTableOffset;  //points to ATOM_PPLIB_SAMU_Table
-    USHORT  usPPMTableOffset;   //points to ATOM_PPLIB_PPM_Table
-} ATOM_PPLIB_EXTENDEDHEADER;
-
-//// ATOM_PPLIB_POWERPLAYTABLE::ulPlatformCaps
-#define ATOM_PP_PLATFORM_CAP_BACKBIAS 1
-#define ATOM_PP_PLATFORM_CAP_POWERPLAY 2
-#define ATOM_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 4
-#define ATOM_PP_PLATFORM_CAP_ASPM_L0s 8
-#define ATOM_PP_PLATFORM_CAP_ASPM_L1 16
-#define ATOM_PP_PLATFORM_CAP_HARDWAREDC 32
-#define ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY 64
-#define ATOM_PP_PLATFORM_CAP_STEPVDDC 128
-#define ATOM_PP_PLATFORM_CAP_VOLTAGECONTROL 256
-#define ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL 512
-#define ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 1024
-#define ATOM_PP_PLATFORM_CAP_HTLINKCONTROL 2048
-#define ATOM_PP_PLATFORM_CAP_MVDDCONTROL 4096
-#define ATOM_PP_PLATFORM_CAP_GOTO_BOOT_ON_ALERT 0x2000              // Go to boot state on alerts, e.g. on an AC->DC transition.
-#define ATOM_PP_PLATFORM_CAP_DONT_WAIT_FOR_VBLANK_ON_ALERT 0x4000   // Do NOT wait for VBLANK during an alert (e.g. AC->DC transition).
-#define ATOM_PP_PLATFORM_CAP_VDDCI_CONTROL 0x8000                   // Does the driver control VDDCI independently from VDDC.
-#define ATOM_PP_PLATFORM_CAP_REGULATOR_HOT 0x00010000               // Enable the 'regulator hot' feature.
-#define ATOM_PP_PLATFORM_CAP_BACO          0x00020000               // Does the driver supports BACO state.
-#define ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE   0x00040000           // Does the driver supports new CAC voltage table.
-#define ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY   0x00080000     // Does the driver supports revert GPIO5 polarity.
-#define ATOM_PP_PLATFORM_CAP_OUTPUT_THERMAL2GPIO17   0x00100000     // Does the driver supports thermal2GPIO17.
-#define ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE   0x00200000   // Does the driver supports VR HOT GPIO Configurable.
-
-typedef struct _ATOM_PPLIB_POWERPLAYTABLE
-{
-      ATOM_COMMON_TABLE_HEADER sHeader;
-
-      UCHAR ucDataRevision;
-
-      UCHAR ucNumStates;
-      UCHAR ucStateEntrySize;
-      UCHAR ucClockInfoSize;
-      UCHAR ucNonClockSize;
-
-      // offset from start of this table to array of ucNumStates ATOM_PPLIB_STATE structures
-      USHORT usStateArrayOffset;
-
-      // offset from start of this table to array of ASIC-specific structures,
-      // currently ATOM_PPLIB_CLOCK_INFO.
-      USHORT usClockInfoArrayOffset;
-
-      // offset from start of this table to array of ATOM_PPLIB_NONCLOCK_INFO
-      USHORT usNonClockInfoArrayOffset;
-
-      USHORT usBackbiasTime;    // in microseconds
-      USHORT usVoltageTime;     // in microseconds
-      USHORT usTableSize;       //the size of this structure, or the extended structure
-
-      ULONG ulPlatformCaps;            // See ATOM_PPLIB_CAPS_*
-
-      ATOM_PPLIB_THERMALCONTROLLER    sThermalController;
-
-      USHORT usBootClockInfoOffset;
-      USHORT usBootNonClockInfoOffset;
-
-} ATOM_PPLIB_POWERPLAYTABLE;
-
-typedef struct _ATOM_PPLIB_POWERPLAYTABLE2
-{
-    ATOM_PPLIB_POWERPLAYTABLE basicTable;
-    UCHAR   ucNumCustomThermalPolicy;
-    USHORT  usCustomThermalPolicyArrayOffset;
-}ATOM_PPLIB_POWERPLAYTABLE2, *LPATOM_PPLIB_POWERPLAYTABLE2;
-
-typedef struct _ATOM_PPLIB_POWERPLAYTABLE3
-{
-    ATOM_PPLIB_POWERPLAYTABLE2 basicTable2;
-    USHORT                     usFormatID;                      // To be used ONLY by PPGen.
-    USHORT                     usFanTableOffset;
-    USHORT                     usExtendendedHeaderOffset;
-} ATOM_PPLIB_POWERPLAYTABLE3, *LPATOM_PPLIB_POWERPLAYTABLE3;
-
-typedef struct _ATOM_PPLIB_POWERPLAYTABLE4
-{
-    ATOM_PPLIB_POWERPLAYTABLE3 basicTable3;
-    ULONG                      ulGoldenPPID;                    // PPGen use only     
-    ULONG                      ulGoldenRevision;                // PPGen use only
-    USHORT                     usVddcDependencyOnSCLKOffset;
-    USHORT                     usVddciDependencyOnMCLKOffset;
-    USHORT                     usVddcDependencyOnMCLKOffset;
-    USHORT                     usMaxClockVoltageOnDCOffset;
-    USHORT                     usVddcPhaseShedLimitsTableOffset;    // Points to ATOM_PPLIB_PhaseSheddingLimits_Table
-    USHORT                     usMvddDependencyOnMCLKOffset;
-} ATOM_PPLIB_POWERPLAYTABLE4, *LPATOM_PPLIB_POWERPLAYTABLE4;
-
-typedef struct _ATOM_PPLIB_POWERPLAYTABLE5
-{
-    ATOM_PPLIB_POWERPLAYTABLE4 basicTable4;
-    ULONG                      ulTDPLimit;
-    ULONG                      ulNearTDPLimit;
-    ULONG                      ulSQRampingThreshold;
-    USHORT                     usCACLeakageTableOffset;         // Points to ATOM_PPLIB_CAC_Leakage_Table
-    ULONG                      ulCACLeakage;                    // The iLeakage for driver calculated CAC leakage table
-    USHORT                     usTDPODLimit;
-    USHORT                     usLoadLineSlope;                 // in milliOhms * 100
-} ATOM_PPLIB_POWERPLAYTABLE5, *LPATOM_PPLIB_POWERPLAYTABLE5;
-
-//// ATOM_PPLIB_NONCLOCK_INFO::usClassification
-#define ATOM_PPLIB_CLASSIFICATION_UI_MASK          0x0007
-#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT         0
-#define ATOM_PPLIB_CLASSIFICATION_UI_NONE          0
-#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY       1
-#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED      3
-#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE   5
-// 2, 4, 6, 7 are reserved
-
-#define ATOM_PPLIB_CLASSIFICATION_BOOT                   0x0008
-#define ATOM_PPLIB_CLASSIFICATION_THERMAL                0x0010
-#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE     0x0020
-#define ATOM_PPLIB_CLASSIFICATION_REST                   0x0040
-#define ATOM_PPLIB_CLASSIFICATION_FORCED                 0x0080
-#define ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE          0x0100
-#define ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE      0x0200
-#define ATOM_PPLIB_CLASSIFICATION_UVDSTATE               0x0400
-#define ATOM_PPLIB_CLASSIFICATION_3DLOW                  0x0800
-#define ATOM_PPLIB_CLASSIFICATION_ACPI                   0x1000
-#define ATOM_PPLIB_CLASSIFICATION_HD2STATE               0x2000
-#define ATOM_PPLIB_CLASSIFICATION_HDSTATE                0x4000
-#define ATOM_PPLIB_CLASSIFICATION_SDSTATE                0x8000
-
-//// ATOM_PPLIB_NONCLOCK_INFO::usClassification2
-#define ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2     0x0001
-#define ATOM_PPLIB_CLASSIFICATION2_ULV                      0x0002
-#define ATOM_PPLIB_CLASSIFICATION2_MVC                      0x0004   //Multi-View Codec (BD-3D)
-
-//// ATOM_PPLIB_NONCLOCK_INFO::ulCapsAndSettings
-#define ATOM_PPLIB_SINGLE_DISPLAY_ONLY           0x00000001
-#define ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK         0x00000002
-
-// 0 is 2.5Gb/s, 1 is 5Gb/s
-#define ATOM_PPLIB_PCIE_LINK_SPEED_MASK            0x00000004
-#define ATOM_PPLIB_PCIE_LINK_SPEED_SHIFT           2
-
-// lanes - 1: 1, 2, 4, 8, 12, 16 permitted by PCIE spec
-#define ATOM_PPLIB_PCIE_LINK_WIDTH_MASK            0x000000F8
-#define ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT           3
-
-// lookup into reduced refresh-rate table
-#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_MASK  0x00000F00
-#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_SHIFT 8
-
-#define ATOM_PPLIB_LIMITED_REFRESHRATE_UNLIMITED    0
-#define ATOM_PPLIB_LIMITED_REFRESHRATE_50HZ         1
-// 2-15 TBD as needed.
-
-#define ATOM_PPLIB_SOFTWARE_DISABLE_LOADBALANCING        0x00001000
-#define ATOM_PPLIB_SOFTWARE_ENABLE_SLEEP_FOR_TIMESTAMPS  0x00002000
-
-#define ATOM_PPLIB_DISALLOW_ON_DC                       0x00004000
-
-#define ATOM_PPLIB_ENABLE_VARIBRIGHT                     0x00008000
-
-//memory related flags
-#define ATOM_PPLIB_SWSTATE_MEMORY_DLL_OFF               0x000010000
-
-//M3 Arb    //2bits, current 3 sets of parameters in total
-#define ATOM_PPLIB_M3ARB_MASK                       0x00060000
-#define ATOM_PPLIB_M3ARB_SHIFT                      17
-
-#define ATOM_PPLIB_ENABLE_DRR                       0x00080000
-
-// remaining 16 bits are reserved
-typedef struct _ATOM_PPLIB_THERMAL_STATE
-{
-    UCHAR   ucMinTemperature;
-    UCHAR   ucMaxTemperature;
-    UCHAR   ucThermalAction;
-}ATOM_PPLIB_THERMAL_STATE, *LPATOM_PPLIB_THERMAL_STATE;
-
-// Contained in an array starting at the offset
-// in ATOM_PPLIB_POWERPLAYTABLE::usNonClockInfoArrayOffset.
-// referenced from ATOM_PPLIB_STATE_INFO::ucNonClockStateIndex
-#define ATOM_PPLIB_NONCLOCKINFO_VER1      12
-#define ATOM_PPLIB_NONCLOCKINFO_VER2      24
-typedef struct _ATOM_PPLIB_NONCLOCK_INFO
-{
-      USHORT usClassification;
-      UCHAR  ucMinTemperature;
-      UCHAR  ucMaxTemperature;
-      ULONG  ulCapsAndSettings;
-      UCHAR  ucRequiredPower;
-      USHORT usClassification2;
-      ULONG  ulVCLK;
-      ULONG  ulDCLK;
-      UCHAR  ucUnused[5];
-} ATOM_PPLIB_NONCLOCK_INFO;
-
-// Contained in an array starting at the offset
-// in ATOM_PPLIB_POWERPLAYTABLE::usClockInfoArrayOffset.
-// referenced from ATOM_PPLIB_STATE::ucClockStateIndices
-typedef struct _ATOM_PPLIB_R600_CLOCK_INFO
-{
-      USHORT usEngineClockLow;
-      UCHAR ucEngineClockHigh;
-
-      USHORT usMemoryClockLow;
-      UCHAR ucMemoryClockHigh;
-
-      USHORT usVDDC;
-      USHORT usUnused1;
-      USHORT usUnused2;
-
-      ULONG ulFlags; // ATOM_PPLIB_R600_FLAGS_*
-
-} ATOM_PPLIB_R600_CLOCK_INFO;
-
-// ulFlags in ATOM_PPLIB_R600_CLOCK_INFO
-#define ATOM_PPLIB_R600_FLAGS_PCIEGEN2          1
-#define ATOM_PPLIB_R600_FLAGS_UVDSAFE           2
-#define ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE    4
-#define ATOM_PPLIB_R600_FLAGS_MEMORY_ODT_OFF    8
-#define ATOM_PPLIB_R600_FLAGS_MEMORY_DLL_OFF   16
-#define ATOM_PPLIB_R600_FLAGS_LOWPOWER         32   // On the RV770 use 'low power' setting (sequencer S0).
-
-typedef struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO
-{
-      USHORT usEngineClockLow;
-      UCHAR  ucEngineClockHigh;
-
-      USHORT usMemoryClockLow;
-      UCHAR  ucMemoryClockHigh;
-
-      USHORT usVDDC;
-      USHORT usVDDCI;
-      USHORT usUnused;
-
-      ULONG ulFlags; // ATOM_PPLIB_R600_FLAGS_*
-
-} ATOM_PPLIB_EVERGREEN_CLOCK_INFO;
-
-typedef struct _ATOM_PPLIB_SI_CLOCK_INFO
-{
-      USHORT usEngineClockLow;
-      UCHAR  ucEngineClockHigh;
-
-      USHORT usMemoryClockLow;
-      UCHAR  ucMemoryClockHigh;
-
-      USHORT usVDDC;
-      USHORT usVDDCI;
-      UCHAR  ucPCIEGen;
-      UCHAR  ucUnused1;
-
-      ULONG ulFlags; // ATOM_PPLIB_SI_FLAGS_*, no flag is necessary for now
-
-} ATOM_PPLIB_SI_CLOCK_INFO;
-
-typedef struct _ATOM_PPLIB_CI_CLOCK_INFO
-{
-      USHORT usEngineClockLow;
-      UCHAR  ucEngineClockHigh;
-
-      USHORT usMemoryClockLow;
-      UCHAR  ucMemoryClockHigh;
-
-      UCHAR  ucPCIEGen;
-      USHORT usPCIELane;
-} ATOM_PPLIB_CI_CLOCK_INFO;
-
-typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
-
-{
-      USHORT usLowEngineClockLow;         // Low Engine clock in MHz (the same way as on the R600).
-      UCHAR  ucLowEngineClockHigh;
-      USHORT usHighEngineClockLow;        // High Engine clock in MHz.
-      UCHAR  ucHighEngineClockHigh;
-      USHORT usMemoryClockLow;            // For now one of the ATOM_PPLIB_RS780_SPMCLK_XXXX constants.
-      UCHAR  ucMemoryClockHigh;           // Currentyl unused.
-      UCHAR  ucPadding;                   // For proper alignment and size.
-      USHORT usVDDC;                      // For the 780, use: None, Low, High, Variable
-      UCHAR  ucMaxHTLinkWidth;            // From SBIOS - {2, 4, 8, 16}
-      UCHAR  ucMinHTLinkWidth;            // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requriement.
-      USHORT usHTLinkFreq;                // See definition ATOM_PPLIB_RS780_HTLINKFREQ_xxx or in MHz(>=200).
-      ULONG  ulFlags; 
-} ATOM_PPLIB_RS780_CLOCK_INFO;
-
-#define ATOM_PPLIB_RS780_VOLTAGE_NONE       0 
-#define ATOM_PPLIB_RS780_VOLTAGE_LOW        1 
-#define ATOM_PPLIB_RS780_VOLTAGE_HIGH       2 
-#define ATOM_PPLIB_RS780_VOLTAGE_VARIABLE   3 
-
-#define ATOM_PPLIB_RS780_SPMCLK_NONE        0   // We cannot change the side port memory clock, leave it as it is.
-#define ATOM_PPLIB_RS780_SPMCLK_LOW         1
-#define ATOM_PPLIB_RS780_SPMCLK_HIGH        2
-
-#define ATOM_PPLIB_RS780_HTLINKFREQ_NONE       0 
-#define ATOM_PPLIB_RS780_HTLINKFREQ_LOW        1 
-#define ATOM_PPLIB_RS780_HTLINKFREQ_HIGH       2 
-
-typedef struct _ATOM_PPLIB_SUMO_CLOCK_INFO{
-      USHORT usEngineClockLow;  //clockfrequency & 0xFFFF. The unit is in 10khz
-      UCHAR  ucEngineClockHigh; //clockfrequency >> 16. 
-      UCHAR  vddcIndex;         //2-bit vddc index;
-      USHORT tdpLimit;
-      //please initalize to 0
-      USHORT rsv1;
-      //please initialize to 0s
-      ULONG rsv2[2];
-}ATOM_PPLIB_SUMO_CLOCK_INFO;
-
-
-
-typedef struct _ATOM_PPLIB_STATE_V2
-{
-      //number of valid dpm levels in this state; Driver uses it to calculate the whole 
-      //size of the state: sizeof(ATOM_PPLIB_STATE_V2) + (ucNumDPMLevels - 1) * sizeof(UCHAR)
-      UCHAR ucNumDPMLevels;
-      
-      //a index to the array of nonClockInfos
-      UCHAR nonClockInfoIndex;
-      /**
-      * Driver will read the first ucNumDPMLevels in this array
-      */
-      UCHAR clockInfoIndex[1];
-} ATOM_PPLIB_STATE_V2;
-
-typedef struct _StateArray{
-    //how many states we have 
-    UCHAR ucNumEntries;
-    
-    ATOM_PPLIB_STATE_V2 states[1];
-}StateArray;
-
-
-typedef struct _ClockInfoArray{
-    //how many clock levels we have
-    UCHAR ucNumEntries;
-    
-    //sizeof(ATOM_PPLIB_CLOCK_INFO)
-    UCHAR ucEntrySize;
-    
-    UCHAR clockInfo[1];
-}ClockInfoArray;
-
-typedef struct _NonClockInfoArray{
-
-    //how many non-clock levels we have. normally should be same as number of states
-    UCHAR ucNumEntries;
-    //sizeof(ATOM_PPLIB_NONCLOCK_INFO)
-    UCHAR ucEntrySize;
-    
-    ATOM_PPLIB_NONCLOCK_INFO nonClockInfo[1];
-}NonClockInfoArray;
-
-typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Record
-{
-    USHORT usClockLow;
-    UCHAR  ucClockHigh;
-    USHORT usVoltage;
-}ATOM_PPLIB_Clock_Voltage_Dependency_Record;
-
-typedef struct _ATOM_PPLIB_Clock_Voltage_Dependency_Table
-{
-    UCHAR ucNumEntries;                                                // Number of entries.
-    ATOM_PPLIB_Clock_Voltage_Dependency_Record entries[1];             // Dynamically allocate entries.
-}ATOM_PPLIB_Clock_Voltage_Dependency_Table;
-
-typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Record
-{
-    USHORT usSclkLow;
-    UCHAR  ucSclkHigh;
-    USHORT usMclkLow;
-    UCHAR  ucMclkHigh;
-    USHORT usVddc;
-    USHORT usVddci;
-}ATOM_PPLIB_Clock_Voltage_Limit_Record;
-
-typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table
-{
-    UCHAR ucNumEntries;                                                // Number of entries.
-    ATOM_PPLIB_Clock_Voltage_Limit_Record entries[1];                  // Dynamically allocate entries.
-}ATOM_PPLIB_Clock_Voltage_Limit_Table;
-
-typedef struct _ATOM_PPLIB_CAC_Leakage_Record
-{
-    USHORT usVddc;  // We use this field for the "fake" standardized VDDC for power calculations; For CI and newer, we use this as the real VDDC value.
-    ULONG  ulLeakageValue;  // For CI and newer we use this as the "fake" standar VDDC value.
-}ATOM_PPLIB_CAC_Leakage_Record;
-
-typedef struct _ATOM_PPLIB_CAC_Leakage_Table
-{
-    UCHAR ucNumEntries;                                                 // Number of entries.
-    ATOM_PPLIB_CAC_Leakage_Record entries[1];                           // Dynamically allocate entries.
-}ATOM_PPLIB_CAC_Leakage_Table;
-
-typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Record
-{
-    USHORT usVoltage;
-    USHORT usSclkLow;
-    UCHAR  ucSclkHigh;
-    USHORT usMclkLow;
-    UCHAR  ucMclkHigh;
-}ATOM_PPLIB_PhaseSheddingLimits_Record;
-
-typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Table
-{
-    UCHAR ucNumEntries;                                                 // Number of entries.
-    ATOM_PPLIB_PhaseSheddingLimits_Record entries[1];                   // Dynamically allocate entries.
-}ATOM_PPLIB_PhaseSheddingLimits_Table;
-
-typedef struct _VCEClockInfo{
-    USHORT usEVClkLow;
-    UCHAR  ucEVClkHigh;
-    USHORT usECClkLow;
-    UCHAR  ucECClkHigh;
-}VCEClockInfo;
-
-typedef struct _VCEClockInfoArray{
-    UCHAR ucNumEntries;
-    VCEClockInfo entries[1];
-}VCEClockInfoArray;
-
-typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record
-{
-    USHORT usVoltage;
-    UCHAR  ucVCEClockInfoIndex;
-}ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record;
-
-typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table
-{
-    UCHAR numEntries;
-    ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record entries[1];
-}ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table;
-
-typedef struct _ATOM_PPLIB_VCE_State_Record
-{
-    UCHAR  ucVCEClockInfoIndex;
-    UCHAR  ucClockInfoIndex; //highest 2 bits indicates memory p-states, lower 6bits indicates index to ClockInfoArrary
-}ATOM_PPLIB_VCE_State_Record;
-
-typedef struct _ATOM_PPLIB_VCE_State_Table
-{
-    UCHAR numEntries;
-    ATOM_PPLIB_VCE_State_Record entries[1];
-}ATOM_PPLIB_VCE_State_Table;
-
-
-typedef struct _ATOM_PPLIB_VCE_Table
-{
-      UCHAR revid;
-//    VCEClockInfoArray array;
-//    ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table limits;
-//    ATOM_PPLIB_VCE_State_Table states;
-}ATOM_PPLIB_VCE_Table;
-
-
-typedef struct _UVDClockInfo{
-    USHORT usVClkLow;
-    UCHAR  ucVClkHigh;
-    USHORT usDClkLow;
-    UCHAR  ucDClkHigh;
-}UVDClockInfo;
-
-typedef struct _UVDClockInfoArray{
-    UCHAR ucNumEntries;
-    UVDClockInfo entries[1];
-}UVDClockInfoArray;
-
-typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record
-{
-    USHORT usVoltage;
-    UCHAR  ucUVDClockInfoIndex;
-}ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record;
-
-typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table
-{
-    UCHAR numEntries;
-    ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record entries[1];
-}ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table;
-
-typedef struct _ATOM_PPLIB_UVD_State_Record
-{
-    UCHAR  ucUVDClockInfoIndex;
-    UCHAR  ucClockInfoIndex; //highest 2 bits indicates memory p-states, lower 6bits indicates index to ClockInfoArrary
-}ATOM_PPLIB_UVD_State_Record;
-
-typedef struct _ATOM_PPLIB_UVD_State_Table
-{
-    UCHAR numEntries;
-    ATOM_PPLIB_UVD_State_Record entries[1];
-}ATOM_PPLIB_UVD_State_Table;
-
-
-typedef struct _ATOM_PPLIB_UVD_Table
-{
-      UCHAR revid;
-//    UVDClockInfoArray array;
-//    ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table limits;
-//    ATOM_PPLIB_UVD_State_Table states;
-}ATOM_PPLIB_UVD_Table;
-
-
-typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Record
-{
-      USHORT usVoltage;
-      USHORT usSAMClockLow;
-      UCHAR  ucSAMClockHigh;
-}ATOM_PPLIB_SAMClk_Voltage_Limit_Record;
-
-typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Table{
-    UCHAR numEntries;
-    ATOM_PPLIB_SAMClk_Voltage_Limit_Record entries[1];
-}ATOM_PPLIB_SAMClk_Voltage_Limit_Table;
-
-typedef struct _ATOM_PPLIB_SAMU_Table
-{
-      UCHAR revid;
-      ATOM_PPLIB_SAMClk_Voltage_Limit_Table limits;
-}ATOM_PPLIB_SAMU_Table;
-
-#define ATOM_PPM_A_A    1
-#define ATOM_PPM_A_I    2
-typedef struct _ATOM_PPLIB_PPM_Table
-{
-      UCHAR  ucRevId;
-      UCHAR  ucPpmDesign;          //A+I or A+A
-      USHORT usCpuCoreNumber;
-      ULONG  ulPlatformTDP;
-      ULONG  ulSmallACPlatformTDP;
-      ULONG  ulPlatformTDC;
-      ULONG  ulSmallACPlatformTDC;
-      ULONG  ulApuTDP;
-      ULONG  ulDGpuTDP;
-      ULONG  ulDGpuUlvPower;
-      ULONG  ulTjmax;
-} ATOM_PPLIB_PPM_Table;
-
-/**************************************************************************/
-
 
 // Following definitions are for compatibility issue in different SW components. 
 #define ATOM_MASTER_DATA_TABLE_REVISION   0x01
@@ -8485,3 +7873,5 @@ typedef struct {
 
 
 #endif /* _ATOMBIOS_H */
+
+#include "pptable.h"
index 01b7592..5550c22 100644 (file)
@@ -1910,6 +1910,12 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
        int i;
 
        atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       /* disable the GRPH */
+       if (ASIC_IS_DCE4(rdev))
+               WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 0);
+       else if (ASIC_IS_AVIVO(rdev))
+               WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 0);
+
        if (ASIC_IS_DCE6(rdev))
                atombios_powergate_crtc(crtc, ATOM_ENABLE);
 
index 8a3bb52..a859d34 100644 (file)
@@ -50,7 +50,7 @@ static char *pre_emph_names[] = {
  * or from atom. Note that atom operates on
  * dw units.
  */
-static void radeon_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le)
+void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le)
 {
 #ifdef __BIG_ENDIAN
        u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */
@@ -100,7 +100,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
 
        base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
 
-       radeon_copy_swap(base, send, send_bytes, true);
+       radeon_atom_copy_swap(base, send, send_bytes, true);
 
        args.v1.lpAuxRequest = cpu_to_le16((u16)(0 + 4));
        args.v1.lpDataOut = cpu_to_le16((u16)(16 + 4));
@@ -137,7 +137,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
                recv_bytes = recv_size;
 
        if (recv && recv_size)
-               radeon_copy_swap(recv, base + 16, recv_bytes, false);
+               radeon_atom_copy_swap(recv, base + 16, recv_bytes, false);
 
        return recv_bytes;
 }
@@ -586,7 +586,7 @@ static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector,
                return false;
        }
 
-       DRM_DEBUG_KMS("link status %*ph\n", 6, link_status);
+       DRM_DEBUG_KMS("link status %6ph\n", link_status);
        return true;
 }
 
index be99a00..44a82c0 100644 (file)
  *
  * Authors: Dave Airlie
  *          Alex Deucher
- *
- * $FreeBSD: head/sys/dev/drm2/radeon/atombios_encoders.c 254885 2013-08-25 19:37:15Z dumbbell $
  */
-
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <uapi_drm/radeon_drm.h>
 #include "radeon.h"
-#include "radeon_asic.h" /* Declares several prototypes; clang is pleased. */
+#include "radeon_asic.h"
 #include "atom.h"
 
 static u8
@@ -751,8 +748,6 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
 int
 atombios_get_encoder_mode(struct drm_encoder *encoder)
 {
-       struct drm_device *dev = encoder->dev;
-       struct radeon_device *rdev = dev->dev_private;
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
        struct drm_connector *connector;
        struct radeon_connector *radeon_connector;
@@ -778,24 +773,37 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        switch (connector->connector_type) {
        case DRM_MODE_CONNECTOR_DVII:
        case DRM_MODE_CONNECTOR_HDMIB: /* HDMI-B is basically DL-DVI; analog works fine */
-               if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                   radeon_audio &&
-                   !ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
-                       return ATOM_ENCODER_MODE_HDMI;
-               else if (radeon_connector->use_digital)
+               if (radeon_audio != 0) {
+                       if (radeon_connector->use_digital &&
+                           (radeon_connector->audio == RADEON_AUDIO_ENABLE))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (radeon_connector->use_digital)
+                               return ATOM_ENCODER_MODE_DVI;
+                       else
+                               return ATOM_ENCODER_MODE_CRT;
+               } else if (radeon_connector->use_digital) {
                        return ATOM_ENCODER_MODE_DVI;
-               else
+               } else {
                        return ATOM_ENCODER_MODE_CRT;
+               }
                break;
        case DRM_MODE_CONNECTOR_DVID:
        case DRM_MODE_CONNECTOR_HDMIA:
        default:
-               if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                   radeon_audio &&
-                   !ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
-                       return ATOM_ENCODER_MODE_HDMI;
-               else
+               if (radeon_audio != 0) {
+                       if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else
+                               return ATOM_ENCODER_MODE_DVI;
+               } else {
                        return ATOM_ENCODER_MODE_DVI;
+               }
                break;
        case DRM_MODE_CONNECTOR_LVDS:
                return ATOM_ENCODER_MODE_LVDS;
@@ -803,14 +811,19 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        case DRM_MODE_CONNECTOR_DisplayPort:
                dig_connector = radeon_connector->con_priv;
                if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
-                   (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
+                   (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
                        return ATOM_ENCODER_MODE_DP;
-               else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
-                        radeon_audio &&
-                        !ASIC_IS_DCE6(rdev)) /* remove once we support DCE6 */
-                       return ATOM_ENCODER_MODE_HDMI;
-               else
+               } else if (radeon_audio != 0) {
+                       if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+                                (radeon_connector->audio == RADEON_AUDIO_AUTO))
+                               return ATOM_ENCODER_MODE_HDMI;
+                       else
+                               return ATOM_ENCODER_MODE_DVI;
+               } else {
                        return ATOM_ENCODER_MODE_DVI;
+               }
                break;
        case DRM_MODE_CONNECTOR_eDP:
                return ATOM_ENCODER_MODE_DP;
@@ -1721,8 +1734,12 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
                        atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
                        atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
-                       /* some early dce3.2 boards have a bug in their transmitter control table */
-                       if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730))
+                       /* some dce3.x boards have a bug in their transmitter control table.
+                        * ACTION_ENABLE_OUTPUT can probably be dropped since ACTION_ENABLE
+                        * does the same thing and more.
+                        */
+                       if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730) &&
+                           (rdev->family != CHIP_RS780) && (rdev->family != CHIP_RS880))
                                atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
                }
                if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
index dacf3b6..847c011 100644 (file)
@@ -37,7 +37,7 @@
 #define TARGET_HW_I2C_CLOCK 50
 
 /* these are a limitation of ProcessI2cChannelTransaction not the hw */
-#define ATOM_MAX_HW_I2C_WRITE 2
+#define ATOM_MAX_HW_I2C_WRITE 3
 #define ATOM_MAX_HW_I2C_READ  255
 
 static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
@@ -57,15 +57,24 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
 
        if (flags & HW_I2C_WRITE) {
                if (num > ATOM_MAX_HW_I2C_WRITE) {
-                       DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 2)\n", num);
+                       DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num);
                        return EINVAL;
                }
-               memcpy(&out, buf, num);
+               args.ucRegIndex = buf[0];
+               if (num > 1)
+                       memcpy(&out, &buf[1], num - 1);
                args.lpI2CDataOut = cpu_to_le16(out);
+       } else {
+               if (num > ATOM_MAX_HW_I2C_READ) {
+                       DRM_ERROR("hw i2c: tried to read too many bytes (%d vs 255)\n", num);
+                       return -EINVAL;
+               }
+               args.ucRegIndex = 0;
+               args.lpI2CDataOut = 0;
        }
 
+       args.ucFlag = flags;
        args.ucI2CSpeed = TARGET_HW_I2C_CLOCK;
-       args.ucRegIndex = 0;
        args.ucTransBytes = num;
        args.ucSlaveAddr = slave_addr << 1;
        args.ucLineNumber = chan->rec.i2c_id;
@@ -79,7 +88,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
        }
 
        if (!(flags & HW_I2C_WRITE))
-               memcpy(buf, base, num);
+               radeon_atom_copy_swap(buf, base, num, false);
 
        return 0;
 }
index 176d119..67d3b25 100644 (file)
@@ -1170,6 +1170,23 @@ static const struct radeon_blacklist_clocks btc_blacklist_clocks[] =
         { 25000, 30000, RADEON_SCLK_UP }
 };
 
+void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table,
+                                                    u32 *max_clock)
+{
+       u32 i, clock = 0;
+
+       if ((table == NULL) || (table->count == 0)) {
+               *max_clock = clock;
+               return;
+       }
+
+       for (i = 0; i < table->count; i++) {
+               if (clock < table->entries[i].clk)
+                       clock = table->entries[i].clk;
+       }
+       *max_clock = clock;
+}
+
 void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
                                        u32 clock, u16 max_voltage, u16 *voltage)
 {
@@ -1915,7 +1932,7 @@ static int btc_set_mc_special_registers(struct radeon_device *rdev,
                        }
                        j++;
 
-                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
 
                        tmp = RREG32(MC_PMG_CMD_MRS);
@@ -1930,7 +1947,7 @@ static int btc_set_mc_special_registers(struct radeon_device *rdev,
                        }
                        j++;
 
-                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
                        break;
                case MC_SEQ_RESERVE_M >> 2:
@@ -1944,7 +1961,7 @@ static int btc_set_mc_special_registers(struct radeon_device *rdev,
                        }
                        j++;
 
-                       if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
+                       if (j >= SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE)
                                return -EINVAL;
                        break;
                default:
@@ -2082,6 +2099,7 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
        bool disable_mclk_switching;
        u32 mclk, sclk;
        u16 vddc, vddci;
+       u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
 
        if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
            btc_dpm_vblank_too_short(rdev))
@@ -2123,6 +2141,39 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
                        ps->low.vddci = max_limits->vddci;
        }
 
+       /* limit clocks to max supported clocks based on voltage dependency tables */
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                       &max_sclk_vddc);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                       &max_mclk_vddci);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                       &max_mclk_vddc);
+
+       if (max_sclk_vddc) {
+               if (ps->low.sclk > max_sclk_vddc)
+                       ps->low.sclk = max_sclk_vddc;
+               if (ps->medium.sclk > max_sclk_vddc)
+                       ps->medium.sclk = max_sclk_vddc;
+               if (ps->high.sclk > max_sclk_vddc)
+                       ps->high.sclk = max_sclk_vddc;
+       }
+       if (max_mclk_vddci) {
+               if (ps->low.mclk > max_mclk_vddci)
+                       ps->low.mclk = max_mclk_vddci;
+               if (ps->medium.mclk > max_mclk_vddci)
+                       ps->medium.mclk = max_mclk_vddci;
+               if (ps->high.mclk > max_mclk_vddci)
+                       ps->high.mclk = max_mclk_vddci;
+       }
+       if (max_mclk_vddc) {
+               if (ps->low.mclk > max_mclk_vddc)
+                       ps->low.mclk = max_mclk_vddc;
+               if (ps->medium.mclk > max_mclk_vddc)
+                       ps->medium.mclk = max_mclk_vddc;
+               if (ps->high.mclk > max_mclk_vddc)
+                       ps->high.mclk = max_mclk_vddc;
+       }
+
        /* XXX validate the min clocks required for display */
 
        if (disable_mclk_switching) {
@@ -2342,12 +2393,6 @@ int btc_dpm_set_power_state(struct radeon_device *rdev)
                return ret;
        }
 
-       ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO);
-       if (ret) {
-               DRM_ERROR("rv770_dpm_force_performance_level failed\n");
-               return ret;
-       }
-
        return 0;
 }
 
@@ -2701,6 +2746,12 @@ int btc_dpm_init(struct radeon_device *rdev)
        else
                rdev->pm.dpm.dyn_state.sclk_mclk_delta = 10000;
 
+       /* make sure dc limits are valid */
+       if ((rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk == 0) ||
+           (rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk == 0))
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc =
+                       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+
        return 0;
 }
 
index 1a15e0e..3b6f12b 100644 (file)
@@ -46,6 +46,8 @@ void btc_adjust_clock_combinations(struct radeon_device *rdev,
                                   struct rv7xx_pl *pl);
 void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
                                        u32 clock, u16 max_voltage, u16 *voltage);
+void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table,
+                                                    u32 *max_clock);
 void btc_apply_voltage_delta_rules(struct radeon_device *rdev,
                                   u16 max_vddc, u16 max_vddci,
                                   u16 *vddc, u16 *vddci);
index 19a0114..98d009e 100644 (file)
@@ -317,58 +317,4 @@ const u32 cayman_default_state[] =
        0x00000010, /*  */
 };
 
-const u32 cayman_vs[] =
-{
-       0x00000004,
-       0x80400400,
-       0x0000a03c,
-       0x95000688,
-       0x00004000,
-       0x15000688,
-       0x00000000,
-       0x88000000,
-       0x04000000,
-       0x67961001,
-#ifdef __BIG_ENDIAN
-       0x00020000,
-#else
-       0x00000000,
-#endif
-       0x00000000,
-       0x04000000,
-       0x67961000,
-#ifdef __BIG_ENDIAN
-       0x00020008,
-#else
-       0x00000008,
-#endif
-       0x00000000,
-};
-
-const u32 cayman_ps[] =
-{
-       0x00000004,
-       0xa00c0000,
-       0x00000008,
-       0x80400000,
-       0x00000000,
-       0x95000688,
-       0x00000000,
-       0x88000000,
-       0x00380400,
-       0x00146b10,
-       0x00380000,
-       0x20146b10,
-       0x00380400,
-       0x40146b00,
-       0x80380000,
-       0x60146b00,
-       0x00000010,
-       0x000d1000,
-       0xb0800000,
-       0x00000000,
-};
-
-const u32 cayman_ps_size = ARRAY_SIZE(cayman_ps);
-const u32 cayman_vs_size = ARRAY_SIZE(cayman_vs);
 const u32 cayman_default_size = ARRAY_SIZE(cayman_default_state);
diff --git a/sys/dev/drm/radeon/ci_dpm.c b/sys/dev/drm/radeon/ci_dpm.c
new file mode 100644 (file)
index 0000000..6123a09
--- /dev/null
@@ -0,0 +1,5250 @@
+/*
+ * 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 <drm/drmP.h>
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "cikd.h"
+#include "r600_dpm.h"
+#include "ci_dpm.h"
+#include "ni_dpm.h"
+#include "atom.h"
+#include <linux/seq_file.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 }
+};
+
+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->ddev->pci_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;
+}
+
+void ci_dpm_powergate_uvd(struct radeon_device *rdev, bool gate)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+
+       if (pi->uvd_power_gated == gate)
+               return;
+
+       pi->uvd_power_gated = gate;
+
+       ci_update_uvd_dpm(rdev, gate);
+}
+
+bool ci_dpm_vblank_too_short(struct radeon_device *rdev)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       u32 vblank_time = r600_dpm_get_vblank_time(rdev);
+       u32 switch_limit = pi->mem_gddr5 ? 450 : 300;
+
+       if (vblank_time < switch_limit)
+               return true;
+       else
+               return false;
+
+}
+
+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;
+       u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
+       int i;
+
+       if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
+           ci_dpm_vblank_too_short(rdev))
+               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;
+               }
+       }
+
+       /* limit clocks to max supported clocks based on voltage dependency tables */
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                       &max_sclk_vddc);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                       &max_mclk_vddci);
+       btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                       &max_mclk_vddc);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (max_sclk_vddc) {
+                       if (ps->performance_levels[i].sclk > max_sclk_vddc)
+                               ps->performance_levels[i].sclk = max_sclk_vddc;
+               }
+               if (max_mclk_vddci) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddci)
+                               ps->performance_levels[i].mclk = max_mclk_vddci;
+               }
+               if (max_mclk_vddc) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddc)
+                               ps->performance_levels[i].mclk = max_mclk_vddc;
+               }
+       }
+
+       /* 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 u32 ci_get_lowest_enabled_level(struct radeon_device *rdev,
+                                      u32 level_mask)
+{
+       u32 level = 0;
+
+       while ((level_mask & (1 << level)) == 0)
+               level++;
+
+       return level;
+}
+
+
+int ci_dpm_force_performance_level(struct radeon_device *rdev,
+                                  enum radeon_dpm_forced_level level)
+{
+       struct ci_power_info *pi = ci_get_pi(rdev);
+       PPSMC_Result smc_result;
+       u32 tmp, levels, i;
+       int ret;
+
+       if (level == RADEON_DPM_FORCED_LEVEL_HIGH) {
+               if ((!pi->sclk_dpm_key_disabled) &&
+                   pi->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+                       levels = 0;
+                       tmp = pi->dpm_level_enable_mask.sclk_dpm_enable_mask;
+                       while (tmp >>= 1)
+                               levels++;
+                       if (levels) {
+                               ret = ci_dpm_force_state_sclk(rdev, levels);
+                               if (ret)
+                                       return ret;
+                               for (i = 0; i < rdev->usec_timeout; i++) {
+                                       tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX) &
+                                              CURR_SCLK_INDEX_MASK) >> CURR_SCLK_INDEX_SHIFT;
+                                       if (tmp == levels)
+                                               break;
+                                       udelay(1);
+                               }
+                       }
+               }
+               if ((!pi->mclk_dpm_key_disabled) &&
+                   pi->dpm_level_enable_mask.mclk_dpm_enable_mask) {
+                       levels = 0;
+                       tmp = pi->dpm_level_enable_mask.mclk_dpm_enable_mask;
+                       while (tmp >>= 1)
+                               levels++;
+                       if (levels) {
+                               ret = ci_dpm_force_state_mclk(rdev, levels);
+                               if (ret)
+                                       return ret;
+                               for (i = 0; i < rdev->usec_timeout; i++) {
+                                       tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX) &
+                                              CURR_MCLK_INDEX_MASK) >> CURR_MCLK_INDEX_SHIFT;
+                                       if (tmp == levels)
+                                               break;
+                                       udelay(1);
+                               }
+                       }
+               }
+               if ((!pi->pcie_dpm_key_disabled) &&
+                   pi->dpm_level_enable_mask.pcie_dpm_enable_mask) {
+                       levels = 0;
+                       tmp = pi->dpm_level_enable_mask.pcie_dpm_enable_mask;
+                       while (tmp >>= 1)
+                               levels++;
+                       if (levels) {
+                               ret = ci_dpm_force_state_pcie(rdev, level);
+                               if (ret)
+                                       return ret;
+                               for (i = 0; i < rdev->usec_timeout; i++) {
+                                       tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX_1) &
+                                              CURR_PCIE_INDEX_MASK) >> CURR_PCIE_INDEX_SHIFT;
+                                       if (tmp == levels)
+                                               break;
+                                       udelay(1);
+                               }
+                       }
+               }
+       } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) {
+               if ((!pi->sclk_dpm_key_disabled) &&
+                   pi->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+                       levels = ci_get_lowest_enabled_level(rdev,
+                                                            pi->dpm_level_enable_mask.sclk_dpm_enable_mask);
+                       ret = ci_dpm_force_state_sclk(rdev, levels);
+                       if (ret)
+                               return ret;
+                       for (i = 0; i < rdev->usec_timeout; i++) {
+                               tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX) &
+                                      CURR_SCLK_INDEX_MASK) >> CURR_SCLK_INDEX_SHIFT;
+                               if (tmp == levels)
+                                       break;
+                               udelay(1);
+                       }
+               }
+               if ((!pi->mclk_dpm_key_disabled) &&
+                   pi->dpm_level_enable_mask.mclk_dpm_enable_mask) {
+                       levels = ci_get_lowest_enabled_level(rdev,
+                                                            pi->dpm_level_enable_mask.mclk_dpm_enable_mask);
+                       ret = ci_dpm_force_state_mclk(rdev, levels);
+                       if (ret)
+                               return ret;
+                       for (i = 0; i < rdev->usec_timeout; i++) {
+                               tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX) &
+                                      CURR_MCLK_INDEX_MASK) >> CURR_MCLK_INDEX_SHIFT;
+                               if (tmp == levels)
+                                       break;
+                               udelay(1);
+                       }
+               }
+               if ((!pi->pcie_dpm_key_disabled) &&
+                   pi->dpm_level_enable_mask.pcie_dpm_enable_mask) {
+                       levels = ci_get_lowest_enabled_level(rdev,
+                                                            pi->dpm_level_enable_mask.pcie_dpm_enable_mask);
+                       ret = ci_dpm_force_state_pcie(rdev, levels);
+                       if (ret)
+                               return ret;
+                       for (i = 0; i < rdev->usec_timeout; i++) {
+                               tmp = (RREG32_SMC(TARGET_AND_CURRENT_PROFILE_INDEX_1) &
+                                      CURR_PCIE_INDEX_MASK) >> CURR_PCIE_INDEX_SHIFT;
+                               if (tmp == levels)
+                                       break;
+                               udelay(1);
+                       }
+               }
+       } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) {
+               if (!pi->sclk_dpm_key_disabled) {
+                       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel);
+                       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_NoForcedLevel);
+                       if (smc_result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+               if (!pi->pcie_dpm_key_disabled) {
+                       smc_result = ci_send_msg_to_smc(rdev, PPSMC_MSG_PCIeDPM_UnForceLevel);
+                       if (smc_result != PPSMC_Result_OK)
+                               return -EINVAL;
+               }
+       }
+
+       rdev->pm.dpm.forced_level = level;
+
+       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) {
+#ifdef CONFIG_ACPI
+               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;
+#endif
+               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;
+
+#ifdef CONFIG_ACPI
+               radeon_acpi_pcie_performance_request(rdev, request, false);
+#endif
+       }
+}
+
+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;
+
+}
+
+static 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;
+}
+
+static 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;
+
+       cik_update_cg(rdev, (RADEON_CG_BLOCK_GFX |
+                            RADEON_CG_BLOCK_MC |
+                            RADEON_CG_BLOCK_SDMA |
+                            RADEON_CG_BLOCK_BIF |
+                            RADEON_CG_BLOCK_UVD |
+                            RADEON_CG_BLOCK_HDP), false);
+
+       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_dpm_powergate_uvd(rdev, true);
+
+       cik_update_cg(rdev, (RADEON_CG_BLOCK_GFX |
+                            RADEON_CG_BLOCK_MC |
+                            RADEON_CG_BLOCK_SDMA |
+                            RADEON_CG_BLOCK_BIF |
+                            RADEON_CG_BLOCK_UVD |
+                            RADEON_CG_BLOCK_HDP), 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;
+
+       cik_update_cg(rdev, (RADEON_CG_BLOCK_GFX |
+                            RADEON_CG_BLOCK_MC |
+                            RADEON_CG_BLOCK_SDMA |
+                            RADEON_CG_BLOCK_UVD |
+                            RADEON_CG_BLOCK_HDP), false);
+
+       ci_dpm_powergate_uvd(rdev, false);
+
+       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;
+
+       cik_update_cg(rdev, (RADEON_CG_BLOCK_GFX |
+                            RADEON_CG_BLOCK_MC |
+                            RADEON_CG_BLOCK_SDMA |
+                            RADEON_CG_BLOCK_BIF |
+                            RADEON_CG_BLOCK_UVD |
+                            RADEON_CG_BLOCK_HDP), false);
+
+       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_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);
+
+       cik_update_cg(rdev, (RADEON_CG_BLOCK_GFX |
+                            RADEON_CG_BLOCK_MC |
+                            RADEON_CG_BLOCK_SDMA |
+                            RADEON_CG_BLOCK_BIF |
+                            RADEON_CG_BLOCK_UVD |
+                            RADEON_CG_BLOCK_HDP), true);
+
+       return 0;
+}
+
+static int __unused ci_dpm_power_control_set_level(struct radeon_device *rdev)
+{
+       return ci_power_control_set_level(rdev);
+}
+
+static void __unused 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++) {
+               u8 *idx;
+               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;
+               idx = (u8 *)&power_state->v2.clockInfoIndex[0];
+               for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+                       clock_array_index = idx[j];
+                       if (clock_array_index >= clock_info_array->ucNumEntries)
+                               continue;
+                       if (k >= CISLANDS_MAX_HARDWARE_POWERLEVELS)
+                               break;
+                       clock_info = (union pplib_clock_info *)
+                               ((u8 *)&clock_info_array->clockInfo[0] +
+                                (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;
+}
+
+static 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;
+
+       pi->caps_uvd_dpm = true;
+
+        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;
+
+       pi->uvd_power_gated = false;
+
+       /* make sure dc limits are valid */
+       if ((rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk == 0) ||
+           (rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk == 0))
+               rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc =
+                       rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+
+       return 0;
+}
+
+void ci_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
+                                                   struct seq_file *m)
+{
+       u32 sclk = ci_get_average_sclk_freq(rdev);
+       u32 mclk = ci_get_average_mclk_freq(rdev);
+
+       seq_printf(m, "power level avg    sclk: %u mclk: %u\n",
+                  sclk, mclk);
+}
+
+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/sys/dev/drm/radeon/ci_dpm.h b/sys/dev/drm/radeon/ci_dpm.h
new file mode 100644 (file)
index 0000000..93bbed9
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * 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;
+       bool uvd_power_gated;
+       /* 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/sys/dev/drm/radeon/ci_smc.c b/sys/dev/drm/radeon/ci_smc.c
new file mode 100644 (file)
index 0000000..1c18c7d
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * 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 <drm/drmP.h>
+#include "radeon.h"
+#include "cikd.h"
+#include "ppsmc.h"
+#include "ci_dpm.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;
+
+