summaryrefslogtreecommitdiff
Diffstat
-rw-r--r--drivers/frame_provider/decoder/utils/Makefile1
-rw-r--r--drivers/frame_provider/decoder/utils/vdec.c689
-rw-r--r--drivers/frame_provider/decoder/utils/vdec_power_ctrl.c801
-rw-r--r--drivers/frame_provider/decoder/utils/vdec_power_ctrl.h107
-rw-r--r--drivers/stream_input/amports/amstream.c1
5 files changed, 935 insertions, 664 deletions
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
index 0000000..36fe9d0
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/vdec_power_ctrl.c
@@ -0,0 +1,801 @@
+/*
+ * 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)
+
+/* wait other module to support this function */
+bool is_support_power_ctrl(void) { return 0; }
+
+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)
+{
+#if 0
+ 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);
+#endif
+}
+
+static void pm_vdec_pd_sec_api_power_off(struct device *dev, int id)
+{
+#if 0
+ 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);
+#endif
+}
+
+static bool pm_vdec_pd_sec_api_power_state(struct device *dev, int id)
+{
+#if 0
+ 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);
+#endif
+ return 0;
+}
+
+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);
+