-rw-r--r-- | drivers/frame_provider/decoder/utils/Makefile | 1 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/vdec.c | 689 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/vdec_power_ctrl.c | 801 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/vdec_power_ctrl.h | 107 | ||||
-rw-r--r-- | drivers/stream_input/amports/amstream.c | 1 |
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); + |