vdec: support power manager for vdec. [1/1]
authorNanxin Qin <nanxin.qin@amlogic.com>
Thu, 25 Jun 2020 10:19:00 +0000 (18:19 +0800)
committerNanxin Qin <nanxin.qin@amlogic.com>
Wed, 22 Jul 2020 05:48:04 +0000 (22:48 -0700)
PD#SWPL-28425

Problem:
SC2 DOS power domain management

Solution:
1. unite interface for vdec power ctrl.
2. support config power ctrl mode from dts.
3. 5 modes can be used: a. legacy b.power-ctrl-api
   c.power-domain d.pd-sec-api e.pd-non-sec-api

Verify:
u212, ac214

Change-Id: I6554fe7abe0afc652e549452cd822ddf3630435b
Signed-off-by: Nanxin Qin <nanxin.qin@amlogic.com>
drivers/frame_provider/decoder/utils/Makefile
drivers/frame_provider/decoder/utils/vdec.c
drivers/frame_provider/decoder/utils/vdec_power_ctrl.c [new file with mode: 0644]
drivers/frame_provider/decoder/utils/vdec_power_ctrl.h [new file with mode: 0644]

index c587fd99ff64832b2dd6eea8d3c25d2da9ce7239..1e7552b2c1dbb99f01f4739f1751636cb7bab950 100644 (file)
@@ -6,4 +6,5 @@ decoder_common-objs     +=      amstream_profile.o
 decoder_common-objs    +=      frame_check.o amlogic_fbc_hook.o
 decoder_common-objs    +=      vdec_v4l2_buffer_ops.o
 decoder_common-objs    +=      vdec_sync.o
+decoder_common-objs    +=      vdec_power_ctrl.o
 
index 32b7b15af9316b0923bab629eae37fa881056ceb..088f5c5e80ce8f4b29e51f1aa432609459b08d00 100644 (file)
@@ -80,6 +80,8 @@
 
 #include <dt-bindings/power/sc2-pd.h>
 #include <linux/amlogic/pwr_ctrl.h>
+#include <linux/of_device.h>
+#include "vdec_power_ctrl.h"
 
 static DEFINE_MUTEX(vdec_mutex);
 
@@ -109,9 +111,12 @@ static unsigned int clk_config;
 u32 debug = VDEC_DBG_ALWAYS_LOAD_FW;
 EXPORT_SYMBOL(debug);
 
-static int hevc_max_reset_count;
+int hevc_max_reset_count;
+EXPORT_SYMBOL(hevc_max_reset_count);
+
+int no_powerdown;
+EXPORT_SYMBOL(no_powerdown);
 
-static int no_powerdown;
 static int parallel_decode = 1;
 static int fps_detection;
 static int fps_clear;
@@ -191,6 +196,7 @@ struct vdec_core_s {
        struct decode_fps_s decode_fps[MAX_INSTANCE_MUN];
        unsigned long buff_flag;
        unsigned long stream_buff_flag;
+       struct power_manager_s *pm;
 };
 
 struct canvas_status_s {
@@ -924,13 +930,6 @@ void update_vdec_clk_config_settings(unsigned int config)
 }
 EXPORT_SYMBOL(update_vdec_clk_config_settings);
 
-static bool hevc_workaround_needed(void)
-{
-       return (get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_GXBB) &&
-               (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MINOR)
-                       == GXBB_REV_A_MINOR);
-}
-
 struct device *get_codec_cma_device(void)
 {
        return vdec_core->cma_dev;
@@ -1140,8 +1139,9 @@ struct vdec_s *vdec_create(struct stream_port_s *port,
                }
        }
 
-       pr_debug("vdec_create instance %p, total %d\n", vdec,
-               atomic_read(&vdec_core->vdec_nr));
+       pr_debug("vdec_create instance %p, total %d, PM: %s\n", vdec,
+               atomic_read(&vdec_core->vdec_nr),
+               get_pm_name(vdec_core->pm->pm_type));
 
        //trace_vdec_create(vdec); /*DEBUG_TMP*/
 
@@ -3377,66 +3377,6 @@ static int vdec_core_thread(void *data)
 }
 
 #if 1                          /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
-static bool test_hevc(u32 decomp_addr, u32 us_delay)
-{
-       int i;
-
-       /* SW_RESET IPP */
-       WRITE_VREG(HEVCD_IPP_TOP_CNTL, 1);
-       WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0);
-
-       /* initialize all canvas table */
-       WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0);
-       for (i = 0; i < 32; i++)
-               WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
-                       0x1 | (i << 8) | decomp_addr);
-       WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
-       WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, (0 << 8) | (0<<1) | 1);
-       for (i = 0; i < 32; i++)
-               WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
-
-       /* Initialize mcrcc */
-       WRITE_VREG(HEVCD_MCRCC_CTL1, 0x2);
-       WRITE_VREG(HEVCD_MCRCC_CTL2, 0x0);
-       WRITE_VREG(HEVCD_MCRCC_CTL3, 0x0);
-       WRITE_VREG(HEVCD_MCRCC_CTL1, 0xff0);
-
-       /* Decomp initialize */
-       WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, 0x0);
-       WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, 0x0);
-
-       /* Frame level initialization */
-       WRITE_VREG(HEVCD_IPP_TOP_FRMCONFIG, 0x100 | (0x100 << 16));
-       WRITE_VREG(HEVCD_IPP_TOP_TILECONFIG3, 0x0);
-       WRITE_VREG(HEVCD_IPP_TOP_LCUCONFIG, 0x1 << 5);
-       WRITE_VREG(HEVCD_IPP_BITDEPTH_CONFIG, 0x2 | (0x2 << 2));
-
-       WRITE_VREG(HEVCD_IPP_CONFIG, 0x0);
-       WRITE_VREG(HEVCD_IPP_LINEBUFF_BASE, 0x0);
-
-       /* Enable SWIMP mode */
-       WRITE_VREG(HEVCD_IPP_SWMPREDIF_CONFIG, 0x1);
-
-       /* Enable frame */
-       WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0x2);
-       WRITE_VREG(HEVCD_IPP_TOP_FRMCTL, 0x1);
-
-       /* Send SW-command CTB info */
-       WRITE_VREG(HEVCD_IPP_SWMPREDIF_CTBINFO, 0x1 << 31);
-
-       /* Send PU_command */
-       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO0, (0x4 << 9) | (0x4 << 16));
-       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO1, 0x1 << 3);
-       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO2, 0x0);
-       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO3, 0x0);
-
-       udelay(us_delay);
-
-       WRITE_VREG(HEVCD_IPP_DBG_SEL, 0x2 << 4);
-
-       return (READ_VREG(HEVCD_IPP_DBG_DATA) & 3) == 1;
-}
-
 void vdec_power_reset(void)
 {
        /* enable vdec1 isolation */
@@ -3486,13 +3426,6 @@ EXPORT_SYMBOL(vdec_power_reset);
 
 void vdec_poweron(enum vdec_type_e core)
 {
-       void *decomp_addr = NULL;
-       dma_addr_t decomp_dma_addr;
-       u32 decomp_addr_aligned = 0;
-       int hevc_loop = 0;
-       int sleep_val, iso_val;
-       bool is_power_ctrl_ver2 = false;
-
        if (core >= VDEC_MAX)
                return;
 
@@ -3504,277 +3437,12 @@ void vdec_poweron(enum vdec_type_e core)
                return;
        }
 
-       if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SC2) {
-               if (core == VDEC_1)
-                       pwr_ctrl_psci_smc(PDID_DOS_VDEC, PWR_ON);
-               else if (core == VDEC_HEVC) {
-                       pwr_ctrl_psci_smc(PDID_DOS_HEVC, PWR_ON);
-
-                       /* wait 10uS */
-                       udelay(10);
-                       /* hevc soft reset */
-                       WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
-                       WRITE_VREG(DOS_SW_RESET3, 0);
-                       /* enable hevc clock */
-                       amports_switch_gate("clk_hevcf_mux", 1);
-                       if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
-                               amports_switch_gate("clk_hevcb_mux", 1);
-                       hevc_clock_hi_enable();
-                       hevc_back_clock_hi_enable();
-                       /* power up hevc memories */
-                       WRITE_VREG(DOS_MEM_PD_HEVC, 0);
-                       /* remove hevc isolation */
-               } else if (core == VDEC_HCODEC)
-                       pwr_ctrl_psci_smc(PDID_DOS_HCODEC, PWR_ON);
-
-               mutex_unlock(&vdec_mutex);
-
-               return;
-       }
-
        if (vdec_on(core)) {
                mutex_unlock(&vdec_mutex);
                return;
        }
 
