summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVikash Garodia <vikash.garodia@oss.qualcomm.com>2025-12-10 15:36:04 +0300
committerHans Verkuil <hverkuil+cisco@kernel.org>2026-01-20 18:22:24 +0300
commitdde659d37036c6edf5eeef81274b43fe12dfad53 (patch)
treea79d669afb93b7fc47c78d2605db8d65d4a3a2fb
parent82b96a8d8d6ebedcda8ef2d051ddd0f43d8eee14 (diff)
downloadlinux-dde659d37036c6edf5eeef81274b43fe12dfad53.tar.xz
media: iris: Introduce vpu ops for vpu4 with necessary hooks
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 <busanna.reddy@oss.qualcomm.com> Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com> Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com> Reviewed-by: Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com> Signed-off-by: Bryan O'Donoghue <bod@kernel.org> Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
-rw-r--r--drivers/media/platform/qcom/iris/Makefile1
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h7
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu4x.c369
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.h1
4 files changed, 378 insertions, 0 deletions
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);