On Fri Feb 7 13:24:48 2025 +0530, Dikshita Agarwal wrote:
> Implement runtime power management for iris, including a platform
> specific power on/off sequence.
> 
> Tested-by: Stefan Schmidt <stefan.schm...@linaro.org> # x1e80100 (Dell XPS 13 
> 9345)
> Reviewed-by: Stefan Schmidt <stefan.schm...@linaro.org>
> Tested-by: Neil Armstrong <neil.armstr...@linaro.org> # on SM8550-QRD
> Tested-by: Neil Armstrong <neil.armstr...@linaro.org> # on SM8550-HDK
> Signed-off-by: Dikshita Agarwal <quic_diksh...@quicinc.com>
> Signed-off-by: Hans Verkuil <hverk...@xs4all.nl>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/platform/qcom/iris/Makefile          |   3 +
 drivers/media/platform/qcom/iris/iris_core.c       |  15 +-
 drivers/media/platform/qcom/iris/iris_core.h       |   4 +
 drivers/media/platform/qcom/iris/iris_firmware.c   |   5 +
 drivers/media/platform/qcom/iris/iris_firmware.h   |   1 +
 drivers/media/platform/qcom/iris/iris_hfi_common.c |  62 ++++++
 drivers/media/platform/qcom/iris/iris_hfi_common.h |   3 +
 .../platform/qcom/iris/iris_hfi_gen1_command.c     |  11 +
 .../platform/qcom/iris/iris_hfi_gen1_defines.h     |   5 +
 .../platform/qcom/iris/iris_hfi_gen2_command.c     |  18 ++
 .../platform/qcom/iris/iris_hfi_gen2_defines.h     |   1 +
 .../platform/qcom/iris/iris_hfi_gen2_packet.c      |  13 ++
 .../platform/qcom/iris/iris_hfi_gen2_packet.h      |   1 +
 drivers/media/platform/qcom/iris/iris_hfi_queue.c  |  18 ++
 .../platform/qcom/iris/iris_platform_common.h      |  14 ++
 .../platform/qcom/iris/iris_platform_sm8550.c      |   9 +
 drivers/media/platform/qcom/iris/iris_probe.c      |  53 +++++
 drivers/media/platform/qcom/iris/iris_resources.c  | 131 +++++++++++
 drivers/media/platform/qcom/iris/iris_resources.h  |  18 ++
 drivers/media/platform/qcom/iris/iris_vidc.c       |   8 +
 drivers/media/platform/qcom/iris/iris_vpu2.c       |  11 +
 drivers/media/platform/qcom/iris/iris_vpu3.c       |  84 +++++++
 drivers/media/platform/qcom/iris/iris_vpu_common.c | 243 ++++++++++++++++++++-
 drivers/media/platform/qcom/iris/iris_vpu_common.h |  11 +
 .../platform/qcom/iris/iris_vpu_register_defines.h |  17 ++
 25 files changed, 755 insertions(+), 4 deletions(-)

---

diff --git a/drivers/media/platform/qcom/iris/Makefile 
b/drivers/media/platform/qcom/iris/Makefile
index 76ca5287c49f..a5f290a8c4af 100644
--- a/drivers/media/platform/qcom/iris/Makefile
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -9,7 +9,10 @@ iris-objs += iris_core.o \
              iris_hfi_queue.o \
              iris_platform_sm8550.o \
              iris_probe.o \
+             iris_resources.o \
              iris_vidc.o \
+             iris_vpu2.o \
+             iris_vpu3.o \
              iris_vpu_common.o \
 
 obj-$(CONFIG_VIDEO_QCOM_IRIS) += iris.o
diff --git a/drivers/media/platform/qcom/iris/iris_core.c 
b/drivers/media/platform/qcom/iris/iris_core.c
index 7e19bdd0a19b..0fa0a3b549a2 100644
--- a/drivers/media/platform/qcom/iris/iris_core.c
+++ b/drivers/media/platform/qcom/iris/iris_core.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
  */
 
+#include <linux/pm_runtime.h>
+
 #include "iris_core.h"
 #include "iris_firmware.h"
 #include "iris_state.h"
@@ -10,11 +12,16 @@
 
 void iris_core_deinit(struct iris_core *core)
 {
+       pm_runtime_resume_and_get(core->dev);
+
        mutex_lock(&core->lock);
        iris_fw_unload(core);
+       iris_vpu_power_off(core);
        iris_hfi_queues_deinit(core);
        core->state = IRIS_CORE_DEINIT;
        mutex_unlock(&core->lock);
+
+       pm_runtime_put_sync(core->dev);
 }
 
 static int iris_wait_for_system_response(struct iris_core *core)
@@ -54,10 +61,14 @@ int iris_core_init(struct iris_core *core)
        if (ret)
                goto error;
 
-       ret = iris_fw_load(core);
+       ret = iris_vpu_power_on(core);
        if (ret)
                goto error_queue_deinit;
 
+       ret = iris_fw_load(core);
+       if (ret)
+               goto error_power_off;
+
        ret = iris_vpu_boot_firmware(core);
        if (ret)
                goto error_unload_fw;