-       is_power_ctrl_ver2 =
-               ((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
-               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1)) ? true : false;
-
-       if (hevc_workaround_needed() &&
-               (core == VDEC_HEVC)) {
-               decomp_addr = codec_mm_dma_alloc_coherent(MEM_NAME,
-                       SZ_64K + SZ_4K, &decomp_dma_addr, GFP_KERNEL, 0);
-
-               if (decomp_addr) {
-                       decomp_addr_aligned = ALIGN(decomp_dma_addr, SZ_64K);
-                       memset((u8 *)decomp_addr +
-                               (decomp_addr_aligned - decomp_dma_addr),
-                               0xff, SZ_4K);
-               } else
-                       pr_err("vdec: alloc HEVC gxbb decomp buffer failed.\n");
-       }
-
-       if (core == VDEC_1) {
-               sleep_val = is_power_ctrl_ver2 ? 0x2 : 0xc;
-               iso_val = is_power_ctrl_ver2 ? 0x2 : 0xc0;
-
-               /* vdec1 power on */
-#ifdef CONFIG_AMLOGIC_POWER
-               if (is_support_power_ctrl()) {
-                       if (power_ctrl_sleep_mask(true, sleep_val, 0)) {
-                               mutex_unlock(&vdec_mutex);
-                               pr_err("vdec-1 power on ctrl sleep fail.\n");
-                               return;
-                       }
-               } else {
-                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
-               }
-#else
-               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
-#endif
-               /* wait 10uS */
-               udelay(10);
-               /* vdec1 soft reset */
-               WRITE_VREG(DOS_SW_RESET0, 0xfffffffc);
-               WRITE_VREG(DOS_SW_RESET0, 0);
-               /* enable vdec1 clock */
-               /*
-                *add power on vdec clock level setting,only for m8 chip,
-                * m8baby and m8m2 can dynamic adjust vdec clock,
-                * power on with default clock level
-                */
-               amports_switch_gate("clk_vdec_mux", 1);
-               vdec_clock_hi_enable();
-               /* power up vdec memories */
-               WRITE_VREG(DOS_MEM_PD_VDEC, 0);
-
-               /* remove vdec1 isolation */
-#ifdef CONFIG_AMLOGIC_POWER
-               if (is_support_power_ctrl()) {
-                       if (power_ctrl_iso_mask(true, iso_val, 0)) {
-                               mutex_unlock(&vdec_mutex);
-                               pr_err("vdec-1 power on ctrl iso fail.\n");
-                               return;
-                       }
-               } else {
-                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
-               }
-#else
-               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
-#endif
-               /* reset DOS top registers */
-               WRITE_VREG(DOS_VDEC_MCRCC_STALL_CTRL, 0);
-       } else if (core == VDEC_2) {
-               if (has_vdec2()) {
-                       /* vdec2 power on */
-                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
-                                       ~0x30);
-                       /* wait 10uS */
-                       udelay(10);
-                       /* vdec2 soft reset */
-                       WRITE_VREG(DOS_SW_RESET2, 0xffffffff);
-                       WRITE_VREG(DOS_SW_RESET2, 0);
-                       /* enable vdec1 clock */
-                       vdec2_clock_hi_enable();
-                       /* power up vdec memories */
-                       WRITE_VREG(DOS_MEM_PD_VDEC2, 0);
-                       /* remove vdec2 isolation */
-                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) &
-                                       ~0x300);
-                       /* reset DOS top registers */
-                       WRITE_VREG(DOS_VDEC2_MCRCC_STALL_CTRL, 0);
-               }
-       } else if (core == VDEC_HCODEC) {
-               if (has_hdec()) {
-                       sleep_val = is_power_ctrl_ver2 ? 0x1 : 0x3;
-                       iso_val = is_power_ctrl_ver2 ? 0x1 : 0x30;
-
-                       /* hcodec power on */
-#ifdef CONFIG_AMLOGIC_POWER
-                       if (is_support_power_ctrl()) {
-                               if (power_ctrl_sleep_mask(true, sleep_val, 0)) {
-                                       mutex_unlock(&vdec_mutex);
-                                       pr_err("hcodec power on ctrl sleep fail.\n");
-                                       return;
-                               }
-                       } else {
-                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
-                       }
-#else
-                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
-#endif
-                       /* wait 10uS */
-                       udelay(10);
-                       /* hcodec soft reset */
-                       WRITE_VREG(DOS_SW_RESET1, 0xffffffff);
-                       WRITE_VREG(DOS_SW_RESET1, 0);
-                       /* enable hcodec clock */
-                       hcodec_clock_enable();
-                       /* power up hcodec memories */
-                       WRITE_VREG(DOS_MEM_PD_HCODEC, 0);
-                       /* remove hcodec isolation */
-#ifdef CONFIG_AMLOGIC_POWER
-                       if (is_support_power_ctrl()) {
-                               if (power_ctrl_iso_mask(true, iso_val, 0)) {
-                                       mutex_unlock(&vdec_mutex);
-                                       pr_err("hcodec power on ctrl iso fail.\n");
-                                       return;
-                               }
-                       } else {
-                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
-                       }
-#else
-                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
-#endif
-               }
-       } else if (core == VDEC_HEVC) {
-               if (has_hevc_vdec()) {
-                       bool hevc_fixed = false;
-
-                       sleep_val = is_power_ctrl_ver2 ? 0x4 : 0xc0;
-                       iso_val = is_power_ctrl_ver2 ? 0x4 : 0xc00;
-
-                       while (!hevc_fixed) {
-                               /* hevc power on */
-#ifdef CONFIG_AMLOGIC_POWER
-                               if (is_support_power_ctrl()) {
-                                       if (power_ctrl_sleep_mask(true, sleep_val, 0)) {
-                                               mutex_unlock(&vdec_mutex);
-                                               pr_err("hevc power on ctrl sleep fail.\n");
-                                               return;
-                                       }
-                               } else {
-                                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
-                               }
-#else
-                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
-#endif
-                               /* wait 10uS */
-                               udelay(10);
-                               /* hevc soft reset */
-                               WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
-                               WRITE_VREG(DOS_SW_RESET3, 0);
-                               /* enable hevc clock */
-                               amports_switch_gate("clk_hevc_mux", 1);
-                               if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
-                                       amports_switch_gate("clk_hevcb_mux", 1);
-                               hevc_clock_hi_enable();
-                               hevc_back_clock_hi_enable();
-                               /* power up hevc memories */
-                               WRITE_VREG(DOS_MEM_PD_HEVC, 0);
-                               /* remove hevc isolation */
-#ifdef CONFIG_AMLOGIC_POWER
-                               if (is_support_power_ctrl()) {
-                                       if (power_ctrl_iso_mask(true, iso_val, 0)) {
-                                               mutex_unlock(&vdec_mutex);
-                                               pr_err("hevc power on ctrl iso fail.\n");
-                                               return;
-                                       }
-                               } else {
-                                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
-                               }
-#else
-                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
-#endif
-                               if (!hevc_workaround_needed())
-                                       break;
-
-                               if (decomp_addr)
-                                       hevc_fixed = test_hevc(
-                                               decomp_addr_aligned, 20);
-
-                               if (!hevc_fixed) {
-                                       hevc_loop++;
-
-                                       mutex_unlock(&vdec_mutex);
-
-                                       if (hevc_loop >= HEVC_TEST_LIMIT) {
-                                               pr_warn("hevc power sequence over limit\n");
-                                               pr_warn("=====================================================\n");
-                                               pr_warn(" This chip is identified to have HW failure.\n");
-                                               pr_warn(" Please contact sqa-platform to replace the platform.\n");
-                                               pr_warn("=====================================================\n");
-
-                                               panic("Force panic for chip detection !!!\n");
-
-                                               break;
-                                       }
-
-                                       vdec_poweroff(VDEC_HEVC);
-
-                                       mdelay(10);
-
-                                       mutex_lock(&vdec_mutex);
-                               }
-                       }
-
-                       if (hevc_loop > hevc_max_reset_count)
-                               hevc_max_reset_count = hevc_loop;
-
-                       WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
-                       udelay(10);
-                       WRITE_VREG(DOS_SW_RESET3, 0);
-               }
-       }
-
-       if (decomp_addr)
-               codec_mm_dma_free_coherent(MEM_NAME,
-                       SZ_64K + SZ_4K, decomp_addr, decomp_dma_addr, 0);
+       vdec_core->pm->power_on(vdec_core->cma_dev, core);
 
        mutex_unlock(&vdec_mutex);
 }
