From 67585e1a0082a6ffe26569e2853c1b48f1e8f46b Mon Sep 17 00:00:00 2001 From: Nanxin Qin Date: Fri, 26 Jun 2020 07:19:35 +0000 Subject: vdec: support power manager for vdec. [1/1] 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: Ie7e8ab6ec15eb5175bfe28e134423489d5ceae15 Signed-off-by: Nanxin Qin --- diff --git a/drivers/frame_provider/decoder/utils/Makefile b/drivers/frame_provider/decoder/utils/Makefile index 07edbe3..4f23c05 100644 --- a/drivers/frame_provider/decoder/utils/Makefile +++ b/drivers/frame_provider/decoder/utils/Makefile @@ -5,4 +5,5 @@ decoder_common-objs += config_parser.o secprot.o vdec_profile.o 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_power_ctrl.o diff --git a/drivers/frame_provider/decoder/utils/vdec.c b/drivers/frame_provider/decoder/utils/vdec.c index a8a4b74..e219e44 100644 --- a/drivers/frame_provider/decoder/utils/vdec.c +++ b/drivers/frame_provider/decoder/utils/vdec.c @@ -83,12 +83,10 @@ #ifdef CONFIG_AMLOGIC_IONVIDEO #include #endif -#include -#include -#include - -/* wait other module to support this function */ -#define is_support_power_ctrl() 0 +//#include +#include +#include +#include "vdec_power_ctrl.h" static DEFINE_MUTEX(vdec_mutex); @@ -109,9 +107,11 @@ static unsigned int clk_config; */ static unsigned int debug; -static int hevc_max_reset_count; +int hevc_max_reset_count; +EXPORT_SYMBOL(hevc_max_reset_count); -static int no_powerdown; +int no_powerdown; +EXPORT_SYMBOL(no_powerdown); static int parallel_decode = 1; static int fps_detection; static int fps_clear; @@ -136,8 +136,6 @@ static int enable_mvdec_info = 1; int decode_underflow = 0; -static bool disable_power_domain; - #define CANVAS_MAX_SIZE (AMVDEC_CANVAS_MAX1 - AMVDEC_CANVAS_START_INDEX + 1 + AMVDEC_CANVAS_MAX2 + 1) struct am_reg { @@ -161,27 +159,6 @@ struct decode_fps_s { u32 fps; }; -enum vdec_pd_e { - PD_VDEC, - PD_HCODEC, - PD_HEVC, - PD_WAVE, - PD_MAX -}; - -struct vdec_pwrc_s { - u8 *name; - struct device *dev; - struct device_link *link; -}; - -static struct vdec_pwrc_s vdec_pd[] = { - { .name = "pwrc-vdec", }, - { .name = "pwrc-hcodec",}, - { .name = "pwrc-hevc", }, - { .name = "pwrc-wave", }, -}; - struct vdec_core_s { struct list_head connected_vdec_list; spinlock_t lock; @@ -210,7 +187,7 @@ struct vdec_core_s { struct decode_fps_s decode_fps[MAX_INSTANCE_MUN]; unsigned long buff_flag; unsigned long stream_buff_flag; - struct vdec_pwrc_s *pd; + struct power_manager_s *pm; }; struct canvas_status_s { @@ -862,13 +839,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; @@ -1071,8 +1041,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*/ @@ -3161,66 +3132,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 */ @@ -3267,17 +3178,9 @@ void vdec_power_reset(void) } EXPORT_SYMBOL(vdec_power_reset); -static void vdec_power_switch(struct vdec_pwrc_s *pd, int id, bool on); 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; @@ -3294,270 +3197,7 @@ void vdec_poweron(enum vdec_type_e core) return; } - /* power domain check. */ - if (!disable_power_domain && vdec_core->pd) { - int pd_id = 0; - - if (core == VDEC_1) { - amports_switch_gate("clk_vdec_mux", 1); - vdec_clock_hi_enable(); - pd_id = PD_VDEC; - } else if (core == VDEC_HCODEC) { - hcodec_clock_enable(); - pd_id = PD_HCODEC; - } else if (core == 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(); - pd_id = PD_HEVC; - } - - vdec_power_switch(vdec_core->pd, pd_id, true); - 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); } @@ -3565,8 +3205,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; @@ -3578,226 +3216,15 @@ void vdec_poweroff(enum vdec_type_e core) return; } - /* power domain check. */ - if (!disable_power_domain && vdec_core->pd) { - int pd_id = 0; - - if (core == VDEC_1) { - vdec_clock_off(); - pd_id = PD_VDEC; - } else if (core == VDEC_HCODEC) { - hcodec_clock_off(); - pd_id = PD_HCODEC; - } else if (core == VDEC_HEVC) { - /* disable hevc clock */ - hevc_clock_off(); - if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A) - hevc_back_clock_off(); - pd_id = PD_HEVC; - } - - vdec_power_switch(vdec_core->pd, pd_id, false); - 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; - - /* disable VDEC_1 DMC REQ*/ -#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); @@ -4989,65 +4416,6 @@ struct device *get_vdec_device(void) } EXPORT_SYMBOL(get_vdec_device); -static void vdec_power_switch(struct vdec_pwrc_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 vdec_power_domain_init(struct device *dev, - struct vdec_pwrc_s **pd_out) -{ - int i, err; - struct vdec_pwrc_s *pd = vdec_pd; - - for (i = 0; i < ARRAY_SIZE(vdec_pd); 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); - } - - *pd_out = pd; - - return 0; -} - -static void vdec_power_domain_remove(struct vdec_pwrc_s *pd) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(vdec_pd); 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 int vdec_probe(struct platform_device *pdev) { s32 i, r; @@ -5128,12 +4496,13 @@ static int vdec_probe(struct platform_device *pdev) WQ_MEM_RECLAIM |WQ_HIGHPRI/*high priority*/, "vdec-work"); /*work queue priority lower than vdec-core.*/ - /* init power domains. */ - if (of_property_read_bool(pdev->dev.of_node, "power-domains")) { - r = vdec_power_domain_init(&pdev->dev, &vdec_core->pd); + /* 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) { - vdec_power_domain_remove(vdec_core->pd); - pr_err("vdec power domain init failed\n"); + pr_err("vdec power manager init failed\n"); return r; } } @@ -5160,21 +4529,14 @@ static int vdec_remove(struct platform_device *pdev) destroy_workqueue(vdec_core->vdec_core_wq); - if (vdec_core->pd) - vdec_power_domain_remove(vdec_core->pd); + 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), @@ -5184,12 +4546,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, } }; @@ -5377,7 +4741,6 @@ module_param(fps_detection, int, 0664); module_param(fps_clear, int, 0664); module_param(force_nosecure_even_drm, int, 0664); module_param(disable_switch_single_to_mult, int, 0664); -module_param(disable_power_domain, bool, 0664); module_param(frameinfo_flag, int, 0664); MODULE_PARM_DESC(frameinfo_flag, 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 +#include +//#include +#include +#include +#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); + 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 index 0000000..e7ab77e --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec_power_ctrl.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + diff --git a/drivers/stream_input/amports/amstream.c b/drivers/stream_input/amports/amstream.c index b1c74ed..8f0139d 100644 --- a/drivers/stream_input/amports/amstream.c +++ b/drivers/stream_input/amports/amstream.c @@ -1336,7 +1336,6 @@ static void set_userdata_poc(struct userdata_poc_info_t poc) if (userdata_poc_wi == USERDATA_FIFO_NUM) userdata_poc_wi = 0; } -EXPORT_SYMBOL(set_userdata_poc); void init_userdata_fifo(void) { -- cgit