@@ -72,6 +83,8 @@ int iris_core_init(struct iris_core *core)
 
 error_unload_fw:
        iris_fw_unload(core);
+error_power_off:
+       iris_vpu_power_off(core);
 error_queue_deinit:
        iris_hfi_queues_deinit(core);
 error:
diff --git a/drivers/media/platform/qcom/iris/iris_core.h 
b/drivers/media/platform/qcom/iris/iris_core.h
index c0f3c189d779..58aab78ab2c4 100644
--- a/drivers/media/platform/qcom/iris/iris_core.h
+++ b/drivers/media/platform/qcom/iris/iris_core.h
@@ -7,11 +7,13 @@
 #define __IRIS_CORE_H__
 
 #include <linux/types.h>
+#include <linux/pm_domain.h>
 #include <media/v4l2-device.h>
 
 #include "iris_hfi_common.h"
 #include "iris_hfi_queue.h"
 #include "iris_platform_common.h"
+#include "iris_resources.h"
 #include "iris_state.h"
 
 struct icc_info {
@@ -52,6 +54,7 @@ struct icc_info {
  * @response_packet: a pointer to response packet from fw to driver
  * @header_id: id of packet header
  * @packet_id: id of packet
+ * @power: a structure for clock and bw information
  * @hfi_ops: iris hfi command ops
  * @hfi_response_ops: iris hfi response ops
  * @core_init_done: structure of signal completion for system response
@@ -86,6 +89,7 @@ struct iris_core {
        u8                                      *response_packet;
        u32                                     header_id;
        u32                                     packet_id;
+       struct iris_core_power                  power;
        const struct iris_hfi_command_ops       *hfi_ops;
        const struct iris_hfi_response_ops      *hfi_response_ops;
        struct completion                       core_init_done;
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c 
b/drivers/media/platform/qcom/iris/iris_firmware.c
index 3d14e596a471..7c493b4a75db 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -109,3 +109,8 @@ int iris_fw_unload(struct iris_core *core)
 {
        return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
 }
+
+int iris_set_hw_state(struct iris_core *core, bool resume)
+{
+       return qcom_scm_set_remote_state(resume, 0);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.h 
b/drivers/media/platform/qcom/iris/iris_firmware.h
index 266bdd92a124..e833ecd34887 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.h
+++ b/drivers/media/platform/qcom/iris/iris_firmware.h
@@ -10,5 +10,6 @@ struct iris_core;
 
 int iris_fw_load(struct iris_core *core);
 int iris_fw_unload(struct iris_core *core);
+int iris_set_hw_state(struct iris_core *core, bool resume);
 
 #endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c 
b/drivers/media/platform/qcom/iris/iris_hfi_common.c
index a19b988c9a88..29f56c2bf74c 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c
@@ -3,6 +3,9 @@
  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
  */
 
+#include <linux/pm_runtime.h>
+
+#include "iris_firmware.h"
 #include "iris_core.h"
 #include "iris_hfi_common.h"
 #include "iris_vpu_common.h"
@@ -38,6 +41,7 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data)
                return IRQ_NONE;
 
        mutex_lock(&core->lock);
+       pm_runtime_mark_last_busy(core->dev);
        iris_vpu_clear_interrupt(core);
        mutex_unlock(&core->lock);
 
@@ -48,3 +52,61 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data)
 
        return IRQ_HANDLED;
 }
+
+int iris_hfi_pm_suspend(struct iris_core *core)
+{
+       int ret;
+
+       ret = iris_vpu_prepare_pc(core);
+       if (ret) {
+               pm_runtime_mark_last_busy(core->dev);
+               ret = -EAGAIN;
+               goto error;
+       }
+
+       ret = iris_set_hw_state(core, false);
+       if (ret)
+               goto error;
+
+       iris_vpu_power_off(core);
+
+       return 0;
+
+error:
+       dev_err(core->dev, "failed to suspend\n");
+
+       return ret;
+}
+
+int iris_hfi_pm_resume(struct iris_core *core)
+{
+       const struct iris_hfi_command_ops *ops = core->hfi_ops;
+       int ret;
+
+       ret = iris_vpu_power_on(core);
+       if (ret)
+               goto error;
+
+       ret = iris_set_hw_state(core, true);
+       if (ret)
+               goto err_power_off;
+
+       ret = iris_vpu_boot_firmware(core);
+       if (ret)
+               goto err_suspend_hw;
+
+       ret = ops->sys_interframe_powercollapse(core);
+       if (ret)
+               goto err_suspend_hw;
+
+       return 0;
+
+err_suspend_hw:
+       iris_set_hw_state(core, false);
+err_power_off:
+       iris_vpu_power_off(core);
+error:
+       dev_err(core->dev, "failed to resume\n");
+
+       return -EBUSY;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h 
b/drivers/media/platform/qcom/iris/iris_hfi_common.h
index b46a2f21102a..36673aafe1c9 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -46,6 +46,7 @@ struct iris_hfi_command_ops {
        int (*sys_init)(struct iris_core *core);
        int (*sys_image_version)(struct iris_core *core);
        int (*sys_interframe_powercollapse)(struct iris_core *core);
+       int (*sys_pc_prep)(struct iris_core *core);
 };
 
 struct iris_hfi_response_ops {
@@ -53,6 +54,8 @@ struct iris_hfi_response_ops {
 };
 
 int iris_hfi_core_init(struct iris_core *core);
+int iris_hfi_pm_suspend(struct iris_core *core);
+int iris_hfi_pm_resume(struct iris_core *core);
 
 irqreturn_t iris_hfi_isr(int irq, void *data);
 irqreturn_t iris_hfi_isr_handler(int irq, void *data);
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c 
b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
index 07007d8812ba..b2e76d1dcbf7 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -56,10 +56,21 @@ static int 
iris_hfi_gen1_sys_interframe_powercollapse(struct iris_core *core)
        return ret;
 }
 
+static int iris_hfi_gen1_sys_pc_prep(struct iris_core *core)
+{
+       struct hfi_sys_pc_prep_pkt pkt;
+
+       pkt.hdr.size = sizeof(struct hfi_sys_pc_prep_pkt);
+       pkt.hdr.pkt_type = HFI_CMD_SYS_PC_PREP;
+
+       return iris_hfi_queue_cmd_write_locked(core, &pkt, pkt.hdr.size);
+}
+
 static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = {
        .sys_init = iris_hfi_gen1_sys_init,
        .sys_image_version = iris_hfi_gen1_sys_image_version,
        .sys_interframe_powercollapse = 
iris_hfi_gen1_sys_interframe_powercollapse,
+       .sys_pc_prep = iris_hfi_gen1_sys_pc_prep,
 };
 
 void iris_hfi_gen1_command_ops_init(struct iris_core *core)
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h 
b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
index 8af824a42bcf..81685a284f23 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -12,6 +12,7 @@
 #define HFI_ERR_NONE                                   0x0
 
 #define HFI_CMD_SYS_INIT                               0x10001
+#define HFI_CMD_SYS_PC_PREP                            0x10002
 #define HFI_CMD_SYS_SET_PROPERTY                       0x10005
 #define HFI_CMD_SYS_GET_PROPERTY                       0x10006
 
@@ -48,6 +49,10 @@ struct hfi_sys_get_property_pkt {
        u32 data;
 };
 
+struct hfi_sys_pc_prep_pkt {
+       struct hfi_pkt_hdr hdr;
+};
+
 struct hfi_msg_event_notify_pkt {
        struct hfi_pkt_hdr hdr;
        u32 event_id;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c 
b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
index 5eaebe170214..f8cb1177ef54 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -68,10 +68,28 @@ static int 
iris_hfi_gen2_sys_interframe_powercollapse(struct iris_core *core)
        return ret;
 }
 
+static int iris_hfi_gen2_sys_pc_prep(struct iris_core *core)
+{
+       struct iris_hfi_header *hdr;
+       int ret;
+
+       hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL);
+       if (!hdr)
+               return -ENOMEM;
+
+       iris_hfi_gen2_packet_sys_pc_prep(core, hdr);
+       ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+       kfree(hdr);
+
+       return ret;
+}
+
 static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = {
        .sys_init = iris_hfi_gen2_sys_init,
        .sys_image_version = iris_hfi_gen2_sys_image_version,
        .sys_interframe_powercollapse = 
iris_hfi_gen2_sys_interframe_powercollapse,
+       .sys_pc_prep = iris_hfi_gen2_sys_pc_prep,
 };
 
 void iris_hfi_gen2_command_ops_init(struct iris_core *core)
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h 
b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
index 2640caa7f9c0..e6a19ffc12fb 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -12,6 +12,7 @@
 
 #define HFI_CMD_BEGIN                          0x01000000
 #define HFI_CMD_INIT                           0x01000001
+#define HFI_CMD_POWER_COLLAPSE                 0x01000002
 #define HFI_CMD_END                            0x01FFFFFF
 
 #define HFI_PROP_BEGIN                         0x03000000
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c 
b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
index 986013aa62df..510d44408b41 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
@@ -159,3 +159,16 @@ void 
iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
                                    &payload,
                                    sizeof(u32));
 }
