[PATCH 4/6] media: iris: add VPU5x hardware ops

From: Wangao Wang

Date: Fri Sep 18 2026 - 00:30:10 EST


The Hawi SoC uses the VPU5x generation of the Iris video hardware, which
requires handling for two new power domains — mm-int for the internal
memory interconnect and cx-int for the CX domain AXI interface — as well
as additional generation-specific clocks. Without a VPU5x vpu_ops
implementation the Iris driver cannot power on or off the hardware on
Hawi.

Move the shared idle and core power status definitions to the common
register header so they can be used by both VPU4x and VPU5x code.

Signed-off-by: Wangao Wang <wangao.wang@xxxxxxxxxxxxxxxx>
---
drivers/media/platform/qcom/iris/Makefile | 1 +
.../platform/qcom/iris/iris_platform_common.h | 6 +
drivers/media/platform/qcom/iris/iris_vpu4x.c | 4 -
drivers/media/platform/qcom/iris/iris_vpu5x.c | 588 +++++++++++++++++++++
drivers/media/platform/qcom/iris/iris_vpu_common.h | 1 +
.../platform/qcom/iris/iris_vpu_register_defines.h | 4 +
6 files changed, 600 insertions(+), 4 deletions(-)

diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile
index 48e415cbc4390bc596f6239fefa2a2ad2cd3a2bb..3ca4db45cb07323bdc18e5aba99ec7195087fe73 100644
--- a/drivers/media/platform/qcom/iris/Makefile
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -26,6 +26,7 @@ qcom-iris-objs += iris_buffer.o \
iris_vpu2.o \
iris_vpu3x.o \
iris_vpu4x.o \
+ iris_vpu5x.o \
iris_vpu_buffer.o \
iris_vpu_common.o \

diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index 9748095091461ba13443c63955a42906fa4f050c..193e6f293cda5462ecca9acc0ebc79c936b68bb4 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -76,6 +76,10 @@ enum platform_clk_type {
IRIS_VPP0_HW_CLK,
IRIS_VPP1_HW_CLK,
IRIS_APV_HW_CLK,
+ IRIS_AXIC_CLK,
+ IRIS_CTRL_DEBUG_CLK,
+ IRIS_VPP_GATING_CLK,
+ IRIS_CX_AXI_CLK,
};