@@ -3782,9 +3450,6 @@ EXPORT_SYMBOL(vdec_poweron);
 
 void vdec_poweroff(enum vdec_type_e core)
 {
-       int sleep_val, iso_val;
-       bool is_power_ctrl_ver2 = false;
-
        if (core >= VDEC_MAX)
                return;
 
@@ -3796,220 +3461,15 @@ void vdec_poweroff(enum vdec_type_e core)
                return;
        }
 
-       if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SC2) {
-               if (core == VDEC_1)
-                       pwr_ctrl_psci_smc(PDID_DOS_VDEC, PWR_OFF);
-               else if (core == VDEC_HEVC) {
-                       pwr_ctrl_psci_smc(PDID_DOS_HEVC, PWR_OFF);
-
-                       /* disable hevc clock */
-                       hevc_clock_off();
-                       if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
-                               hevc_back_clock_off();
-               }
-               else if (core == VDEC_HCODEC)
-                       pwr_ctrl_psci_smc(PDID_DOS_HCODEC, PWR_OFF);
-
-               mutex_unlock(&vdec_mutex);
-               return;
-       }
-
-       is_power_ctrl_ver2 =
-               ((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
-               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1)) ? true : false;
-
-       if (core == VDEC_1) {
-               sleep_val = is_power_ctrl_ver2 ? 0x2 : 0xc;
-               iso_val = is_power_ctrl_ver2 ? 0x2 : 0xc0;
-
-               /* enable vdec1 isolation */
-#ifdef CONFIG_AMLOGIC_POWER
-               if (is_support_power_ctrl()) {
-                       if (power_ctrl_iso_mask(false, iso_val, 0)) {
-                               mutex_unlock(&vdec_mutex);
-                               pr_err("vdec-1 power off ctrl iso fail.\n");
-                               return;
-                       }
-               } else {
-                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
-               }
-#else
-               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
-#endif
-               /* power off vdec1 memories */
-               WRITE_VREG(DOS_MEM_PD_VDEC, 0xffffffffUL);
-               /* disable vdec1 clock */
-               vdec_clock_off();
-               /* vdec1 power off */
-#ifdef CONFIG_AMLOGIC_POWER
-               if (is_support_power_ctrl()) {
-                       if (power_ctrl_sleep_mask(false, sleep_val, 0)) {
-                               mutex_unlock(&vdec_mutex);
-                               pr_err("vdec-1 power off ctrl sleep fail.\n");
-                               return;
-                       }
-               } else {
-                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
-               }
-#else
-               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
-#endif
-       } else if (core == VDEC_2) {
-               if (has_vdec2()) {
-                       /* enable vdec2 isolation */
-                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) |
-                                       0x300);
-                       /* power off vdec2 memories */
-                       WRITE_VREG(DOS_MEM_PD_VDEC2, 0xffffffffUL);
-                       /* disable vdec2 clock */
-                       vdec2_clock_off();
-                       /* vdec2 power off */
-                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) |
-                                       0x30);
-               }
-       } else if (core == VDEC_HCODEC) {
-               if (has_hdec()) {
-                       sleep_val = is_power_ctrl_ver2 ? 0x1 : 0x3;
-                       iso_val = is_power_ctrl_ver2 ? 0x1 : 0x30;
-
-                       /* enable hcodec isolation */
-#ifdef CONFIG_AMLOGIC_POWER
-                       if (is_support_power_ctrl()) {
-                               if (power_ctrl_iso_mask(false, iso_val, 0)) {
-                                       mutex_unlock(&vdec_mutex);
-                                       pr_err("hcodec power off ctrl iso fail.\n");
-                                       return;
-                               }
-                       } else {
-                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
-                       }
-#else
-                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
-#endif
-                       /* power off hcodec memories */
-                       WRITE_VREG(DOS_MEM_PD_HCODEC, 0xffffffffUL);
-                       /* disable hcodec clock */
-                       hcodec_clock_off();
-                       /* hcodec power off */
-#ifdef CONFIG_AMLOGIC_POWER
-                       if (is_support_power_ctrl()) {
-                               if (power_ctrl_sleep_mask(false, sleep_val, 0)) {
-                                       mutex_unlock(&vdec_mutex);
-                                       pr_err("hcodec power off ctrl sleep fail.\n");
-                                       return;
-                               }
-                       } else {
-                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
-                       }
-#else
-                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
-#endif
-               }
-       } else if (core == VDEC_HEVC) {
-               if (has_hevc_vdec()) {
-                       sleep_val = is_power_ctrl_ver2 ? 0x4 : 0xc0;
-                       iso_val = is_power_ctrl_ver2 ? 0x4 : 0xc00;
-
-                       if (no_powerdown == 0) {
-                               /* enable hevc isolation */
-#ifdef CONFIG_AMLOGIC_POWER
-                               if (is_support_power_ctrl()) {
-                                       if (power_ctrl_iso_mask(false, iso_val, 0)) {
-                                               mutex_unlock(&vdec_mutex);
-                                               pr_err("hevc power off ctrl iso fail.\n");
-                                               return;
-                                       }
-                               } else {
-                                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
-                               }
-#else
-                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
-#endif
-                               /* power off hevc memories */
-                               WRITE_VREG(DOS_MEM_PD_HEVC, 0xffffffffUL);
-
-                               /* disable hevc clock */
-                               hevc_clock_off();
-                               if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
-                                       hevc_back_clock_off();
+       vdec_core->pm->power_off(vdec_core->cma_dev, core);
 
-                               /* hevc power off */
-#ifdef CONFIG_AMLOGIC_POWER
-                               if (is_support_power_ctrl()) {
-                                       if (power_ctrl_sleep_mask(false, sleep_val, 0)) {
-                                               mutex_unlock(&vdec_mutex);
-                                               pr_err("hevc power off ctrl sleep fail.\n");
-                                               return;
-                                       }
-                               } else {
-                                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
-                               }
-#else
-                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
-                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
-#endif
-                       } else {
-                               pr_info("!!!!!!!!not power down\n");
-                               hevc_reset_core(NULL);
-                               no_powerdown = 0;
-                       }
-               }
-       }
        mutex_unlock(&vdec_mutex);
 }
 EXPORT_SYMBOL(vdec_poweroff);
 
 bool vdec_on(enum vdec_type_e core)
 {
-       bool ret = false;
-
-       if (core == VDEC_1) {
-               if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
-                       (((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
-                       (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1))
-                       ? 0x2 : 0xc)) == 0) &&
-                       (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x100))
-                       ret = true;
-       } else if (core == VDEC_2) {
-               if (has_vdec2()) {
-                       if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0x30) == 0) &&
-                               (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x100))
-                               ret = true;
-               }
-       } else if (core == VDEC_HCODEC) {
-               if (has_hdec()) {
-                       if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
-                               (((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
-                               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1))
-                               ? 0x1 : 0x3)) == 0) &&
-                               (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x1000000))
-                               ret = true;
-               }
-       } else if (core == VDEC_HEVC) {
-               if (has_hevc_vdec()) {
-                       if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
-                               (((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
-                               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1))
-                               ? 0x4 : 0xc0)) == 0) &&
-                               (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x1000000))
-                               ret = true;
-               }
-       }
-
-       return ret;
+       return vdec_core->pm->power_state(vdec_core->cma_dev, core);
 }
 EXPORT_SYMBOL(vdec_on);
 