+
+void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct 
iris_hfi_header *hdr)
+{
+       iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++);
+
+       iris_hfi_gen2_create_packet(hdr,
+                                   HFI_CMD_POWER_COLLAPSE,
+                                   HFI_HOST_FLAGS_NONE,
+                                   HFI_PAYLOAD_NONE,
+                                   HFI_PORT_NONE,
+                                   core->packet_id++,
+                                   NULL, 0);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h 
b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
index 10dcb6e4c3d9..3b771b7516de 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
@@ -65,5 +65,6 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, 
struct iris_hfi_heade
 void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct 
iris_hfi_header *hdr);
 void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
                                                       struct iris_hfi_header 
*hdr);
+void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct 
iris_hfi_header *hdr);
 
 #endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c 
b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
index ee245c540ce7..fac7df0c4d1a 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_queue.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
@@ -3,6 +3,8 @@
  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
  */
 
+#include <linux/pm_runtime.h>
+
 #include "iris_core.h"
 #include "iris_hfi_queue.h"
 #include "iris_vpu_common.h"
@@ -128,10 +130,26 @@ int iris_hfi_queue_cmd_write(struct iris_core *core, void 
*pkt, u32 pkt_size)
 {
        int ret;
 
+       ret = pm_runtime_resume_and_get(core->dev);
+       if (ret < 0)
+               goto exit;
+
        mutex_lock(&core->lock);
        ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt_size);