struct platform_clk_data {
@@ -237,6 +241,8 @@ enum platform_pm_domain_type {
IRIS_VPP0_HW_POWER_DOMAIN,
IRIS_VPP1_HW_POWER_DOMAIN,
IRIS_APV_HW_POWER_DOMAIN,
+ IRIS_MM_INT_POWER_DOMAIN,
+ IRIS_CX_INT_POWER_DOMAIN,
};

struct iris_firmware_data {
diff --git a/drivers/media/platform/qcom/iris/iris_vpu4x.c b/drivers/media/platform/qcom/iris/iris_vpu4x.c
index 93acbfa0f471fdec1cb75db732f37ef6c6ef5cbf..267139a732d923b8e4939bc7d2932d06fac7a006 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu4x.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu4x.c
@@ -11,10 +11,6 @@
#include "iris_vpu_register_defines.h"

#define MVP_NOC_RESET_REQ_MASK 0x70103
-#define VPU_IDLE_BITS 0x7103
-
-#define CORE_CLK_HALT BIT(0)
-#define CORE_PWR_ON BIT(1)

static int iris_vpu4x_genpd_set_hwmode(struct iris_core *core, bool hw_mode, u32 efuse_value)
{
diff --git a/drivers/media/platform/qcom/iris/iris_vpu5x.c b/drivers/media/platform/qcom/iris/iris_vpu5x.c
new file mode 100644
index 0000000000000000000000000000000000000000..726c3f35ece4dd1abe4fd3916a5db699029d154e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu5x.c
@@ -0,0 +1,588 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/pm_opp.h>
+#include <linux/reset.h>
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
+/*
+ * iris5 specific register defines.
+ * Many base offsets are shared with prior generations:
+ * WRAPPER_BASE_OFFS = 0x000B0000
+ * AON_BASE_OFFS = 0x000E0000
+ * AON_MVP_NOC_RESET = 0x0001F000
+ * CPU_BASE_OFFS = 0x000A0000
+ */
+
+#define WRAPPER_MVP_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x110)
+#define WRAPPER_MVP_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x114)
+#define WRAPPER_MVP_NOC_CX_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x118)
+#define WRAPPER_MVP_NOC_CX_LPI_STATUS (WRAPPER_BASE_OFFS + 0x11C)
+
+#define AON_WRAPPER_MVP_NOC_ARCG_CONTROL (AON_BASE_OFFS + 0x10)
+
+#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL (AON_BASE_OFFS + 0x2C)
+#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS (AON_BASE_OFFS + 0x30)
+
+#define CPU_NOC_BASE_OFFS 0x000D0000
+#define CPU_NOC_ERRORLOGGER_MAINCTL_LOW (CPU_NOC_BASE_OFFS + 0x08)
+#define CPU_NOC_SBM_FAULTINEN0_LOW (CPU_NOC_BASE_OFFS + 0x240)
+
+#define NOC_BASE_OFFS 0x00010000
+#define NOC_ERL_ERRORLOGGER_MAINCTL_LOW (NOC_BASE_OFFS + 0xA008)
+#define NOC_ERL_ERRORLOGGER_ERRCLR_LOW (NOC_BASE_OFFS + 0xA018)
+#define NOC_SBM_FAULTINEN0_LOW (NOC_BASE_OFFS + 0x7040)
+
+#define NOC5_RESET_VPP0_ONLY_DISABLED 0x36010E
+#define NOC5_RESET_VPP1_DISABLED 0x35000F
+#define NOC5_RESET_ALL_VPP_ENABLED 0x37010F
+
+#define NOC_LPI_PD_QREQ BIT(0)
+#define NOC_SBM_FAULTINEN0_PORT0 BIT(0)
+#define NOC_ERRORLOGGER_MAINCTL_FAULTEN BIT(0)
+#define NOC_ERL_ERRORLOGGER_ERRCLR BIT(0)
+#define AON_MVP_NOC_ARCG_OVERRIDE BIT(0)
+
+#define WRAPPER_INTR_MASK_SS_CPU_NOC BIT(6)
+#define WRAPPER_INTR_MASK_SS_NOC BIT(5)
+
+static bool iris_vpu5x_hw_power_collapsed(struct iris_core *core)
+{
+ u32 value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
+
+ return !(value & CORE_PWR_ON);
+}
+
+static int iris_vpu5x_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;
+ }
+
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_MM_INT_POWER_DOMAIN],
+ hw_mode);
+ if (ret)
+ goto restore_apv_domain_mode;
+
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_CX_INT_POWER_DOMAIN],
+ hw_mode);
+ if (ret)
+ goto restore_mm_int_domain_mode;
+
+ return 0;
+
+restore_mm_int_domain_mode:
+ dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_MM_INT_POWER_DOMAIN], !hw_mode);
+
+restore_apv_domain_mode:
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT))
+ dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN],
+ !hw_mode);
+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_vpu5x_set_hwmode(struct iris_core *core)
+{
+ u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
+
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT))
+ iris_vpu4x_vpu5x_ahb_sync_reset_apv(core);
+
+ iris_vpu4x_vpu5x_ahb_sync_reset_hardware(core);
+
+ return iris_vpu5x_genpd_set_hwmode(core, true, efuse_value);
+}
+
+/*
+ * Perform an LPI power-down handshake on a single NOC interface.
+ *
+ * Sets QREQ and polls for the status DONE bit. On a busy response the request
+ * is toggled and, when @errclr_offs is non-zero, the NOC error-log clear
+ * register at that offset is written between retries to unblock the fabric.
+ * Pass @errclr_offs = 0 for interfaces that do not require error clearing.
+ */
+static void iris_vpu5x_noc_lpi_handshake(struct iris_core *core,
+ u32 ctrl_offs, u32 status_offs,
+ u32 busy_mask, u32 errclr_offs,
+ const char *name)
+{
+ bool handshake_done, handshake_busy;
+ u32 value, count = 0;
+ int ret;
+
+ do {
+ if (errclr_offs) {
+ value = readl(core->reg_base + errclr_offs);
+ value |= NOC_ERL_ERRORLOGGER_ERRCLR;
+ writel(value, core->reg_base + errclr_offs);
+ }
+
+ value = readl(core->reg_base + ctrl_offs);
+ value |= NOC_LPI_PD_QREQ;
+ writel(value, core->reg_base + ctrl_offs);
+ usleep_range(10, 20);
+
+ value = readl(core->reg_base + status_offs);
+ handshake_done = value & NOC_LPI_STATUS_DONE;
+ handshake_busy = value & busy_mask;
+
+ if (handshake_done || !handshake_busy)
+ break;
+
+ value = readl(core->reg_base + ctrl_offs);
+ value &= ~NOC_LPI_PD_QREQ;
+ writel(value, core->reg_base + ctrl_offs);
+ usleep_range(10, 20);
+
+ } while (++count < 1000);
+
+ if (!handshake_done && handshake_busy)
+ dev_err(core->dev, "%s NOC LPI handshake timeout\n", name);
+
+ ret = readl_poll_timeout(core->reg_base + status_offs,
+ value, value & NOC_LPI_STATUS_DONE, 200, 2000);
+ if (ret)
+ dev_err(core->dev, "%s NOC LPI status timeout\n", name);
+
+ value = readl(core->reg_base + ctrl_offs);
+ value &= ~NOC_LPI_PD_QREQ;
+ writel(value, core->reg_base + ctrl_offs);
+}
+
+static int iris_vpu5x_power_on_cx_int(struct iris_core *core)
+{
+ u32 value;
+ int ret;
+
+ ret = iris_enable_power_domains(core,
+ core->pmdomain_tbl->pd_devs[IRIS_CX_INT_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CX_AXI_CLK);
+ if (ret)
+ goto err_disable_cx_int_power;
+
+ value = readl(core->reg_base + WRAPPER_MVP_NOC_CX_LPI_CONTROL);
+ value &= ~NOC_LPI_PD_QREQ;
+ writel(value, core->reg_base + WRAPPER_MVP_NOC_CX_LPI_CONTROL);
+
+ readl_poll_timeout(core->reg_base + WRAPPER_MVP_NOC_CX_LPI_STATUS,
+ value, !(value & NOC_LPI_STATUS_DONE), 200, 2000);
+
+ return 0;
+
+err_disable_cx_int_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CX_INT_POWER_DOMAIN]);
+
+ return ret;
+}
+
+static void iris_vpu5x_power_off_cx_int(struct iris_core *core)
+{
+ iris_vpu5x_noc_lpi_handshake(core,
+ WRAPPER_MVP_NOC_CX_LPI_CONTROL,
+ WRAPPER_MVP_NOC_CX_LPI_STATUS,
+ NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE,
+ NOC_ERL_ERRORLOGGER_ERRCLR_LOW, "CX");
+
+ iris_disable_unprepare_clock(core, IRIS_CX_AXI_CLK);
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CX_INT_POWER_DOMAIN]);
+}
+
+static int iris_vpu5x_power_on_mm_int(struct iris_core *core)
+{
+ u32 value;
+ int ret;
+
+ ret = iris_enable_power_domains(core,
+ core->pmdomain_tbl->pd_devs[IRIS_MM_INT_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_HW_FREERUN_CLK);
+ if (ret)
+ goto err_disable_mm_int_power;
+
+ value = readl(core->reg_base + WRAPPER_MVP_NOC_LPI_CONTROL);
+ value &= ~NOC_LPI_PD_QREQ;
+ writel(value, core->reg_base + WRAPPER_MVP_NOC_LPI_CONTROL);
+
+ readl_poll_timeout(core->reg_base + WRAPPER_MVP_NOC_LPI_STATUS,
+ value, !(value & NOC_LPI_STATUS_DONE), 200, 2000);
+
+ return 0;
+
+err_disable_mm_int_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_MM_INT_POWER_DOMAIN]);
+
+ return ret;
+}
+
+static void iris_vpu5x_power_off_mm_int(struct iris_core *core)
+{
+ u32 value;
+
+ value = readl(core->reg_base + WRAPPER_MVP_NOC_LPI_CONTROL);
+ value &= ~NOC_LPI_PD_QREQ;
+ writel(value, core->reg_base + WRAPPER_MVP_NOC_LPI_CONTROL);
+
+ value = readl(core->reg_base + WRAPPER_MVP_NOC_CX_LPI_CONTROL);
+ value &= ~NOC_LPI_PD_QREQ;
+ writel(value, core->reg_base + WRAPPER_MVP_NOC_CX_LPI_CONTROL);
+
+ iris_vpu5x_noc_lpi_handshake(core,
+ WRAPPER_MVP_NOC_LPI_CONTROL,
+ WRAPPER_MVP_NOC_LPI_STATUS,
+ NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE,
+ NOC_ERL_ERRORLOGGER_ERRCLR_LOW, "MM");
+
+ iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK);
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_MM_INT_POWER_DOMAIN]);
+}
+
+static int iris_vpu5x_enable_hw_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_CLK);
+ if (ret)
+ goto err_disable_axi_clk;
+
+ ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK);
+ if (ret)
+ goto err_disable_hw_clk;
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) {
+ ret = iris_prepare_enable_clock(core, IRIS_VPP0_HW_CLK);
+ if (ret)
+ goto err_disable_bse_clk;
+ }
+
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) {
+ ret = iris_prepare_enable_clock(core, IRIS_VPP1_HW_CLK);
+ if (ret)
+ goto err_disable_vpp0_clk;
+ }
+
+ ret = iris_prepare_enable_clock(core, IRIS_VPP_GATING_CLK);
+ if (ret)
+ goto err_disable_vpp1_clk;
+
+ return 0;
+
+err_disable_vpp1_clk:
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
+ iris_disable_unprepare_clock(core, IRIS_VPP1_HW_CLK);
+err_disable_vpp0_clk:
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
+ iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK);
+err_disable_bse_clk:
+ iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK);
+err_disable_hw_clk:
+ iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+err_disable_axi_clk:
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+
+ return ret;
+}
+
+static void iris_vpu5x_disable_hw_clocks(struct iris_core *core, u32 efuse_value)
+{
+ iris_disable_unprepare_clock(core, IRIS_VPP_GATING_CLK);
+
+ 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_AXI_CLK);
+}
+
+static int iris_vpu5x_power_on_hardware(struct iris_core *core)
+{
+ u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
+ int ret;
+
+ ret = iris_vpu5x_power_on_cx_int(core);
+ if (ret)
+ return ret;
+
+ ret = iris_vpu5x_power_on_mm_int(core);
+ if (ret)
+ goto err_disable_cx_int;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ if (ret)
+ goto err_disable_mm_int;
+
+ 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 err_disable_hw_power;
+ }
+
+ 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 err_disable_vpp0_power;
+ }
+
+ ret = iris_vpu5x_enable_hw_clocks(core, efuse_value);
+ if (ret)
+ goto err_disable_vpp1_power;
+
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) {
+ ret = iris_vpu4x_vpu5x_power_on_apv(core);
+ if (ret)
+ goto err_disable_hw_clocks;
+ }
+
+ return 0;
+
+err_disable_hw_clocks:
+ iris_vpu5x_disable_hw_clocks(core, efuse_value);
+err_disable_vpp1_power:
+ if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT))
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP1_HW_POWER_DOMAIN]);
+err_disable_vpp0_power:
+ if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT))
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs
+ [IRIS_VPP0_HW_POWER_DOMAIN]);
+err_disable_hw_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+err_disable_mm_int:
+ iris_vpu5x_power_off_mm_int(core);
+err_disable_cx_int:
+ iris_vpu5x_power_off_cx_int(core);
+
+ return ret;
+}
+
+static void iris_vpu5x_power_off_hardware(struct iris_core *core)
+{
+ u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR);
+ u32 noc_reset_mask;
+ u32 value;
+ int ret;
+
+ iris_vpu5x_genpd_set_hwmode(core, false, efuse_value);
+
+ if (!(efuse_value & DISABLE_VIDEO_APV_BIT))
+ iris_vpu4x_vpu5x_power_off_apv(core);
+
+ if (iris_vpu5x_hw_power_collapsed(core))
+ goto disable_clocks_and_power;
+
+ value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+ if (value & CORE_CLK_HALT)
+ writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN,
+ value, (value & VPU_IDLE_BITS) == VPU_IDLE_BITS, 2000, 20000);
+
+ if (efuse_value & DISABLE_VIDEO_VPP0_BIT)
+ noc_reset_mask = NOC5_RESET_VPP0_ONLY_DISABLED;
+ else if (efuse_value & DISABLE_VIDEO_VPP1_BIT)
+ noc_reset_mask = NOC5_RESET_VPP1_DISABLED;
+ else
+ noc_reset_mask = NOC5_RESET_ALL_VPP_ENABLED;
+
+ writel(noc_reset_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 & noc_reset_mask) == noc_reset_mask, 200, 2000);
+ if (ret)
+ dev_err(core->dev, "MVP NOC reset ack timeout\n");
+
+ writel(noc_reset_mask, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST);
+ 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)
+ dev_err(core->dev, "MVP NOC reset deassert timeout\n");
+
+ 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_vpu5x_disable_hw_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]);
+ iris_vpu5x_power_off_mm_int(core);
+ iris_vpu5x_power_off_cx_int(core);
+}
+
+static int iris_vpu5x_power_on_controller(struct iris_core *core)
+{
+ u32 mask_val;
+ int ret;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
+ if (ret)
+ goto err_disable_ctrl_power;
+
+ ret = iris_prepare_enable_clock(core, IRIS_AXIC_CLK);
+ if (ret)
+ goto err_disable_ctrl_clk;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CTRL_FREERUN_CLK);
+ if (ret)
+ goto err_disable_axic_clk;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CTRL_DEBUG_CLK);
+ if (ret)
+ goto err_disable_ctrl_freerun_clk;
+
+ mask_val = readl(core->reg_base + CPU_NOC_SBM_FAULTINEN0_LOW);
+ mask_val |= NOC_SBM_FAULTINEN0_PORT0;
+ writel(mask_val, core->reg_base + CPU_NOC_SBM_FAULTINEN0_LOW);
+
+ mask_val = readl(core->reg_base + CPU_NOC_ERRORLOGGER_MAINCTL_LOW);
+ mask_val |= NOC_ERRORLOGGER_MAINCTL_FAULTEN;
+ writel(mask_val, core->reg_base + CPU_NOC_ERRORLOGGER_MAINCTL_LOW);
+
+ mask_val = readl(core->reg_base + WRAPPER_INTR_MASK);
+ mask_val &= ~WRAPPER_INTR_MASK_SS_CPU_NOC;
+ writel(mask_val, core->reg_base + WRAPPER_INTR_MASK);
+
+ mask_val = readl(core->reg_base + NOC_SBM_FAULTINEN0_LOW);
+ mask_val |= NOC_SBM_FAULTINEN0_PORT0;
+ writel(mask_val, core->reg_base + NOC_SBM_FAULTINEN0_LOW);
+
+ mask_val = readl(core->reg_base + NOC_ERL_ERRORLOGGER_MAINCTL_LOW);
+ mask_val |= NOC_ERRORLOGGER_MAINCTL_FAULTEN;
+ writel(mask_val, core->reg_base + NOC_ERL_ERRORLOGGER_MAINCTL_LOW);
+
+ mask_val = readl(core->reg_base + WRAPPER_INTR_MASK);
+ mask_val &= ~WRAPPER_INTR_MASK_SS_NOC;
+ writel(mask_val, core->reg_base + WRAPPER_INTR_MASK);
+
+ return 0;
+
+err_disable_ctrl_freerun_clk:
+ iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
+err_disable_axic_clk:
+ iris_disable_unprepare_clock(core, IRIS_AXIC_CLK);
+err_disable_ctrl_clk:
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+err_disable_ctrl_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ return ret;
+}
+
+static int iris_vpu5x_power_off_controller(struct iris_core *core)
+{
+ u32 ctrl_rst_tbl_size = core->iris_platform_data->controller_rst_tbl_size;
+ u32 value;
+
+ writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON,
+ core->reg_base + CPU_CS_X2RPMH);
+
+ iris_vpu5x_noc_lpi_handshake(core,
+ WRAPPER_IRIS_CPU_NOC_LPI_CONTROL,
+ WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ NOC_LPI_STATUS_DENY, 0, "CPU");
+
+ iris_vpu5x_noc_lpi_handshake(core,
+ AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL,
+ AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS,
+ NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE, 0, "Video CTL");
+
+ writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
+ readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
+ value, value == 0x0, 200, 2000);
+
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_ARCG_CONTROL);
+ value |= AON_MVP_NOC_ARCG_OVERRIDE;
+ writel(value, core->reg_base + AON_WRAPPER_MVP_NOC_ARCG_CONTROL);
+
+ iris_disable_unprepare_clock(core, IRIS_CTRL_DEBUG_CLK);
+ iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK);
+ iris_disable_unprepare_clock(core, IRIS_AXIC_CLK);
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+
+ reset_control_bulk_assert(ctrl_rst_tbl_size, core->controller_resets);
+ usleep_range(400, 500);
+ reset_control_bulk_deassert(ctrl_rst_tbl_size, core->controller_resets);
+
+ return 0;
+}
+
+const struct vpu_ops iris_vpu5x_ops = {
+ .power_off_hw = iris_vpu5x_power_off_hardware,
+ .power_on_hw = iris_vpu5x_power_on_hardware,
+ .power_off_controller = iris_vpu5x_power_off_controller,
+ .power_on_controller = iris_vpu5x_power_on_controller,
+ .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers,
+ .calc_freq = iris_vpu3x_vpu4x_calculate_frequency,
+ .set_hwmode = iris_vpu5x_set_hwmode,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h
index cc22c7459c4b77fdcb95b546fb8a55eea522bfb8..888b4b9407d0e0c7222448fe61bf8a41b669d2d6 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
@@ -13,6 +13,7 @@ extern const struct vpu_ops iris_vpu3_ops;
extern const struct vpu_ops iris_vpu33_ops;
extern const struct vpu_ops iris_vpu35_ops;
extern const struct vpu_ops iris_vpu4x_ops;
+extern const struct vpu_ops iris_vpu5x_ops;

struct vpu_ops {
void (*power_off_hw)(struct iris_core *core);
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
index 33f3d01cf79d763a62a2be9eac910a462bcd479b..3d08ea15eacb7ababaefd6e233b6232ce39f2cb5 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
@@ -85,4 +85,8 @@
#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)

+#define VPU_IDLE_BITS 0x7103
+#define CORE_CLK_HALT BIT(0)
+#define CORE_PWR_ON BIT(1)
+
#endif

--
2.43.0