// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include "iris_instance.h" #include "iris_vpu_common.h" #include "iris_vpu_register_defines.h" #define AON_WRAPPER_MVP_NOC_RESET_SYNCRST (AON_MVP_NOC_RESET + 0x08) #define CPU_CS_APV_BRIDGE_SYNC_RESET (CPU_BASE_OFFS + 0x174) #define MVP_NOC_RESET_REQ_MASK 0x70103 #define VPU_IDLE_BITS 0x7103 #define WRAPPER_EFUSE_MONITOR (WRAPPER_BASE_OFFS + 0x08) #define APV_CLK_HALT BIT(1) #define CORE_CLK_HALT BIT(0) #define CORE_PWR_ON BIT(1) #define DISABLE_VIDEO_APV_BIT BIT(27) #define DISABLE_VIDEO_VPP1_BIT BIT(28) #define DISABLE_VIDEO_VPP0_BIT BIT(29) static int iris_vpu4x_genpd_set_hwmode(struct iris_core *core, bool hw_mode, u32 efuse_value) { int ret; ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], hw_mode); if (ret) return ret; if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) { ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs [IRIS_VPP0_HW_POWER_DOMAIN], hw_mode); if (ret) goto restore_hw_domain_mode; } if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) { ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs [IRIS_VPP1_HW_POWER_DOMAIN], hw_mode); if (ret) goto restore_vpp0_domain_mode; } if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) { ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs [IRIS_APV_HW_POWER_DOMAIN], hw_mode); if (ret) goto restore_vpp1_domain_mode; } return 0; restore_vpp1_domain_mode: if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP1_HW_POWER_DOMAIN], !hw_mode); restore_vpp0_domain_mode: if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP0_HW_POWER_DOMAIN], !hw_mode); restore_hw_domain_mode: dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], !hw_mode); return ret; } static int iris_vpu4x_power_on_apv(struct iris_core *core) { int ret; ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]); if (ret) return ret; ret = iris_prepare_enable_clock(core, IRIS_APV_HW_CLK); if (ret) goto disable_apv_hw_power_domain; return 0; disable_apv_hw_power_domain: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]); return ret; } static void iris_vpu4x_power_off_apv(struct iris_core *core) { bool handshake_done, handshake_busy; u32 value, count = 0; int ret; value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); if (value & APV_CLK_HALT) writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); do { writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); usleep_range(10, 20); value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS); handshake_done = value & NOC_LPI_STATUS_DONE; handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE); if (handshake_done || !handshake_busy) break; writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); usleep_range(10, 20); } while (++count < 1000); if (!handshake_done && handshake_busy) dev_err(core->dev, "LPI handshake timeout\n"); writel(0x080200, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, value, value & 0x080200, 200, 2000); if (ret) goto disable_clocks_and_power; writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST); writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, value, value == 0x0, 200, 2000); if (ret) goto disable_clocks_and_power; writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); disable_clocks_and_power: iris_disable_unprepare_clock(core, IRIS_APV_HW_CLK); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]); } static void iris_vpu4x_ahb_sync_reset_apv(struct iris_core *core) { writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); } static void iris_vpu4x_ahb_sync_reset_hardware(struct iris_core *core) { writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); } static int iris_vpu4x_enable_hardware_clocks(struct iris_core *core, u32 efuse_value) { int ret; ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK); if (ret) return ret; ret = iris_prepare_enable_clock(core, IRIS_HW_FREERUN_CLK); if (ret) goto disable_axi_clock; ret = iris_prepare_enable_clock(core, IRIS_HW_CLK); if (ret) goto disable_hw_free_run_clock; ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK); if (ret) goto disable_hw_clock; if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) { ret = iris_prepare_enable_clock(core, IRIS_VPP0_HW_CLK); if (ret) goto disable_bse_hw_clock; } if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) { ret = iris_prepare_enable_clock(core, IRIS_VPP1_HW_CLK); if (ret) goto disable_vpp0_hw_clock; } return 0; disable_vpp0_hw_clock: if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK); disable_bse_hw_clock: iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); disable_hw_clock: iris_disable_unprepare_clock(core, IRIS_HW_CLK); disable_hw_free_run_clock: iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK); disable_axi_clock: iris_disable_unprepare_clock(core, IRIS_AXI_CLK); return ret; } static void iris_vpu4x_disable_hardware_clocks(struct iris_core *core, u32 efuse_value) { if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) iris_disable_unprepare_clock(core, IRIS_VPP1_HW_CLK); if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK); iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); iris_disable_unprepare_clock(core, IRIS_HW_CLK); iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK); iris_disable_unprepare_clock(core, IRIS_AXI_CLK); } static int iris_vpu4x_power_on_hardware(struct iris_core *core) { u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR); int ret; ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); if (ret) return ret; if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) { ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs [IRIS_VPP0_HW_POWER_DOMAIN]); if (ret) goto disable_hw_power_domain; } if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) { ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs [IRIS_VPP1_HW_POWER_DOMAIN]); if (ret) goto disable_vpp0_power_domain; } ret = iris_vpu4x_enable_hardware_clocks(core, efuse_value); if (ret) goto disable_vpp1_power_domain; if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) { ret = iris_vpu4x_power_on_apv(core); if (ret) goto disable_hw_clocks; iris_vpu4x_ahb_sync_reset_apv(core); } iris_vpu4x_ahb_sync_reset_hardware(core); ret = iris_vpu4x_genpd_set_hwmode(core, true, efuse_value); if (ret) goto disable_apv_power_domain; return 0; disable_apv_power_domain: if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) iris_vpu4x_power_off_apv(core); disable_hw_clocks: iris_vpu4x_disable_hardware_clocks(core, efuse_value); disable_vpp1_power_domain: if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs [IRIS_VPP1_HW_POWER_DOMAIN]); disable_vpp0_power_domain: if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs [IRIS_VPP0_HW_POWER_DOMAIN]); disable_hw_power_domain: iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); return ret; } static void iris_vpu4x_power_off_hardware(struct iris_core *core) { u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR); bool handshake_done, handshake_busy; u32 value, count = 0; int ret; iris_vpu4x_genpd_set_hwmode(core, false, efuse_value); if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) iris_vpu4x_power_off_apv(core); value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS); if (!(value & CORE_PWR_ON)) goto disable_clocks_and_power; value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); if (value & CORE_CLK_HALT) writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN, value, value & VPU_IDLE_BITS, 2000, 20000); do { writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); usleep_range(10, 20); value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS); handshake_done = value & NOC_LPI_STATUS_DONE; handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE); if (handshake_done || !handshake_busy) break; writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); usleep_range(10, 20); } while (++count < 1000); if (!handshake_done && handshake_busy) dev_err(core->dev, "LPI handshake timeout\n"); writel(MVP_NOC_RESET_REQ_MASK, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, value, value & MVP_NOC_RESET_REQ_MASK, 200, 2000); if (ret) goto disable_clocks_and_power; writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST); writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, value, value == 0x0, 200, 2000); if (ret) goto disable_clocks_and_power; writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); disable_clocks_and_power: iris_vpu4x_disable_hardware_clocks(core, efuse_value); if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs [IRIS_VPP1_HW_POWER_DOMAIN]); if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs [IRIS_VPP0_HW_POWER_DOMAIN]); iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); } const struct vpu_ops iris_vpu4x_ops = { .power_off_hw = iris_vpu4x_power_off_hardware, .power_on_hw = iris_vpu4x_power_on_hardware, .power_off_controller = iris_vpu35_vpu4x_power_off_controller, .power_on_controller = iris_vpu35_vpu4x_power_on_controller, .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers, .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, };