+       if (ret) {
+               mutex_unlock(&core->lock);
+               goto exit;
+       }
        mutex_unlock(&core->lock);
 
+       pm_runtime_mark_last_busy(core->dev);
+       pm_runtime_put_autosuspend(core->dev);
+
+       return 0;
+
+exit:
+       pm_runtime_put_sync(core->dev);
+
        return ret;
 }
 
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h 
b/drivers/media/platform/qcom/iris/iris_platform_common.h
index adf639d1a109..69c0a8b3d12d 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -10,6 +10,7 @@ struct iris_core;
 
 #define IRIS_PAS_ID                            9
 #define HW_RESPONSE_TIMEOUT_VALUE               (1000) /* milliseconds */
+#define AUTOSUSPEND_DELAY_VALUE                        
(HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */
 
 extern struct iris_platform_data sm8550_data;
 
@@ -41,10 +42,22 @@ struct ubwc_config_data {
        u32     bank_spreading;
 };
 
+struct iris_core_power {
+       u64 clk_freq;
+       u64 icc_bw;
+};
+
+enum platform_pm_domain_type {
+       IRIS_CTRL_POWER_DOMAIN,
+       IRIS_HW_POWER_DOMAIN,
+};
+
 struct iris_platform_data {
        void (*init_hfi_command_ops)(struct iris_core *core);
        void (*init_hfi_response_ops)(struct iris_core *core);
        struct iris_inst *(*get_instance)(void);
+       const struct vpu_ops *vpu_ops;
+       void (*set_preset_registers)(struct iris_core *core);
        const struct icc_info *icc_tbl;
        unsigned int icc_tbl_size;
        const char * const *pmdomain_tbl;
@@ -62,6 +75,7 @@ struct iris_platform_data {
        u32 core_arch;
        u32 hw_response_timeout;
        struct ubwc_config_data *ubwc_config;
+       u32 num_vpp_pipe;
 };
 
 #endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.c 
b/drivers/media/platform/qcom/iris/iris_platform_sm8550.c
index 30d9664bd419..ed99cdb13d06 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_sm8550.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8550.c
@@ -6,9 +6,15 @@
 #include "iris_core.h"
 #include "iris_hfi_gen2.h"
 #include "iris_platform_common.h"
+#include "iris_vpu_common.h"
 
 #define VIDEO_ARCH_LX 1
 
+static void iris_set_sm8550_preset_registers(struct iris_core *core)
+{
+       writel(0x0, core->reg_base + 0xB0088);
+}
+
 static const struct icc_info sm8550_icc_table[] = {
        { "cpu-cfg",    1000, 1000     },
        { "video-mem",  1000, 15000000 },
@@ -47,6 +53,8 @@ struct iris_platform_data sm8550_data = {
        .get_instance = iris_hfi_gen2_get_instance,
        .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
        .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+       .vpu_ops = &iris_vpu3_ops,
+       .set_preset_registers = iris_set_sm8550_preset_registers,
        .icc_tbl = sm8550_icc_table,
        .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
        .clk_rst_tbl = sm8550_clk_reset_table,
@@ -65,4 +73,5 @@ struct iris_platform_data sm8550_data = {
        .core_arch = VIDEO_ARCH_LX,
        .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
        .ubwc_config = &ubwc_config_sm8550,
+       .num_vpp_pipe = 4,
 };
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c 
b/drivers/media/platform/qcom/iris/iris_probe.c
index 02887b3dbe0e..e8ef258b4f2e 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
 #include "iris_core.h"
@@ -252,6 +253,12 @@ static int iris_probe(struct platform_device *pdev)
        dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
        dma_set_seg_boundary(&pdev->dev, DMA_BIT_MASK(32));
 
+       pm_runtime_set_autosuspend_delay(core->dev, AUTOSUSPEND_DELAY_VALUE);
+       pm_runtime_use_autosuspend(core->dev);
+       ret = devm_pm_runtime_enable(core->dev);
+       if (ret)
+               goto err_vdev_unreg;
+
        return 0;
 
 err_vdev_unreg:
@@ -262,6 +269,51 @@ err_v4l2_unreg:
        return ret;
 }
 
+static int __maybe_unused iris_pm_suspend(struct device *dev)
+{
+       struct iris_core *core;
+       int ret = 0;
+
+       core = dev_get_drvdata(dev);
+
+       mutex_lock(&core->lock);
+       if (core->state != IRIS_CORE_INIT)
+               goto exit;
+
+       ret = iris_hfi_pm_suspend(core);
+
+exit:
+       mutex_unlock(&core->lock);
+
+       return ret;
+}
+
+static int __maybe_unused iris_pm_resume(struct device *dev)
+{
+       struct iris_core *core;
+       int ret = 0;
+
+       core = dev_get_drvdata(dev);
+
+       mutex_lock(&core->lock);
+       if (core->state != IRIS_CORE_INIT)
+               goto exit;
+
+       ret = iris_hfi_pm_resume(core);
+       pm_runtime_mark_last_busy(core->dev);
+
+exit:
+       mutex_unlock(&core->lock);
+
+       return ret;
+}
+
+static const struct dev_pm_ops iris_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
+       SET_RUNTIME_PM_OPS(iris_pm_suspend, iris_pm_resume, NULL)
+};
+
 static const struct of_device_id iris_dt_match[] = {
        {
                .compatible = "qcom,sm8550-iris",
@@ -277,6 +329,7 @@ static struct platform_driver qcom_iris_driver = {
        .driver = {
                .name = "qcom-iris",
                .of_match_table = iris_dt_match,
+               .pm = &iris_pm_ops,
        },
 };
 
diff --git a/drivers/media/platform/qcom/iris/iris_resources.c 
b/drivers/media/platform/qcom/iris/iris_resources.c
new file mode 100644
index 000000000000..cf32f268b703
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_resources.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/interconnect.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "iris_core.h"
+#include "iris_resources.h"
+
+#define BW_THRESHOLD 50000
+
+int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw)
+{
+       unsigned long bw_kbps = 0, bw_prev = 0;
+       const struct icc_info *icc_tbl;
+       int ret = 0, i;
+
+       icc_tbl = core->iris_platform_data->icc_tbl;
+
+       for (i = 0; i < core->icc_count; i++) {
+               if (!strcmp(core->icc_tbl[i].name, "video-mem")) {
+                       bw_kbps = icc_bw;
+                       bw_prev = core->power.icc_bw;
+
+                       bw_kbps = clamp_t(typeof(bw_kbps), bw_kbps,
+                                         icc_tbl[i].bw_min_kbps, 
icc_tbl[i].bw_max_kbps);
+
+                       if (abs(bw_kbps - bw_prev) < BW_THRESHOLD && bw_prev)
+                               return ret;
+
+                       core->icc_tbl[i].avg_bw = bw_kbps;
+
+                       core->power.icc_bw = bw_kbps;
+                       break;
+               }
+       }
+
+       return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
+}
+
+int iris_unset_icc_bw(struct iris_core *core)
+{
+       u32 i;
+
+       core->power.icc_bw = 0;
+
+       for (i = 0; i < core->icc_count; i++) {
+               core->icc_tbl[i].avg_bw = 0;
+               core->icc_tbl[i].peak_bw = 0;
+       }
+
+       return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
+}
+
+int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev)
+{
+       int ret;
+
+       ret = dev_pm_opp_set_rate(core->dev, ULONG_MAX);
+       if (ret)
+               return ret;
+
+       ret = pm_runtime_get_sync(pd_dev);
+       if (ret < 0)
+               return ret;
+
+       return ret;
+}
+
+int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev)
+{
+       int ret;
+
+       ret = dev_pm_opp_set_rate(core->dev, 0);
+       if (ret)
+               return ret;
+
+       pm_runtime_put_sync(pd_dev);
+
+       return 0;
+}
+
+static struct clk *iris_get_clk_by_type(struct iris_core *core, enum 
platform_clk_type clk_type)
+{
+       const struct platform_clk_data *clk_tbl;
+       u32 clk_cnt, i, j;
+
+       clk_tbl = core->iris_platform_data->clk_tbl;
+       clk_cnt = core->iris_platform_data->clk_tbl_size;
+
+       for (i = 0; i < clk_cnt; i++) {
+               if (clk_tbl[i].clk_type == clk_type) {
+                       for (j = 0; core->clock_tbl && j < core->clk_count; 
j++) {
+                               if (!strcmp(core->clock_tbl[j].id, 
clk_tbl[i].clk_name))
+                                       return core->clock_tbl[j].clk;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type 
clk_type)
+{
+       struct clk *clock;
+
+       clock = iris_get_clk_by_type(core, clk_type);
+       if (!clock)
+               return -EINVAL;
+
+       return clk_prepare_enable(clock);
+}
+
+int iris_disable_unprepare_clock(struct iris_core *core, enum 
platform_clk_type clk_type)
+{
+       struct clk *clock;
+
+       clock = iris_get_clk_by_type(core, clk_type);
+       if (!clock)
+               return -EINVAL;
+
+       clk_disable_unprepare(clock);
+
+       return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_resources.h 
b/drivers/media/platform/qcom/iris/iris_resources.h
new file mode 100644
index 000000000000..f723dfe5bd81
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_resources.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
+ */
+
+#ifndef __IRIS_RESOURCES_H__
+#define __IRIS_RESOURCES_H__
+
+struct iris_core;
+
+int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev);
+int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev);
+int iris_unset_icc_bw(struct iris_core *core);
+int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw);
+int iris_disable_unprepare_clock(struct iris_core *core, enum 
platform_clk_type clk_type);
+int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type 
clk_type);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c 
b/drivers/media/platform/qcom/iris/iris_vidc.c
index 5dd0ccbaa2fb..b8654e73f516 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -3,6 +3,7 @@
  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
  */
 
+#include <linux/pm_runtime.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-mem2mem.h>
 
@@ -81,12 +82,19 @@ int iris_open(struct file *filp)
        struct iris_inst *inst;
        int ret;
 
+       ret = pm_runtime_resume_and_get(core->dev);
+       if (ret < 0)
+               return ret;
+
        ret = iris_core_init(core);
        if (ret) {
                dev_err(core->dev, "core init failed\n");
+               pm_runtime_put_sync(core->dev);
                return ret;
        }
 
+       pm_runtime_put_sync(core->dev);
+
        inst = core->iris_platform_data->get_instance();
        if (!inst)
                return -ENOMEM;
diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c 
b/drivers/media/platform/qcom/iris/iris_vpu2.c
new file mode 100644
index 000000000000..bd8427411576
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu2.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
+ */
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+
+const struct vpu_ops iris_vpu2_ops = {
+       .power_off_hw = iris_vpu_power_off_hw,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu3.c 
b/drivers/media/platform/qcom/iris/iris_vpu3.c
new file mode 100644
index 000000000000..10599f1fa789
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu3.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
+ */
+
+#include <linux/iopoll.h>
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
+#define AON_MVP_NOC_RESET                      0x0001F000
+
+#define WRAPPER_CORE_CLOCK_CONFIG              (WRAPPER_BASE_OFFS + 0x88)
+#define CORE_CLK_RUN                           0x0
+
+#define CPU_CS_AHB_BRIDGE_SYNC_RESET           (CPU_CS_BASE_OFFS + 0x160)
+#define CORE_BRIDGE_SW_RESET                   BIT(0)
+#define CORE_BRIDGE_HW_RESET_DISABLE           BIT(1)
+
+#define AON_WRAPPER_MVP_NOC_RESET_REQ          (AON_MVP_NOC_RESET + 0x000)
+#define VIDEO_NOC_RESET_REQ                    (BIT(0) | BIT(1))
+
+#define AON_WRAPPER_MVP_NOC_RESET_ACK          (AON_MVP_NOC_RESET + 0x004)
+
+#define VCODEC_SS_IDLE_STATUSN                 (VCODEC_BASE_OFFS + 0x70)
+
+static bool iris_vpu3_hw_power_collapsed(struct iris_core *core)
+{
+       u32 value, pwr_status;
+
+       value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
+       pwr_status = value & BIT(1);
+
+       return pwr_status ? false : true;
+}
+
+static void iris_vpu3_power_off_hardware(struct iris_core *core)
+{
+       u32 reg_val = 0, value, i;
+       int ret;
+
+       if (iris_vpu3_hw_power_collapsed(core))
+               goto disable_power;
+
+       dev_err(core->dev, "video hw is power on\n");
+
+       value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+       if (value)
+               writel(CORE_CLK_RUN, core->reg_base + 
WRAPPER_CORE_CLOCK_CONFIG);
+
+       for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
+               ret = readl_poll_timeout(core->reg_base + 
VCODEC_SS_IDLE_STATUSN + 4 * i,
+                                        reg_val, reg_val & 0x400000, 2000, 
20000);
+               if (ret)
+                       goto disable_power;
+       }
+
+       writel(VIDEO_NOC_RESET_REQ, core->reg_base + 
AON_WRAPPER_MVP_NOC_RESET_REQ);
+
+       ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+                                reg_val, reg_val & 0x3, 200, 2000);
+       if (ret)
+               goto disable_power;
+
+       writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+
+       ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+                                reg_val, !(reg_val & 0x3), 200, 2000);
+       if (ret)
+               goto disable_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_power:
+       iris_vpu_power_off_hw(core);
+}
+
+const struct vpu_ops iris_vpu3_ops = {
+       .power_off_hw = iris_vpu3_power_off_hardware,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c 
b/drivers/media/platform/qcom/iris/iris_vpu_common.c
index 34817573f61b..fe9896d66848 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -4,13 +4,16 @@
  */
 
 #include <linux/iopoll.h>
+#include <linux/pm_opp.h>
+#include <linux/reset.h>
 
 #include "iris_core.h"
 #include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
 
-#define CPU_BASE_OFFS                          0x000A0000
+#define WRAPPER_TZ_BASE_OFFS                   0x000C0000
+#define AON_BASE_OFFS                          0x000E0000
 
-#define CPU_CS_BASE_OFFS                       (CPU_BASE_OFFS)
 #define CPU_IC_BASE_OFFS                       (CPU_BASE_OFFS)
 
 #define CPU_CS_A2HSOFTINTCLR                   (CPU_CS_BASE_OFFS + 0x1C)
@@ -21,6 +24,7 @@
 
 #define CTRL_INIT_IDLE_MSG_BMSK                        0x40000000
 #define CTRL_ERROR_STATUS__M                   0xfe
+#define CTRL_STATUS_PC_READY                   0x100
 
 #define QTBL_INFO                              (CPU_CS_BASE_OFFS + 0x50)
 #define QTBL_ENABLE                            BIT(0)
@@ -35,15 +39,48 @@
 #define HOST2XTENSA_INTR_ENABLE                        BIT(0)
 
 #define CPU_CS_X2RPMH                          (CPU_CS_BASE_OFFS + 0x168)
+#define MSK_SIGNAL_FROM_TENSILICA              BIT(0)
+#define MSK_CORE_POWER_ON                      BIT(1)
 
 #define CPU_IC_SOFTINT                         (CPU_IC_BASE_OFFS + 0x150)
 #define CPU_IC_SOFTINT_H2A_SHFT                        0x0
 
-#define WRAPPER_BASE_OFFS                      0x000B0000
 #define WRAPPER_INTR_STATUS                    (WRAPPER_BASE_OFFS + 0x0C)
 #define WRAPPER_INTR_STATUS_A2HWD_BMSK         BIT(3)
 #define WRAPPER_INTR_STATUS_A2H_BMSK           BIT(2)
 
+#define WRAPPER_INTR_MASK                      (WRAPPER_BASE_OFFS + 0x10)
+#define WRAPPER_INTR_MASK_A2HWD_BMSK           BIT(3)
+#define WRAPPER_INTR_MASK_A2HCPU_BMSK          BIT(2)
+
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL       (WRAPPER_BASE_OFFS + 0x54)
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS                (WRAPPER_BASE_OFFS + 
0x58)
+#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL       (WRAPPER_BASE_OFFS + 0x5C)
+#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS                (WRAPPER_BASE_OFFS + 
0x60)
+
+#define WRAPPER_TZ_CPU_STATUS                  (WRAPPER_TZ_BASE_OFFS + 0x10)
+#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG                (WRAPPER_TZ_BASE_OFFS + 
0x14)
+#define CTL_AXI_CLK_HALT                       BIT(0)
+#define CTL_CLK_HALT                           BIT(1)
+
+#define WRAPPER_TZ_QNS4PDXFIFO_RESET           (WRAPPER_TZ_BASE_OFFS + 0x18)
+#define RESET_HIGH                             BIT(0)
+
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL                (AON_BASE_OFFS)
+#define REQ_POWER_DOWN_PREP                    BIT(0)
+
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS         (AON_BASE_OFFS + 0x4)
+
+static void iris_vpu_interrupt_init(struct iris_core *core)
+{
+       u32 mask_val;
+
+       mask_val = readl(core->reg_base + WRAPPER_INTR_MASK);
+       mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK |
+                     WRAPPER_INTR_MASK_A2HCPU_BMSK);
+       writel(mask_val, core->reg_base + WRAPPER_INTR_MASK);
+}
+
 static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core)
 {
        u32 queue_size, value;
@@ -130,3 +167,203 @@ int iris_vpu_watchdog(struct iris_core *core, u32 
intr_status)
 
        return 0;
 }
