blob: e43c45a8a52e655e5f5f3fcd28bb1184dbd1af24 [file] [log] [blame]
/*
* 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/power_domain.h>
#include <dt-bindings/power/sc2-pd.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)
#define PDID_DSP 0
#define PDID_DOS_HCODEC 1
#define PDID_DOS_HEVC 2
#define PDID_DOS_VDEC 3
#define PDID_DOS_WAVE 4
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 */
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SC2 &&
(get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_T5) &&
(get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_T5D))
amports_switch_gate("clk_hevcf_mux", 1);
else
amports_switch_gate("clk_hevc_mux", 1);
if ((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A) &&
!is_hevc_front_back_clk_combined())
amports_switch_gate("clk_hevcb_mux", 1);
hevc_clock_hi_enable();
if (!is_hevc_front_back_clk_combined())
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) &&
!is_hevc_front_back_clk_combined())
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;
ulong decomp_dma_addr;
ulong mem_handle;
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(&mem_handle,
&decomp_dma_addr, SZ_64K + SZ_4K, "vdec_prealloc");
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(mem_handle);
}
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);