@@ -5387,6 +4847,18 @@ static int vdec_probe(struct platform_device *pdev)
        vdec_core->vdec_core_wq = alloc_ordered_workqueue("%s",__WQ_LEGACY |
                WQ_MEM_RECLAIM |WQ_HIGHPRI/*high priority*/, "vdec-work");
        /*work queue priority lower than vdec-core.*/
+
+       /* power manager init. */
+       vdec_core->pm = (struct power_manager_s *)
+               of_device_get_match_data(&pdev->dev);
+       if (vdec_core->pm->init) {
+               r = vdec_core->pm->init(&pdev->dev);
+               if (r) {
+                       pr_err("vdec power manager init failed\n");
+                       return r;
+               }
+       }
+
        return 0;
 }
 
@@ -5408,18 +4880,15 @@ static int vdec_remove(struct platform_device *pdev)
        kthread_stop(vdec_core->thread);
 
        destroy_workqueue(vdec_core->vdec_core_wq);
+
+       if (vdec_core->pm->release)
+               vdec_core->pm->release(&pdev->dev);
+
        class_unregister(&vdec_class);
 
        return 0;
 }
 
-static const struct of_device_id amlogic_vdec_dt_match[] = {
-       {
-               .compatible = "amlogic, vdec",
-       },
-       {},
-};
-
 static struct mconfig vdec_configs[] = {
        MC_PU32("debug_trace_num", &debug_trace_num),
        MC_PI32("hevc_max_reset_count", &hevc_max_reset_count),
@@ -5429,12 +4898,14 @@ static struct mconfig vdec_configs[] = {
 };
 static struct mconfig_node vdec_node;
 
+extern const struct of_device_id amlogic_vdec_matches[];
+
 static struct platform_driver vdec_driver = {
        .probe = vdec_probe,
        .remove = vdec_remove,
        .driver = {
                .name = "vdec",
-               .of_match_table = amlogic_vdec_dt_match,
+               .of_match_table = amlogic_vdec_matches,
        }
 };
 
diff --git a/drivers/frame_provider/decoder/utils/vdec_power_ctrl.c b/drivers/frame_provider/decoder/utils/vdec_power_ctrl.c
new file mode 100644 (file)
index 0000000..fd1f29b
--- /dev/null
@@ -0,0 +1,792 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/vdec_power_ctrl.c
+ *
+ * Copyright (C) 2016 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#define DEBUG
+#include "vdec_power_ctrl.h"
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include <linux/amlogic/power_ctrl.h>
+#include <dt-bindings/power/sc2-pd.h>
+#include <linux/amlogic/pwr_ctrl.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+#include "../../../common/media_clock/switch/amports_gate.h"
+#include "../../../common/chips/decoder_cpu_ver_info.h"
+#include "../../../common/media_clock/clk/clk.h"
+
+#define HEVC_TEST_LIMIT                (100)
+#define GXBB_REV_A_MINOR       (0xa)
+
+extern int no_powerdown;
+extern int hevc_max_reset_count;
+
+struct pm_name_s {
+       int type;
+       const char *name;
+};
+
+static const struct pm_name_s pm_name[] = {
+       {PM_POWER_CTRL_RW_REG,          "legacy"},
+       {PM_POWER_CTRL_API,             "power-ctrl-api"},
+       {PM_POWER_DOMAIN,               "power-domain"},
+       {PM_POWER_DOMAIN_SEC_API,       "pd-sec-api"},
+       {PM_POWER_DOMAIN_NONSEC_API,    "pd-non-sec-api"},
+};
+
+const char *get_pm_name(int type)
+{
+       const char *name = "unknown";
+       int i, size = ARRAY_SIZE(pm_name);
+
+       for (i = 0; i < size; i++) {
+               if (type == pm_name[i].type)
+                       name = pm_name[i].name;
+       }
+
+       return name;
+}
+EXPORT_SYMBOL(get_pm_name);
+
+static struct pm_pd_s pm_domain_data[] = {
+       { .name = "pwrc-vdec", },
+       { .name = "pwrc-hcodec",},
+       { .name = "pwrc-vdec-2", },
+       { .name = "pwrc-hevc", },
+       { .name = "pwrc-hevc-b", },
+       { .name = "pwrc-wave", },
+};
+
+static void pm_vdec_power_switch(struct pm_pd_s *pd, int id, bool on)
+{
+       struct device *dev = pd[id].dev;
+
+       if (on)
+               pm_runtime_get_sync(dev);
+       else
+               pm_runtime_put_sync(dev);
+
+       pr_debug("the %-15s power %s\n",
+               pd[id].name, on ? "on" : "off");
+}
+
+static int pm_vdec_power_domain_init(struct device *dev)
+{
+       int i, err;
+       const struct power_manager_s *pm = of_device_get_match_data(dev);
+       struct pm_pd_s *pd = pm->pd_data;
+
+       for (i = 0; i < ARRAY_SIZE(pm_domain_data); i++) {
+               pd[i].dev = dev_pm_domain_attach_by_name(dev, pd[i].name);
+               if (IS_ERR_OR_NULL(pd[i].dev)) {
+                       err = PTR_ERR(pd[i].dev);
+                       dev_err(dev, "Get %s failed, pm-domain: %d\n",
+                               pd[i].name, err);
+                       continue;
+               }
+
+               pd[i].link = device_link_add(dev, pd[i].dev,
+                                            DL_FLAG_PM_RUNTIME |
+                                            DL_FLAG_STATELESS);
+               if (IS_ERR_OR_NULL(pd[i].link)) {
+                       dev_err(dev, "Adding %s device link failed!\n",
+                               pd[i].name);
+                       return -ENODEV;
+               }
+
+               pr_debug("power domain: name: %s, dev: %px, link: %px\n",
+                       pd[i].name, pd[i].dev, pd[i].link);
+       }
+
+       return 0;
+}
+
+static void pm_vdec_power_domain_relese(struct device *dev)
+{
+       int i;
+       const struct power_manager_s *pm = of_device_get_match_data(dev);
+       struct pm_pd_s *pd = pm->pd_data;
+
+       for (i = 0; i < ARRAY_SIZE(pm_domain_data); i++) {
+               if (!IS_ERR_OR_NULL(pd[i].link))
+                       device_link_del(pd[i].link);
+
+               if (!IS_ERR_OR_NULL(pd[i].dev))
+                       dev_pm_domain_detach(pd[i].dev, true);
+       }
+}
+
+static void pm_vdec_clock_on(int id)
+{
+       if (id == VDEC_1) {
+               amports_switch_gate("clk_vdec_mux", 1);
+               vdec_clock_hi_enable();
+       } else if (id == VDEC_HCODEC) {
+               hcodec_clock_enable();
+       } else if (id == VDEC_HEVC) {
+               /* enable hevc clock */
+               amports_switch_gate("clk_hevc_mux", 1);
+               if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
+                       amports_switch_gate("clk_hevcb_mux", 1);
+               hevc_clock_hi_enable();
+               hevc_back_clock_hi_enable();
+       }
+}
+
+static void pm_vdec_clock_off(int id)
+{
+       if (id == VDEC_1) {
+               vdec_clock_off();
+       } else if (id == VDEC_HCODEC) {
+               hcodec_clock_off();
+       } else if (id == VDEC_HEVC) {
+               /* disable hevc clock */
+               hevc_clock_off();
+               if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
+                       hevc_back_clock_off();
+       }
+}
+
+static void pm_vdec_power_domain_power_on(struct device *dev, int id)
+{
+       const struct power_manager_s *pm = of_device_get_match_data(dev);
+
+       pm_vdec_clock_on(id);
+       pm_vdec_power_switch(pm->pd_data, id, true);
+}
+
+static void pm_vdec_power_domain_power_off(struct device *dev, int id)
+{
+       const struct power_manager_s *pm = of_device_get_match_data(dev);
+
+       pm_vdec_clock_off(id);
+       pm_vdec_power_switch(pm->pd_data, id, false);
+}
+
+static bool pm_vdec_power_domain_power_state(struct device *dev, int id)
+{
+       const struct power_manager_s *pm = of_device_get_match_data(dev);
+
+       return pm_runtime_active(pm->pd_data[id].dev);
+}
+
+static bool test_hevc(u32 decomp_addr, u32 us_delay)
+{
+       int i;
+
+       /* SW_RESET IPP */
+       WRITE_VREG(HEVCD_IPP_TOP_CNTL, 1);
+       WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0);
+
+       /* initialize all canvas table */
+       WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0);
+       for (i = 0; i < 32; i++)
+               WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+                       0x1 | (i << 8) | decomp_addr);
+       WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
+       WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, (0 << 8) | (0<<1) | 1);
+       for (i = 0; i < 32; i++)
+               WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
+
+       /* Initialize mcrcc */
+       WRITE_VREG(HEVCD_MCRCC_CTL1, 0x2);
+       WRITE_VREG(HEVCD_MCRCC_CTL2, 0x0);
+       WRITE_VREG(HEVCD_MCRCC_CTL3, 0x0);
+       WRITE_VREG(HEVCD_MCRCC_CTL1, 0xff0);
+
+       /* Decomp initialize */
+       WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, 0x0);
+       WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, 0x0);
+
+       /* Frame level initialization */
+       WRITE_VREG(HEVCD_IPP_TOP_FRMCONFIG, 0x100 | (0x100 << 16));
+       WRITE_VREG(HEVCD_IPP_TOP_TILECONFIG3, 0x0);
+       WRITE_VREG(HEVCD_IPP_TOP_LCUCONFIG, 0x1 << 5);
+       WRITE_VREG(HEVCD_IPP_BITDEPTH_CONFIG, 0x2 | (0x2 << 2));
+
+       WRITE_VREG(HEVCD_IPP_CONFIG, 0x0);
+       WRITE_VREG(HEVCD_IPP_LINEBUFF_BASE, 0x0);
+
+       /* Enable SWIMP mode */
+       WRITE_VREG(HEVCD_IPP_SWMPREDIF_CONFIG, 0x1);
+
+       /* Enable frame */
+       WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0x2);
+       WRITE_VREG(HEVCD_IPP_TOP_FRMCTL, 0x1);
+
+       /* Send SW-command CTB info */
+       WRITE_VREG(HEVCD_IPP_SWMPREDIF_CTBINFO, 0x1 << 31);
+
+       /* Send PU_command */
+       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO0, (0x4 << 9) | (0x4 << 16));
+       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO1, 0x1 << 3);
+       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO2, 0x0);
+       WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO3, 0x0);
+
+       udelay(us_delay);
+
+       WRITE_VREG(HEVCD_IPP_DBG_SEL, 0x2 << 4);
+
+       return (READ_VREG(HEVCD_IPP_DBG_DATA) & 3) == 1;
+}
+
+static bool hevc_workaround_needed(void)
+{
+       return (get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_GXBB) &&
+               (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MINOR)
+                       == GXBB_REV_A_MINOR);
+}
+
+static void pm_vdec_legacy_power_off(struct device *dev, int id);
+
+static void pm_vdec_legacy_power_on(struct device *dev, int id)
+{
+       void *decomp_addr = NULL;
+       dma_addr_t decomp_dma_addr;
+       u32 decomp_addr_aligned = 0;
+       int hevc_loop = 0;
+       int sleep_val, iso_val;
+       bool is_power_ctrl_ver2 = false;
+
+       is_power_ctrl_ver2 =
+               ((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
+               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1)) ? true : false;
+
+       if (hevc_workaround_needed() &&
+               (id == VDEC_HEVC)) {
+               decomp_addr = codec_mm_dma_alloc_coherent("vdec_prealloc",
+                       SZ_64K + SZ_4K, &decomp_dma_addr, GFP_KERNEL, 0);
+
+               if (decomp_addr) {
+                       decomp_addr_aligned = ALIGN(decomp_dma_addr, SZ_64K);
+                       memset((u8 *)decomp_addr +
+                               (decomp_addr_aligned - decomp_dma_addr),
+                               0xff, SZ_4K);
+               } else
+                       pr_err("vdec: alloc HEVC gxbb decomp buffer failed.\n");
+       }
+
+       if (id == VDEC_1) {
+               sleep_val = is_power_ctrl_ver2 ? 0x2 : 0xc;
+               iso_val = is_power_ctrl_ver2 ? 0x2 : 0xc0;
+
+               /* vdec1 power on */
+#ifdef CONFIG_AMLOGIC_POWER
+               if (is_support_power_ctrl()) {
+                       if (power_ctrl_sleep_mask(true, sleep_val, 0)) {
+                               pr_err("vdec-1 power on ctrl sleep fail.\n");
+                               return;
+                       }
+               } else {
+                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
+               }
+#else
+               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
+#endif
+               /* wait 10uS */
+               udelay(10);
+               /* vdec1 soft reset */
+               WRITE_VREG(DOS_SW_RESET0, 0xfffffffc);
+               WRITE_VREG(DOS_SW_RESET0, 0);
+               /* enable vdec1 clock */
+               /*
+                *add power on vdec clock level setting,only for m8 chip,
+                * m8baby and m8m2 can dynamic adjust vdec clock,
+                * power on with default clock level
+                */
+               amports_switch_gate("clk_vdec_mux", 1);
+               vdec_clock_hi_enable();
+               /* power up vdec memories */
+               WRITE_VREG(DOS_MEM_PD_VDEC, 0);
+
+               /* remove vdec1 isolation */
+#ifdef CONFIG_AMLOGIC_POWER
+               if (is_support_power_ctrl()) {
+                       if (power_ctrl_iso_mask(true, iso_val, 0)) {
+                               pr_err("vdec-1 power on ctrl iso fail.\n");
+                               return;
+                       }
+               } else {
+                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
+               }
+#else
+               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
+#endif
+               /* reset DOS top registers */
+               WRITE_VREG(DOS_VDEC_MCRCC_STALL_CTRL, 0);
+       } else if (id == VDEC_2) {
+               if (has_vdec2()) {
+                       /* vdec2 power on */
+                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
+                                       ~0x30);
+                       /* wait 10uS */
+                       udelay(10);
+                       /* vdec2 soft reset */
+                       WRITE_VREG(DOS_SW_RESET2, 0xffffffff);
+                       WRITE_VREG(DOS_SW_RESET2, 0);
+                       /* enable vdec1 clock */
+                       vdec2_clock_hi_enable();
+                       /* power up vdec memories */
+                       WRITE_VREG(DOS_MEM_PD_VDEC2, 0);
+                       /* remove vdec2 isolation */
+                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) &
+                                       ~0x300);
+                       /* reset DOS top registers */
+                       WRITE_VREG(DOS_VDEC2_MCRCC_STALL_CTRL, 0);
+               }
+       } else if (id == VDEC_HCODEC) {
+               if (has_hdec()) {
+                       sleep_val = is_power_ctrl_ver2 ? 0x1 : 0x3;
+                       iso_val = is_power_ctrl_ver2 ? 0x1 : 0x30;
+
+                       /* hcodec power on */
+#ifdef CONFIG_AMLOGIC_POWER
+                       if (is_support_power_ctrl()) {
+                               if (power_ctrl_sleep_mask(true, sleep_val, 0)) {
+                                       pr_err("hcodec power on ctrl sleep fail.\n");
+                                       return;
+                               }
+                       } else {
+                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
+                       }
+#else
+                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
+#endif
+                       /* wait 10uS */
+                       udelay(10);
+                       /* hcodec soft reset */
+                       WRITE_VREG(DOS_SW_RESET1, 0xffffffff);
+                       WRITE_VREG(DOS_SW_RESET1, 0);
+                       /* enable hcodec clock */
+                       hcodec_clock_enable();
+                       /* power up hcodec memories */
+                       WRITE_VREG(DOS_MEM_PD_HCODEC, 0);
+                       /* remove hcodec isolation */
+#ifdef CONFIG_AMLOGIC_POWER
+                       if (is_support_power_ctrl()) {
+                               if (power_ctrl_iso_mask(true, iso_val, 0)) {
+                                       pr_err("hcodec power on ctrl iso fail.\n");
+                                       return;
+                               }
+                       } else {
+                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
+                       }
+#else
+                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
+#endif
+               }
+       } else if (id == VDEC_HEVC) {
+               if (has_hevc_vdec()) {
+                       bool hevc_fixed = false;
+
+                       sleep_val = is_power_ctrl_ver2 ? 0x4 : 0xc0;
+                       iso_val = is_power_ctrl_ver2 ? 0x4 : 0xc00;
+
+                       while (!hevc_fixed) {
+                               /* hevc power on */
+#ifdef CONFIG_AMLOGIC_POWER
+                               if (is_support_power_ctrl()) {
+                                       if (power_ctrl_sleep_mask(true, sleep_val, 0)) {
+                                               pr_err("hevc power on ctrl sleep fail.\n");
+                                               return;
+                                       }
+                               } else {
+                                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
+                               }
+#else
+                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~sleep_val);
+#endif
+                               /* wait 10uS */
+                               udelay(10);
+                               /* hevc soft reset */
+                               WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
+                               WRITE_VREG(DOS_SW_RESET3, 0);
+                               /* enable hevc clock */
+                               amports_switch_gate("clk_hevc_mux", 1);
+                               if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
+                                       amports_switch_gate("clk_hevcb_mux", 1);
+                               hevc_clock_hi_enable();
+                               hevc_back_clock_hi_enable();
+                               /* power up hevc memories */
+                               WRITE_VREG(DOS_MEM_PD_HEVC, 0);
+                               /* remove hevc isolation */
+#ifdef CONFIG_AMLOGIC_POWER
+                               if (is_support_power_ctrl()) {
+                                       if (power_ctrl_iso_mask(true, iso_val, 0)) {
+                                               pr_err("hevc power on ctrl iso fail.\n");
+                                               return;
+                                       }
+                               } else {
+                                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
+                               }
+#else
+                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~iso_val);
+#endif
+                               if (!hevc_workaround_needed())
+                                       break;
+
+                               if (decomp_addr)
+                                       hevc_fixed = test_hevc(
+                                               decomp_addr_aligned, 20);
+
+                               if (!hevc_fixed) {
+                                       hevc_loop++;
+                                       if (hevc_loop >= HEVC_TEST_LIMIT) {
+                                               pr_warn("hevc power sequence over limit\n");
+                                               pr_warn("=====================================================\n");
+                                               pr_warn(" This chip is identified to have HW failure.\n");
+                                               pr_warn(" Please contact sqa-platform to replace the platform.\n");
+                                               pr_warn("=====================================================\n");
+
+                                               panic("Force panic for chip detection !!!\n");
+
+                                               break;
+                                       }
+
+                                       pm_vdec_legacy_power_off(NULL, VDEC_HEVC);
+
+                                       mdelay(10);
+                               }
+                       }
+
+                       if (hevc_loop > hevc_max_reset_count)
+                               hevc_max_reset_count = hevc_loop;
+
+                       WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
+                       udelay(10);
+                       WRITE_VREG(DOS_SW_RESET3, 0);
+               }
+       }
+
+       if (decomp_addr)
+               codec_mm_dma_free_coherent("vdec_prealloc",
+                       SZ_64K + SZ_4K, decomp_addr, decomp_dma_addr, 0);
+}
+
+static void pm_vdec_legacy_power_off(struct device *dev, int id)
+{
+       int sleep_val, iso_val;
+       bool is_power_ctrl_ver2 = false;
+
+       is_power_ctrl_ver2 =
+               ((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
+               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1)) ? true : false;
+
+       if (id == VDEC_1) {
+               sleep_val = is_power_ctrl_ver2 ? 0x2 : 0xc;
+               iso_val = is_power_ctrl_ver2 ? 0x2 : 0xc0;
+
+               /* enable vdec1 isolation */
+#ifdef CONFIG_AMLOGIC_POWER
+               if (is_support_power_ctrl()) {
+                       if (power_ctrl_iso_mask(false, iso_val, 0)) {
+                               pr_err("vdec-1 power off ctrl iso fail.\n");
+                               return;
+                       }
+               } else {
+                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
+               }
+#else
+               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
+#endif
+               /* power off vdec1 memories */
+               WRITE_VREG(DOS_MEM_PD_VDEC, 0xffffffffUL);
+               /* disable vdec1 clock */
+               vdec_clock_off();
+               /* vdec1 power off */
+#ifdef CONFIG_AMLOGIC_POWER
+               if (is_support_power_ctrl()) {
+                       if (power_ctrl_sleep_mask(false, sleep_val, 0)) {
+                               pr_err("vdec-1 power off ctrl sleep fail.\n");
+                               return;
+                       }
+               } else {
+                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
+               }
+#else
+               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
+#endif
+       } else if (id == VDEC_2) {
+               if (has_vdec2()) {
+                       /* enable vdec2 isolation */
+                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) |
+                                       0x300);
+                       /* power off vdec2 memories */
+                       WRITE_VREG(DOS_MEM_PD_VDEC2, 0xffffffffUL);
+                       /* disable vdec2 clock */
+                       vdec2_clock_off();
+                       /* vdec2 power off */
+                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) |
+                                       0x30);
+               }
+       } else if (id == VDEC_HCODEC) {
+               if (has_hdec()) {
+                       sleep_val = is_power_ctrl_ver2 ? 0x1 : 0x3;
+                       iso_val = is_power_ctrl_ver2 ? 0x1 : 0x30;
+
+                       /* enable hcodec isolation */
+#ifdef CONFIG_AMLOGIC_POWER
+                       if (is_support_power_ctrl()) {
+                               if (power_ctrl_iso_mask(false, iso_val, 0)) {
+                                       pr_err("hcodec power off ctrl iso fail.\n");
+                                       return;
+                               }
+                       } else {
+                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
+                       }
+#else
+                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
+#endif
+                       /* power off hcodec memories */
+                       WRITE_VREG(DOS_MEM_PD_HCODEC, 0xffffffffUL);
+                       /* disable hcodec clock */
+                       hcodec_clock_off();
+                       /* hcodec power off */
+#ifdef CONFIG_AMLOGIC_POWER
+                       if (is_support_power_ctrl()) {
+                               if (power_ctrl_sleep_mask(false, sleep_val, 0)) {
+                                       pr_err("hcodec power off ctrl sleep fail.\n");
+                                       return;
+                               }
+                       } else {
+                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
+                       }
+#else
+                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
+#endif
+               }
+       } else if (id == VDEC_HEVC) {
+               if (has_hevc_vdec()) {
+                       sleep_val = is_power_ctrl_ver2 ? 0x4 : 0xc0;
+                       iso_val = is_power_ctrl_ver2 ? 0x4 : 0xc00;
+
+                       if (no_powerdown == 0) {
+                               /* enable hevc isolation */
+#ifdef CONFIG_AMLOGIC_POWER
+                               if (is_support_power_ctrl()) {
+                                       if (power_ctrl_iso_mask(false, iso_val, 0)) {
+                                               pr_err("hevc power off ctrl iso fail.\n");
+                                               return;
+                                       }
+                               } else {
+                                       WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                               READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
+                               }
+#else
+                               WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_ISO0) | iso_val);
+#endif
+                               /* power off hevc memories */
+                               WRITE_VREG(DOS_MEM_PD_HEVC, 0xffffffffUL);
+
+                               /* disable hevc clock */
+                               hevc_clock_off();
+                               if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
+                                       hevc_back_clock_off();
+
+                               /* hevc power off */
+#ifdef CONFIG_AMLOGIC_POWER
+                               if (is_support_power_ctrl()) {
+                                       if (power_ctrl_sleep_mask(false, sleep_val, 0)) {
+                                               pr_err("hevc power off ctrl sleep fail.\n");
+                                               return;
+                                       }
+                               } else {
+                                       WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                               READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
+                               }
+#else
+                               WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+                                       READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | sleep_val);
+#endif
+                       } else {
+                               pr_info("!!!!!!!!not power down\n");
+                               hevc_reset_core(NULL);
+                               no_powerdown = 0;
+                       }
+               }
+       }
+}
+
+static bool pm_vdec_legacy_power_state(struct device *dev, int id)
+{
+       bool ret = false;
+
+       if (id == VDEC_1) {
+               if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
+                       (((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
+                       (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1))
+                       ? 0x2 : 0xc)) == 0) &&
+                       (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x100))
+                       ret = true;
+       } else if (id == VDEC_2) {
+               if (has_vdec2()) {
+                       if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0x30) == 0) &&
+                               (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x100))
+                               ret = true;
+               }
+       } else if (id == VDEC_HCODEC) {
+               if (has_hdec()) {
+                       if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
+                               (((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
+                               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1))
+                               ? 0x1 : 0x3)) == 0) &&
+                               (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x1000000))
+                               ret = true;
+               }
+       } else if (id == VDEC_HEVC) {
+               if (has_hevc_vdec()) {
+                       if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
+                               (((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) &&
+                               (get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TL1))
+                               ? 0x4 : 0xc0)) == 0) &&
+                               (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x1000000))
+                               ret = true;
+               }
+       }
+
+       return ret;
+}
+
+static void pm_vdec_pd_sec_api_power_on(struct device *dev, int id)
+{
+       int pd_id = (id == VDEC_1) ? PDID_DOS_VDEC :
+                   (id == VDEC_HEVC) ? PDID_DOS_HEVC :
+                   PDID_DOS_HCODEC;
+
+       pm_vdec_clock_on(id);
+       pwr_ctrl_psci_smc(pd_id, PWR_ON);
+
+}
+
+static void pm_vdec_pd_sec_api_power_off(struct device *dev, int id)
+{
+       int pd_id = (id == VDEC_1) ? PDID_DOS_VDEC :
+                   (id == VDEC_HEVC) ? PDID_DOS_HEVC :
+                   PDID_DOS_HCODEC;
+
+       pm_vdec_clock_off(id);
+       pwr_ctrl_psci_smc(pd_id, PWR_OFF);
+}
+
+static bool pm_vdec_pd_sec_api_power_state(struct device *dev, int id)
+{
+       int pd_id = (id == VDEC_1) ? PDID_DOS_VDEC :
+                   (id == VDEC_HEVC) ? PDID_DOS_HEVC :
+                   PDID_DOS_HCODEC;
+
+       return !pwr_ctrl_status_psci_smc(pd_id);
+}
+
+static void pm_vdec_pd_nosec_api_power_on(struct device *dev, int id)
+{
+#if 0
+       int pd_id = (id == VDEC_1) ? PM_DOS_VDEC :
+                   (id == VDEC_HEVC) ? PM_DOS_HEVC :
+                   PM_DOS_HCODEC;
+
+       pm_vdec_clock_on(id);
+       power_domain_switch(pd_id, PWR_ON);
+#endif
+}
+
+static void pm_vdec_pd_nosec_api_power_off(struct device *dev, int id)
+{
+#if 0
+       int pd_id = (id == VDEC_1) ? PM_DOS_VDEC :
+                   (id == VDEC_HEVC) ? PM_DOS_HEVC :
+                   PM_DOS_HCODEC;
+
+       pm_vdec_clock_off(id);
+       power_domain_switch(pd_id, PWR_OFF);
+#endif
+}
+
+static bool pm_vdec_pd_nosec_api_power_state(struct device *dev, int id)
+{
+       return pm_vdec_legacy_power_state(dev, id);
+}
+
+static const struct power_manager_s pm_rw_reg_data = {
+       .pm_type        = PM_POWER_CTRL_RW_REG,
+       .power_on       = pm_vdec_legacy_power_on,
+       .power_off      = pm_vdec_legacy_power_off,
+       .power_state    = pm_vdec_legacy_power_state,
+};
+
+static const struct power_manager_s pm_ctrl_api_data = {
+       .pm_type        = PM_POWER_CTRL_API,
+       .power_on       = pm_vdec_legacy_power_on,
+       .power_off      = pm_vdec_legacy_power_off,
+       .power_state    = pm_vdec_legacy_power_state,
+};
+
+static const struct power_manager_s pm_pd_data = {
+       .pm_type        = PM_POWER_DOMAIN,
+       .pd_data        = pm_domain_data,
+       .init           = pm_vdec_power_domain_init,
+       .release        = pm_vdec_power_domain_relese,
+       .power_on       = pm_vdec_power_domain_power_on,
+       .power_off      = pm_vdec_power_domain_power_off,
+       .power_state    = pm_vdec_power_domain_power_state,
+};
+
+static const struct power_manager_s pm_pd_sec_api_data = {
+       .pm_type        = PM_POWER_DOMAIN_SEC_API,
+       .power_on       = pm_vdec_pd_sec_api_power_on,
+       .power_off      = pm_vdec_pd_sec_api_power_off,
+       .power_state    = pm_vdec_pd_sec_api_power_state,
+};
+
+static const struct power_manager_s pm_pd_nosec_api_data = {
+       .pm_type        = PM_POWER_DOMAIN_NONSEC_API,
+       .power_on       = pm_vdec_pd_nosec_api_power_on,
+       .power_off      = pm_vdec_pd_nosec_api_power_off,
+       .power_state    = pm_vdec_pd_nosec_api_power_state,
+};
+
+const struct of_device_id amlogic_vdec_matches[] = {
+       { .compatible = "amlogic, vdec",                .data = &pm_rw_reg_data },
+       { .compatible = "amlogic, vdec-pm-api",         .data = &pm_ctrl_api_data },
+       { .compatible = "amlogic, vdec-pm-pd",          .data = &pm_pd_data },
+       { .compatible = "amlogic, vdec-pm-pd-sec-api",  .data = &pm_pd_sec_api_data },
+       { .compatible = "amlogic, vdec-pm-pd-nsec-api", .data = &pm_pd_nosec_api_data },
+       {},
+};
+EXPORT_SYMBOL(amlogic_vdec_matches);
+
diff --git a/drivers/frame_provider/decoder/utils/vdec_power_ctrl.h b/drivers/frame_provider/decoder/utils/vdec_power_ctrl.h
new file mode 100644 (file)
index 0000000..e7ab77e
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/vdec_power_ctrl.h
+ *
+ * Copyright (C) 2016 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include "vdec.h"
+
+/* Directly controlled by reading and writing registers. */
+#define PM_POWER_CTRL_RW_REG           (0)
+
+/* Use power_ctrl_xxx family of interface controls. */
+#define PM_POWER_CTRL_API              (1)
+
+/*
+ * Power Domain interface control, currently supported
+ * in versions 4.19 and above.
+ */
+#define PM_POWER_DOMAIN                        (2)
+
+/*
+ * Controlled by the secure API provided by power domain,
+ * version 4.9 supports currently supported platforms (SC2).
+ */
+#define PM_POWER_DOMAIN_SEC_API                (3)
+
+/*
+ * Use non-secure API control through power domain, version 4.9 support,
+ * currently supported platforms (SM1, TM2, TM2-revB).
+ */
+#define PM_POWER_DOMAIN_NONSEC_API     (4)
+
+enum pm_pd_e {
+       PD_VDEC,
+       PD_HCODEC,
+       PD_VDEC2,
+       PD_HEVC,
+       PD_HEVCB,
+       PD_WAVE,
+       PD_MAX
+};
+
+struct pm_pd_s {
+       u8 *name;
+       struct device *dev;
+       struct device_link *link;
+};
+
+struct power_manager_s {
+       int pm_type;
+       struct pm_pd_s *pd_data;
+       int (*init) (struct device *dev);
+       void (*release) (struct device *dev);
+       void (*power_on) (struct device *dev, int id);
+       void (*power_off) (struct device *dev, int id);
+       bool (*power_state) (struct device *dev, int id);
+};
+
+const char *get_pm_name(int type);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+#define DL_FLAG_STATELESS              BIT(0)
+#define DL_FLAG_AUTOREMOVE_CONSUMER    BIT(1)
+#define DL_FLAG_PM_RUNTIME             BIT(2)
+#define DL_FLAG_RPM_ACTIVE             BIT(3)
+#define DL_FLAG_AUTOREMOVE_SUPPLIER    BIT(4)
+
+struct device_link {
+       u32 flags;
+       /* ... */
+};
+
+static inline struct device *dev_pm_domain_attach_by_name(struct device *dev,
+                                                         const char *name)
+                                                         { return NULL; }
+static inline struct device_link *device_link_add(struct device *consumer,
+                                   struct device *supplier, u32 flags)
+                                   { return NULL; }
+static inline void device_link_del(struct device_link *link) { return; }
+static inline void device_link_remove(void *consumer, struct device *supplier) { return; }
+#endif
+