On Wed Dec 10 18:06:04 2025 +0530, Vikash Garodia wrote:
> Add power sequence for vpu4 by reusing from previous generation wherever
> possible. Hook up vpu4 op with vpu4 specific implemtation or resue from
> earlier generation wherever feasible, like clock calculation in this
> case.
> 
> Co-developed-by: Vishnu Reddy <[email protected]>
> Signed-off-by: Vishnu Reddy <[email protected]>
> Signed-off-by: Vikash Garodia <[email protected]>
> Reviewed-by: Dikshita Agarwal <[email protected]>
> Signed-off-by: Bryan O'Donoghue <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/platform/qcom/iris/Makefile          |   1 +
 .../platform/qcom/iris/iris_platform_common.h      |   7 +
 drivers/media/platform/qcom/iris/iris_vpu4x.c      | 369 +++++++++++++++++++++
 drivers/media/platform/qcom/iris/iris_vpu_common.h |   1 +
 4 files changed, 378 insertions(+)

---

diff --git a/drivers/media/platform/qcom/iris/Makefile 
b/drivers/media/platform/qcom/iris/Makefile
index fad3be044e5f..2abbd3aeb4af 100644
--- a/drivers/media/platform/qcom/iris/Makefile
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -22,6 +22,7 @@ qcom-iris-objs += iris_buffer.o \
              iris_venc.o \
              iris_vpu2.o \
              iris_vpu3x.o \
+             iris_vpu4x.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 f215abcd36eb..5a489917580e 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -57,6 +57,10 @@ enum platform_clk_type {
        IRIS_AXI1_CLK,
        IRIS_CTRL_FREERUN_CLK,
        IRIS_HW_FREERUN_CLK,
+       IRIS_BSE_HW_CLK,
+       IRIS_VPP0_HW_CLK,
+       IRIS_VPP1_HW_CLK,
+       IRIS_APV_HW_CLK,
 };
 
 struct platform_clk_data {
@@ -203,6 +207,9 @@ struct icc_vote_data {
 enum platform_pm_domain_type {
        IRIS_CTRL_POWER_DOMAIN,
        IRIS_HW_POWER_DOMAIN,
+       IRIS_VPP0_HW_POWER_DOMAIN,
+       IRIS_VPP1_HW_POWER_DOMAIN,
+       IRIS_APV_HW_POWER_DOMAIN,
 };
 
 struct iris_platform_data {
diff --git a/drivers/media/platform/qcom/iris/iris_vpu4x.c 
b/drivers/media/platform/qcom/iris/iris_vpu4x.c
new file mode 100644
index 000000000000..a8db02ce5c5e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu4x.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#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,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h 
b/drivers/media/platform/qcom/iris/iris_vpu_common.h
index 7cf4304604cc..f6dffc613b82 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
@@ -12,6 +12,7 @@ extern const struct vpu_ops iris_vpu2_ops;
 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;
 
 struct vpu_ops {
        void (*power_off_hw)(struct iris_core *core);
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to