+
+int iris_vpu_prepare_pc(struct iris_core *core)
+{
+       u32 wfi_status, idle_status, pc_ready;
+       u32 ctrl_status, val = 0;
+       int ret;
+
+       ctrl_status = readl(core->reg_base + CTRL_STATUS);
+       pc_ready = ctrl_status & CTRL_STATUS_PC_READY;
+       idle_status = ctrl_status & BIT(30);
+       if (pc_ready)
+               return 0;
+
+       wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
+       wfi_status &= BIT(0);
+       if (!wfi_status || !idle_status)
+               goto skip_power_off;
+
+       ret = core->hfi_ops->sys_pc_prep(core);
+       if (ret)
+               goto skip_power_off;
+
+       ret = readl_poll_timeout(core->reg_base + CTRL_STATUS, val,
+                                val & CTRL_STATUS_PC_READY, 250, 2500);
+       if (ret)
+               goto skip_power_off;
+
+       ret = readl_poll_timeout(core->reg_base + WRAPPER_TZ_CPU_STATUS,
+                                val, val & BIT(0), 250, 2500);
+       if (ret)
+               goto skip_power_off;
+
+       return 0;
+
+skip_power_off:
+       ctrl_status = readl(core->reg_base + CTRL_STATUS);
+       wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
+       wfi_status &= BIT(0);
+       dev_err(core->dev, "skip power collapse, wfi=%#x, idle=%#x, pcr=%#x, 
ctrl=%#x)\n",
+               wfi_status, idle_status, pc_ready, ctrl_status);
+
+       return -EAGAIN;
+}
+
+static int iris_vpu_power_off_controller(struct iris_core *core)
+{
+       u32 val = 0;
+       int ret;
+
+       writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + 
CPU_CS_X2RPMH);
+
+       writel(REQ_POWER_DOWN_PREP, core->reg_base + 
AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+
+       ret = readl_poll_timeout(core->reg_base + 
AON_WRAPPER_MVP_NOC_LPI_STATUS,
+                                val, val & BIT(0), 200, 2000);
+       if (ret)
+               goto disable_power;
+
+       writel(REQ_POWER_DOWN_PREP, core->reg_base + 
WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
+
+       ret = readl_poll_timeout(core->reg_base + 
WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+                                val, val & BIT(0), 200, 2000);
+       if (ret)
+               goto disable_power;
+
+       writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
+
+       ret = readl_poll_timeout(core->reg_base + 
WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
+                                val, val == 0, 200, 2000);
+       if (ret)
+               goto disable_power;
+
+       writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT,
+              core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+       writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+       writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+       writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+
+disable_power:
+       iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+       iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+       iris_disable_power_domains(core, 
core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+       return 0;
+}
+
+void iris_vpu_power_off_hw(struct iris_core *core)
+{
+       
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], 
false);
+       iris_disable_power_domains(core, 
core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+       iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+}
+
+void iris_vpu_power_off(struct iris_core *core)
+{
+       dev_pm_opp_set_rate(core->dev, 0);
+       core->iris_platform_data->vpu_ops->power_off_hw(core);
+       iris_vpu_power_off_controller(core);
+       iris_unset_icc_bw(core);
+
+       if (!iris_vpu_watchdog(core, core->intr_status))
+               disable_irq_nosync(core->irq);
+}
+
+static int iris_vpu_power_on_controller(struct iris_core *core)
+{
+       u32 rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
+       int ret;
+
+       ret = iris_enable_power_domains(core, 
core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+       if (ret)
+               return ret;
+
+       ret = reset_control_bulk_reset(rst_tbl_size, core->resets);
+       if (ret)
+               goto err_disable_power;
+
+       ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK);
+       if (ret)
+               goto err_disable_power;
+
+       ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
+       if (ret)
+               goto err_disable_clock;
+
+       return 0;
+
+err_disable_clock:
+       iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+err_disable_power:
+       iris_disable_power_domains(core, 
core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+       return ret;
+}
+
+static int iris_vpu_power_on_hw(struct iris_core *core)
+{
+       int ret;
+
+       ret = iris_enable_power_domains(core, 
core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+       if (ret)
+               return ret;
+
+       ret = iris_prepare_enable_clock(core, IRIS_HW_CLK);
+       if (ret)
+               goto err_disable_power;
+
+       ret = 
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], 
true);
+       if (ret)
+               goto err_disable_clock;
+
+       return 0;
+
+err_disable_clock:
+       iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+err_disable_power:
+       iris_disable_power_domains(core, 
core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+
+       return ret;
+}
+
+int iris_vpu_power_on(struct iris_core *core)
+{
+       u32 freq;
+       int ret;
+
+       ret = iris_set_icc_bw(core, INT_MAX);
+       if (ret)
+               goto err;
+
+       ret = iris_vpu_power_on_controller(core);
+       if (ret)
+               goto err_unvote_icc;
+
+       ret = iris_vpu_power_on_hw(core);
+       if (ret)
+               goto err_power_off_ctrl;
+
+       freq = core->power.clk_freq ? core->power.clk_freq :
+                                     (u32)ULONG_MAX;
+
+       dev_pm_opp_set_rate(core->dev, freq);
+
+       core->iris_platform_data->set_preset_registers(core);
+
+       iris_vpu_interrupt_init(core);
+       core->intr_status = 0;
+       enable_irq(core->irq);
+
+       return 0;
+
+err_power_off_ctrl:
+       iris_vpu_power_off_controller(core);
+err_unvote_icc:
+       iris_unset_icc_bw(core);
+err:
+       dev_err(core->dev, "power on failed\n");
+
+       return ret;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h 
b/drivers/media/platform/qcom/iris/iris_vpu_common.h
index c38c055d3d14..d3efa7c0ce9a 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
@@ -8,9 +8,20 @@
 
 struct iris_core;
 
+extern const struct vpu_ops iris_vpu2_ops;
+extern const struct vpu_ops iris_vpu3_ops;
+
+struct vpu_ops {
+       void (*power_off_hw)(struct iris_core *core);
+};
+
 int iris_vpu_boot_firmware(struct iris_core *core);
 void iris_vpu_raise_interrupt(struct iris_core *core);
 void iris_vpu_clear_interrupt(struct iris_core *core);
 int iris_vpu_watchdog(struct iris_core *core, u32 intr_status);
+int iris_vpu_prepare_pc(struct iris_core *core);
+int iris_vpu_power_on(struct iris_core *core);
+void iris_vpu_power_off_hw(struct iris_core *core);
+void iris_vpu_power_off(struct iris_core *core);
 
 #endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h 
b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
new file mode 100644
index 000000000000..fe8a39e5e5a3
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights 
reserved.
+ */
+
+#ifndef __IRIS_VPU_REGISTER_DEFINES_H__
+#define __IRIS_VPU_REGISTER_DEFINES_H__
+
+#define VCODEC_BASE_OFFS                       0x00000000
+#define CPU_BASE_OFFS                          0x000A0000
+#define WRAPPER_BASE_OFFS                      0x000B0000
+
+#define CPU_CS_BASE_OFFS                       (CPU_BASE_OFFS)
+
+#define WRAPPER_CORE_POWER_STATUS              (WRAPPER_BASE_OFFS + 0x80)
+
+#endif

Reply via email to