Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

media: iris: implement power management

Implement runtime power management for iris, including a platform
specific power on/off sequence.

Tested-by: Stefan Schmidt <stefan.schmidt@linaro.org> # x1e80100 (Dell XPS 13 9345)
Reviewed-by: Stefan Schmidt <stefan.schmidt@linaro.org>
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8550-QRD
Tested-by: Neil Armstrong <neil.armstrong@linaro.org> # on SM8550-HDK
Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>

authored by

Dikshita Agarwal and committed by
Hans Verkuil
bb8a95aa fb583a21

+755 -4
+3
drivers/media/platform/qcom/iris/Makefile
··· 9 9 iris_hfi_queue.o \ 10 10 iris_platform_sm8550.o \ 11 11 iris_probe.o \ 12 + iris_resources.o \ 12 13 iris_vidc.o \ 14 + iris_vpu2.o \ 15 + iris_vpu3.o \ 13 16 iris_vpu_common.o \ 14 17 15 18 obj-$(CONFIG_VIDEO_QCOM_IRIS) += iris.o
+14 -1
drivers/media/platform/qcom/iris/iris_core.c
··· 3 3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 4 */ 5 5 6 + #include <linux/pm_runtime.h> 7 + 6 8 #include "iris_core.h" 7 9 #include "iris_firmware.h" 8 10 #include "iris_state.h" ··· 12 10 13 11 void iris_core_deinit(struct iris_core *core) 14 12 { 13 + pm_runtime_resume_and_get(core->dev); 14 + 15 15 mutex_lock(&core->lock); 16 16 iris_fw_unload(core); 17 + iris_vpu_power_off(core); 17 18 iris_hfi_queues_deinit(core); 18 19 core->state = IRIS_CORE_DEINIT; 19 20 mutex_unlock(&core->lock); 21 + 22 + pm_runtime_put_sync(core->dev); 20 23 } 21 24 22 25 static int iris_wait_for_system_response(struct iris_core *core) ··· 61 54 if (ret) 62 55 goto error; 63 56 64 - ret = iris_fw_load(core); 57 + ret = iris_vpu_power_on(core); 65 58 if (ret) 66 59 goto error_queue_deinit; 60 + 61 + ret = iris_fw_load(core); 62 + if (ret) 63 + goto error_power_off; 67 64 68 65 ret = iris_vpu_boot_firmware(core); 69 66 if (ret) ··· 83 72 84 73 error_unload_fw: 85 74 iris_fw_unload(core); 75 + error_power_off: 76 + iris_vpu_power_off(core); 86 77 error_queue_deinit: 87 78 iris_hfi_queues_deinit(core); 88 79 error:
+4
drivers/media/platform/qcom/iris/iris_core.h
··· 7 7 #define __IRIS_CORE_H__ 8 8 9 9 #include <linux/types.h> 10 + #include <linux/pm_domain.h> 10 11 #include <media/v4l2-device.h> 11 12 12 13 #include "iris_hfi_common.h" 13 14 #include "iris_hfi_queue.h" 14 15 #include "iris_platform_common.h" 16 + #include "iris_resources.h" 15 17 #include "iris_state.h" 16 18 17 19 struct icc_info { ··· 54 52 * @response_packet: a pointer to response packet from fw to driver 55 53 * @header_id: id of packet header 56 54 * @packet_id: id of packet 55 + * @power: a structure for clock and bw information 57 56 * @hfi_ops: iris hfi command ops 58 57 * @hfi_response_ops: iris hfi response ops 59 58 * @core_init_done: structure of signal completion for system response ··· 89 86 u8 *response_packet; 90 87 u32 header_id; 91 88 u32 packet_id; 89 + struct iris_core_power power; 92 90 const struct iris_hfi_command_ops *hfi_ops; 93 91 const struct iris_hfi_response_ops *hfi_response_ops; 94 92 struct completion core_init_done;
+5
drivers/media/platform/qcom/iris/iris_firmware.c
··· 109 109 { 110 110 return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); 111 111 } 112 + 113 + int iris_set_hw_state(struct iris_core *core, bool resume) 114 + { 115 + return qcom_scm_set_remote_state(resume, 0); 116 + }
+1
drivers/media/platform/qcom/iris/iris_firmware.h
··· 10 10 11 11 int iris_fw_load(struct iris_core *core); 12 12 int iris_fw_unload(struct iris_core *core); 13 + int iris_set_hw_state(struct iris_core *core, bool resume); 13 14 14 15 #endif
+62
drivers/media/platform/qcom/iris/iris_hfi_common.c
··· 3 3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 4 */ 5 5 6 + #include <linux/pm_runtime.h> 7 + 8 + #include "iris_firmware.h" 6 9 #include "iris_core.h" 7 10 #include "iris_hfi_common.h" 8 11 #include "iris_vpu_common.h" ··· 41 38 return IRQ_NONE; 42 39 43 40 mutex_lock(&core->lock); 41 + pm_runtime_mark_last_busy(core->dev); 44 42 iris_vpu_clear_interrupt(core); 45 43 mutex_unlock(&core->lock); 46 44 ··· 51 47 enable_irq(irq); 52 48 53 49 return IRQ_HANDLED; 50 + } 51 + 52 + int iris_hfi_pm_suspend(struct iris_core *core) 53 + { 54 + int ret; 55 + 56 + ret = iris_vpu_prepare_pc(core); 57 + if (ret) { 58 + pm_runtime_mark_last_busy(core->dev); 59 + ret = -EAGAIN; 60 + goto error; 61 + } 62 + 63 + ret = iris_set_hw_state(core, false); 64 + if (ret) 65 + goto error; 66 + 67 + iris_vpu_power_off(core); 68 + 69 + return 0; 70 + 71 + error: 72 + dev_err(core->dev, "failed to suspend\n"); 73 + 74 + return ret; 75 + } 76 + 77 + int iris_hfi_pm_resume(struct iris_core *core) 78 + { 79 + const struct iris_hfi_command_ops *ops = core->hfi_ops; 80 + int ret; 81 + 82 + ret = iris_vpu_power_on(core); 83 + if (ret) 84 + goto error; 85 + 86 + ret = iris_set_hw_state(core, true); 87 + if (ret) 88 + goto err_power_off; 89 + 90 + ret = iris_vpu_boot_firmware(core); 91 + if (ret) 92 + goto err_suspend_hw; 93 + 94 + ret = ops->sys_interframe_powercollapse(core); 95 + if (ret) 96 + goto err_suspend_hw; 97 + 98 + return 0; 99 + 100 + err_suspend_hw: 101 + iris_set_hw_state(core, false); 102 + err_power_off: 103 + iris_vpu_power_off(core); 104 + error: 105 + dev_err(core->dev, "failed to resume\n"); 106 + 107 + return -EBUSY; 54 108 }
+3
drivers/media/platform/qcom/iris/iris_hfi_common.h
··· 46 46 int (*sys_init)(struct iris_core *core); 47 47 int (*sys_image_version)(struct iris_core *core); 48 48 int (*sys_interframe_powercollapse)(struct iris_core *core); 49 + int (*sys_pc_prep)(struct iris_core *core); 49 50 }; 50 51 51 52 struct iris_hfi_response_ops { ··· 54 53 }; 55 54 56 55 int iris_hfi_core_init(struct iris_core *core); 56 + int iris_hfi_pm_suspend(struct iris_core *core); 57 + int iris_hfi_pm_resume(struct iris_core *core); 57 58 58 59 irqreturn_t iris_hfi_isr(int irq, void *data); 59 60 irqreturn_t iris_hfi_isr_handler(int irq, void *data);
+11
drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
··· 56 56 return ret; 57 57 } 58 58 59 + static int iris_hfi_gen1_sys_pc_prep(struct iris_core *core) 60 + { 61 + struct hfi_sys_pc_prep_pkt pkt; 62 + 63 + pkt.hdr.size = sizeof(struct hfi_sys_pc_prep_pkt); 64 + pkt.hdr.pkt_type = HFI_CMD_SYS_PC_PREP; 65 + 66 + return iris_hfi_queue_cmd_write_locked(core, &pkt, pkt.hdr.size); 67 + } 68 + 59 69 static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = { 60 70 .sys_init = iris_hfi_gen1_sys_init, 61 71 .sys_image_version = iris_hfi_gen1_sys_image_version, 62 72 .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse, 73 + .sys_pc_prep = iris_hfi_gen1_sys_pc_prep, 63 74 }; 64 75 65 76 void iris_hfi_gen1_command_ops_init(struct iris_core *core)
+5
drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
··· 12 12 #define HFI_ERR_NONE 0x0 13 13 14 14 #define HFI_CMD_SYS_INIT 0x10001 15 + #define HFI_CMD_SYS_PC_PREP 0x10002 15 16 #define HFI_CMD_SYS_SET_PROPERTY 0x10005 16 17 #define HFI_CMD_SYS_GET_PROPERTY 0x10006 17 18 ··· 47 46 struct hfi_pkt_hdr hdr; 48 47 u32 num_properties; 49 48 u32 data; 49 + }; 50 + 51 + struct hfi_sys_pc_prep_pkt { 52 + struct hfi_pkt_hdr hdr; 50 53 }; 51 54 52 55 struct hfi_msg_event_notify_pkt {
+18
drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
··· 68 68 return ret; 69 69 } 70 70 71 + static int iris_hfi_gen2_sys_pc_prep(struct iris_core *core) 72 + { 73 + struct iris_hfi_header *hdr; 74 + int ret; 75 + 76 + hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL); 77 + if (!hdr) 78 + return -ENOMEM; 79 + 80 + iris_hfi_gen2_packet_sys_pc_prep(core, hdr); 81 + ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size); 82 + 83 + kfree(hdr); 84 + 85 + return ret; 86 + } 87 + 71 88 static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = { 72 89 .sys_init = iris_hfi_gen2_sys_init, 73 90 .sys_image_version = iris_hfi_gen2_sys_image_version, 74 91 .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse, 92 + .sys_pc_prep = iris_hfi_gen2_sys_pc_prep, 75 93 }; 76 94 77 95 void iris_hfi_gen2_command_ops_init(struct iris_core *core)
+1
drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
··· 12 12 13 13 #define HFI_CMD_BEGIN 0x01000000 14 14 #define HFI_CMD_INIT 0x01000001 15 + #define HFI_CMD_POWER_COLLAPSE 0x01000002 15 16 #define HFI_CMD_END 0x01FFFFFF 16 17 17 18 #define HFI_PROP_BEGIN 0x03000000
+13
drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
··· 159 159 &payload, 160 160 sizeof(u32)); 161 161 } 162 + 163 + void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr) 164 + { 165 + iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++); 166 + 167 + iris_hfi_gen2_create_packet(hdr, 168 + HFI_CMD_POWER_COLLAPSE, 169 + HFI_HOST_FLAGS_NONE, 170 + HFI_PAYLOAD_NONE, 171 + HFI_PORT_NONE, 172 + core->packet_id++, 173 + NULL, 0); 174 + }
+1
drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
··· 65 65 void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct iris_hfi_header *hdr); 66 66 void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core, 67 67 struct iris_hfi_header *hdr); 68 + void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr); 68 69 69 70 #endif
+18
drivers/media/platform/qcom/iris/iris_hfi_queue.c
··· 3 3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 4 */ 5 5 6 + #include <linux/pm_runtime.h> 7 + 6 8 #include "iris_core.h" 7 9 #include "iris_hfi_queue.h" 8 10 #include "iris_vpu_common.h" ··· 130 128 { 131 129 int ret; 132 130 131 + ret = pm_runtime_resume_and_get(core->dev); 132 + if (ret < 0) 133 + goto exit; 134 + 133 135 mutex_lock(&core->lock); 134 136 ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt_size); 137 + if (ret) { 138 + mutex_unlock(&core->lock); 139 + goto exit; 140 + } 135 141 mutex_unlock(&core->lock); 142 + 143 + pm_runtime_mark_last_busy(core->dev); 144 + pm_runtime_put_autosuspend(core->dev); 145 + 146 + return 0; 147 + 148 + exit: 149 + pm_runtime_put_sync(core->dev); 136 150 137 151 return ret; 138 152 }
+14
drivers/media/platform/qcom/iris/iris_platform_common.h
··· 10 10 11 11 #define IRIS_PAS_ID 9 12 12 #define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */ 13 + #define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */ 13 14 14 15 extern struct iris_platform_data sm8550_data; 15 16 ··· 42 41 u32 bank_spreading; 43 42 }; 44 43 44 + struct iris_core_power { 45 + u64 clk_freq; 46 + u64 icc_bw; 47 + }; 48 + 49 + enum platform_pm_domain_type { 50 + IRIS_CTRL_POWER_DOMAIN, 51 + IRIS_HW_POWER_DOMAIN, 52 + }; 53 + 45 54 struct iris_platform_data { 46 55 void (*init_hfi_command_ops)(struct iris_core *core); 47 56 void (*init_hfi_response_ops)(struct iris_core *core); 48 57 struct iris_inst *(*get_instance)(void); 58 + const struct vpu_ops *vpu_ops; 59 + void (*set_preset_registers)(struct iris_core *core); 49 60 const struct icc_info *icc_tbl; 50 61 unsigned int icc_tbl_size; 51 62 const char * const *pmdomain_tbl; ··· 75 62 u32 core_arch; 76 63 u32 hw_response_timeout; 77 64 struct ubwc_config_data *ubwc_config; 65 + u32 num_vpp_pipe; 78 66 }; 79 67 80 68 #endif
+9
drivers/media/platform/qcom/iris/iris_platform_sm8550.c
··· 6 6 #include "iris_core.h" 7 7 #include "iris_hfi_gen2.h" 8 8 #include "iris_platform_common.h" 9 + #include "iris_vpu_common.h" 9 10 10 11 #define VIDEO_ARCH_LX 1 12 + 13 + static void iris_set_sm8550_preset_registers(struct iris_core *core) 14 + { 15 + writel(0x0, core->reg_base + 0xB0088); 16 + } 11 17 12 18 static const struct icc_info sm8550_icc_table[] = { 13 19 { "cpu-cfg", 1000, 1000 }, ··· 53 47 .get_instance = iris_hfi_gen2_get_instance, 54 48 .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, 55 49 .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, 50 + .vpu_ops = &iris_vpu3_ops, 51 + .set_preset_registers = iris_set_sm8550_preset_registers, 56 52 .icc_tbl = sm8550_icc_table, 57 53 .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table), 58 54 .clk_rst_tbl = sm8550_clk_reset_table, ··· 73 65 .core_arch = VIDEO_ARCH_LX, 74 66 .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, 75 67 .ubwc_config = &ubwc_config_sm8550, 68 + .num_vpp_pipe = 4, 76 69 };
+53
drivers/media/platform/qcom/iris/iris_probe.c
··· 8 8 #include <linux/module.h> 9 9 #include <linux/pm_domain.h> 10 10 #include <linux/pm_opp.h> 11 + #include <linux/pm_runtime.h> 11 12 #include <linux/reset.h> 12 13 13 14 #include "iris_core.h" ··· 253 252 dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); 254 253 dma_set_seg_boundary(&pdev->dev, DMA_BIT_MASK(32)); 255 254 255 + pm_runtime_set_autosuspend_delay(core->dev, AUTOSUSPEND_DELAY_VALUE); 256 + pm_runtime_use_autosuspend(core->dev); 257 + ret = devm_pm_runtime_enable(core->dev); 258 + if (ret) 259 + goto err_vdev_unreg; 260 + 256 261 return 0; 257 262 258 263 err_vdev_unreg: ··· 268 261 269 262 return ret; 270 263 } 264 + 265 + static int __maybe_unused iris_pm_suspend(struct device *dev) 266 + { 267 + struct iris_core *core; 268 + int ret = 0; 269 + 270 + core = dev_get_drvdata(dev); 271 + 272 + mutex_lock(&core->lock); 273 + if (core->state != IRIS_CORE_INIT) 274 + goto exit; 275 + 276 + ret = iris_hfi_pm_suspend(core); 277 + 278 + exit: 279 + mutex_unlock(&core->lock); 280 + 281 + return ret; 282 + } 283 + 284 + static int __maybe_unused iris_pm_resume(struct device *dev) 285 + { 286 + struct iris_core *core; 287 + int ret = 0; 288 + 289 + core = dev_get_drvdata(dev); 290 + 291 + mutex_lock(&core->lock); 292 + if (core->state != IRIS_CORE_INIT) 293 + goto exit; 294 + 295 + ret = iris_hfi_pm_resume(core); 296 + pm_runtime_mark_last_busy(core->dev); 297 + 298 + exit: 299 + mutex_unlock(&core->lock); 300 + 301 + return ret; 302 + } 303 + 304 + static const struct dev_pm_ops iris_pm_ops = { 305 + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 306 + pm_runtime_force_resume) 307 + SET_RUNTIME_PM_OPS(iris_pm_suspend, iris_pm_resume, NULL) 308 + }; 271 309 272 310 static const struct of_device_id iris_dt_match[] = { 273 311 { ··· 329 277 .driver = { 330 278 .name = "qcom-iris", 331 279 .of_match_table = iris_dt_match, 280 + .pm = &iris_pm_ops, 332 281 }, 333 282 }; 334 283
+131
drivers/media/platform/qcom/iris/iris_resources.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 + */ 5 + 6 + #include <linux/clk.h> 7 + #include <linux/interconnect.h> 8 + #include <linux/pm_domain.h> 9 + #include <linux/pm_opp.h> 10 + #include <linux/pm_runtime.h> 11 + #include <linux/reset.h> 12 + 13 + #include "iris_core.h" 14 + #include "iris_resources.h" 15 + 16 + #define BW_THRESHOLD 50000 17 + 18 + int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw) 19 + { 20 + unsigned long bw_kbps = 0, bw_prev = 0; 21 + const struct icc_info *icc_tbl; 22 + int ret = 0, i; 23 + 24 + icc_tbl = core->iris_platform_data->icc_tbl; 25 + 26 + for (i = 0; i < core->icc_count; i++) { 27 + if (!strcmp(core->icc_tbl[i].name, "video-mem")) { 28 + bw_kbps = icc_bw; 29 + bw_prev = core->power.icc_bw; 30 + 31 + bw_kbps = clamp_t(typeof(bw_kbps), bw_kbps, 32 + icc_tbl[i].bw_min_kbps, icc_tbl[i].bw_max_kbps); 33 + 34 + if (abs(bw_kbps - bw_prev) < BW_THRESHOLD && bw_prev) 35 + return ret; 36 + 37 + core->icc_tbl[i].avg_bw = bw_kbps; 38 + 39 + core->power.icc_bw = bw_kbps; 40 + break; 41 + } 42 + } 43 + 44 + return icc_bulk_set_bw(core->icc_count, core->icc_tbl); 45 + } 46 + 47 + int iris_unset_icc_bw(struct iris_core *core) 48 + { 49 + u32 i; 50 + 51 + core->power.icc_bw = 0; 52 + 53 + for (i = 0; i < core->icc_count; i++) { 54 + core->icc_tbl[i].avg_bw = 0; 55 + core->icc_tbl[i].peak_bw = 0; 56 + } 57 + 58 + return icc_bulk_set_bw(core->icc_count, core->icc_tbl); 59 + } 60 + 61 + int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev) 62 + { 63 + int ret; 64 + 65 + ret = dev_pm_opp_set_rate(core->dev, ULONG_MAX); 66 + if (ret) 67 + return ret; 68 + 69 + ret = pm_runtime_get_sync(pd_dev); 70 + if (ret < 0) 71 + return ret; 72 + 73 + return ret; 74 + } 75 + 76 + int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev) 77 + { 78 + int ret; 79 + 80 + ret = dev_pm_opp_set_rate(core->dev, 0); 81 + if (ret) 82 + return ret; 83 + 84 + pm_runtime_put_sync(pd_dev); 85 + 86 + return 0; 87 + } 88 + 89 + static struct clk *iris_get_clk_by_type(struct iris_core *core, enum platform_clk_type clk_type) 90 + { 91 + const struct platform_clk_data *clk_tbl; 92 + u32 clk_cnt, i, j; 93 + 94 + clk_tbl = core->iris_platform_data->clk_tbl; 95 + clk_cnt = core->iris_platform_data->clk_tbl_size; 96 + 97 + for (i = 0; i < clk_cnt; i++) { 98 + if (clk_tbl[i].clk_type == clk_type) { 99 + for (j = 0; core->clock_tbl && j < core->clk_count; j++) { 100 + if (!strcmp(core->clock_tbl[j].id, clk_tbl[i].clk_name)) 101 + return core->clock_tbl[j].clk; 102 + } 103 + } 104 + } 105 + 106 + return NULL; 107 + } 108 + 109 + int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type) 110 + { 111 + struct clk *clock; 112 + 113 + clock = iris_get_clk_by_type(core, clk_type); 114 + if (!clock) 115 + return -EINVAL; 116 + 117 + return clk_prepare_enable(clock); 118 + } 119 + 120 + int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type) 121 + { 122 + struct clk *clock; 123 + 124 + clock = iris_get_clk_by_type(core, clk_type); 125 + if (!clock) 126 + return -EINVAL; 127 + 128 + clk_disable_unprepare(clock); 129 + 130 + return 0; 131 + }
+18
drivers/media/platform/qcom/iris/iris_resources.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 + */ 5 + 6 + #ifndef __IRIS_RESOURCES_H__ 7 + #define __IRIS_RESOURCES_H__ 8 + 9 + struct iris_core; 10 + 11 + int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev); 12 + int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev); 13 + int iris_unset_icc_bw(struct iris_core *core); 14 + int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw); 15 + int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type); 16 + int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type); 17 + 18 + #endif
+8
drivers/media/platform/qcom/iris/iris_vidc.c
··· 3 3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 4 */ 5 5 6 + #include <linux/pm_runtime.h> 6 7 #include <media/v4l2-ioctl.h> 7 8 #include <media/v4l2-mem2mem.h> 8 9 ··· 82 81 struct iris_inst *inst; 83 82 int ret; 84 83 84 + ret = pm_runtime_resume_and_get(core->dev); 85 + if (ret < 0) 86 + return ret; 87 + 85 88 ret = iris_core_init(core); 86 89 if (ret) { 87 90 dev_err(core->dev, "core init failed\n"); 91 + pm_runtime_put_sync(core->dev); 88 92 return ret; 89 93 } 94 + 95 + pm_runtime_put_sync(core->dev); 90 96 91 97 inst = core->iris_platform_data->get_instance(); 92 98 if (!inst)
+11
drivers/media/platform/qcom/iris/iris_vpu2.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 + */ 5 + 6 + #include "iris_instance.h" 7 + #include "iris_vpu_common.h" 8 + 9 + const struct vpu_ops iris_vpu2_ops = { 10 + .power_off_hw = iris_vpu_power_off_hw, 11 + };
+84
drivers/media/platform/qcom/iris/iris_vpu3.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 + */ 5 + 6 + #include <linux/iopoll.h> 7 + 8 + #include "iris_instance.h" 9 + #include "iris_vpu_common.h" 10 + #include "iris_vpu_register_defines.h" 11 + 12 + #define AON_MVP_NOC_RESET 0x0001F000 13 + 14 + #define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88) 15 + #define CORE_CLK_RUN 0x0 16 + 17 + #define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160) 18 + #define CORE_BRIDGE_SW_RESET BIT(0) 19 + #define CORE_BRIDGE_HW_RESET_DISABLE BIT(1) 20 + 21 + #define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000) 22 + #define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1)) 23 + 24 + #define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004) 25 + 26 + #define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70) 27 + 28 + static bool iris_vpu3_hw_power_collapsed(struct iris_core *core) 29 + { 30 + u32 value, pwr_status; 31 + 32 + value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS); 33 + pwr_status = value & BIT(1); 34 + 35 + return pwr_status ? false : true; 36 + } 37 + 38 + static void iris_vpu3_power_off_hardware(struct iris_core *core) 39 + { 40 + u32 reg_val = 0, value, i; 41 + int ret; 42 + 43 + if (iris_vpu3_hw_power_collapsed(core)) 44 + goto disable_power; 45 + 46 + dev_err(core->dev, "video hw is power on\n"); 47 + 48 + value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); 49 + if (value) 50 + writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); 51 + 52 + for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) { 53 + ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i, 54 + reg_val, reg_val & 0x400000, 2000, 20000); 55 + if (ret) 56 + goto disable_power; 57 + } 58 + 59 + writel(VIDEO_NOC_RESET_REQ, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); 60 + 61 + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, 62 + reg_val, reg_val & 0x3, 200, 2000); 63 + if (ret) 64 + goto disable_power; 65 + 66 + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); 67 + 68 + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, 69 + reg_val, !(reg_val & 0x3), 200, 2000); 70 + if (ret) 71 + goto disable_power; 72 + 73 + writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, 74 + core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); 75 + writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); 76 + writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); 77 + 78 + disable_power: 79 + iris_vpu_power_off_hw(core); 80 + } 81 + 82 + const struct vpu_ops iris_vpu3_ops = { 83 + .power_off_hw = iris_vpu3_power_off_hardware, 84 + };
+240 -3
drivers/media/platform/qcom/iris/iris_vpu_common.c
··· 4 4 */ 5 5 6 6 #include <linux/iopoll.h> 7 + #include <linux/pm_opp.h> 8 + #include <linux/reset.h> 7 9 8 10 #include "iris_core.h" 9 11 #include "iris_vpu_common.h" 12 + #include "iris_vpu_register_defines.h" 10 13 11 - #define CPU_BASE_OFFS 0x000A0000 14 + #define WRAPPER_TZ_BASE_OFFS 0x000C0000 15 + #define AON_BASE_OFFS 0x000E0000 12 16 13 - #define CPU_CS_BASE_OFFS (CPU_BASE_OFFS) 14 17 #define CPU_IC_BASE_OFFS (CPU_BASE_OFFS) 15 18 16 19 #define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C) ··· 24 21 25 22 #define CTRL_INIT_IDLE_MSG_BMSK 0x40000000 26 23 #define CTRL_ERROR_STATUS__M 0xfe 24 + #define CTRL_STATUS_PC_READY 0x100 27 25 28 26 #define QTBL_INFO (CPU_CS_BASE_OFFS + 0x50) 29 27 #define QTBL_ENABLE BIT(0) ··· 39 35 #define HOST2XTENSA_INTR_ENABLE BIT(0) 40 36 41 37 #define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168) 38 + #define MSK_SIGNAL_FROM_TENSILICA BIT(0) 39 + #define MSK_CORE_POWER_ON BIT(1) 42 40 43 41 #define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150) 44 42 #define CPU_IC_SOFTINT_H2A_SHFT 0x0 45 43 46 - #define WRAPPER_BASE_OFFS 0x000B0000 47 44 #define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C) 48 45 #define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3) 49 46 #define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2) 47 + 48 + #define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10) 49 + #define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3) 50 + #define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2) 51 + 52 + #define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54) 53 + #define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58) 54 + #define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C) 55 + #define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60) 56 + 57 + #define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10) 58 + #define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14) 59 + #define CTL_AXI_CLK_HALT BIT(0) 60 + #define CTL_CLK_HALT BIT(1) 61 + 62 + #define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18) 63 + #define RESET_HIGH BIT(0) 64 + 65 + #define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS) 66 + #define REQ_POWER_DOWN_PREP BIT(0) 67 + 68 + #define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4) 69 + 70 + static void iris_vpu_interrupt_init(struct iris_core *core) 71 + { 72 + u32 mask_val; 73 + 74 + mask_val = readl(core->reg_base + WRAPPER_INTR_MASK); 75 + mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK | 76 + WRAPPER_INTR_MASK_A2HCPU_BMSK); 77 + writel(mask_val, core->reg_base + WRAPPER_INTR_MASK); 78 + } 50 79 51 80 static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core) 52 81 { ··· 166 129 } 167 130 168 131 return 0; 132 + } 133 + 134 + int iris_vpu_prepare_pc(struct iris_core *core) 135 + { 136 + u32 wfi_status, idle_status, pc_ready; 137 + u32 ctrl_status, val = 0; 138 + int ret; 139 + 140 + ctrl_status = readl(core->reg_base + CTRL_STATUS); 141 + pc_ready = ctrl_status & CTRL_STATUS_PC_READY; 142 + idle_status = ctrl_status & BIT(30); 143 + if (pc_ready) 144 + return 0; 145 + 146 + wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS); 147 + wfi_status &= BIT(0); 148 + if (!wfi_status || !idle_status) 149 + goto skip_power_off; 150 + 151 + ret = core->hfi_ops->sys_pc_prep(core); 152 + if (ret) 153 + goto skip_power_off; 154 + 155 + ret = readl_poll_timeout(core->reg_base + CTRL_STATUS, val, 156 + val & CTRL_STATUS_PC_READY, 250, 2500); 157 + if (ret) 158 + goto skip_power_off; 159 + 160 + ret = readl_poll_timeout(core->reg_base + WRAPPER_TZ_CPU_STATUS, 161 + val, val & BIT(0), 250, 2500); 162 + if (ret) 163 + goto skip_power_off; 164 + 165 + return 0; 166 + 167 + skip_power_off: 168 + ctrl_status = readl(core->reg_base + CTRL_STATUS); 169 + wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS); 170 + wfi_status &= BIT(0); 171 + dev_err(core->dev, "skip power collapse, wfi=%#x, idle=%#x, pcr=%#x, ctrl=%#x)\n", 172 + wfi_status, idle_status, pc_ready, ctrl_status); 173 + 174 + return -EAGAIN; 175 + } 176 + 177 + static int iris_vpu_power_off_controller(struct iris_core *core) 178 + { 179 + u32 val = 0; 180 + int ret; 181 + 182 + writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH); 183 + 184 + writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); 185 + 186 + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS, 187 + val, val & BIT(0), 200, 2000); 188 + if (ret) 189 + goto disable_power; 190 + 191 + writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); 192 + 193 + ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS, 194 + val, val & BIT(0), 200, 2000); 195 + if (ret) 196 + goto disable_power; 197 + 198 + writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL); 199 + 200 + ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS, 201 + val, val == 0, 200, 2000); 202 + if (ret) 203 + goto disable_power; 204 + 205 + writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT, 206 + core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG); 207 + writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET); 208 + writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET); 209 + writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG); 210 + 211 + disable_power: 212 + iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); 213 + iris_disable_unprepare_clock(core, IRIS_AXI_CLK); 214 + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); 215 + 216 + return 0; 217 + } 218 + 219 + void iris_vpu_power_off_hw(struct iris_core *core) 220 + { 221 + dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false); 222 + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); 223 + iris_disable_unprepare_clock(core, IRIS_HW_CLK); 224 + } 225 + 226 + void iris_vpu_power_off(struct iris_core *core) 227 + { 228 + dev_pm_opp_set_rate(core->dev, 0); 229 + core->iris_platform_data->vpu_ops->power_off_hw(core); 230 + iris_vpu_power_off_controller(core); 231 + iris_unset_icc_bw(core); 232 + 233 + if (!iris_vpu_watchdog(core, core->intr_status)) 234 + disable_irq_nosync(core->irq); 235 + } 236 + 237 + static int iris_vpu_power_on_controller(struct iris_core *core) 238 + { 239 + u32 rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; 240 + int ret; 241 + 242 + ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); 243 + if (ret) 244 + return ret; 245 + 246 + ret = reset_control_bulk_reset(rst_tbl_size, core->resets); 247 + if (ret) 248 + goto err_disable_power; 249 + 250 + ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK); 251 + if (ret) 252 + goto err_disable_power; 253 + 254 + ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK); 255 + if (ret) 256 + goto err_disable_clock; 257 + 258 + return 0; 259 + 260 + err_disable_clock: 261 + iris_disable_unprepare_clock(core, IRIS_AXI_CLK); 262 + err_disable_power: 263 + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); 264 + 265 + return ret; 266 + } 267 + 268 + static int iris_vpu_power_on_hw(struct iris_core *core) 269 + { 270 + int ret; 271 + 272 + ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); 273 + if (ret) 274 + return ret; 275 + 276 + ret = iris_prepare_enable_clock(core, IRIS_HW_CLK); 277 + if (ret) 278 + goto err_disable_power; 279 + 280 + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true); 281 + if (ret) 282 + goto err_disable_clock; 283 + 284 + return 0; 285 + 286 + err_disable_clock: 287 + iris_disable_unprepare_clock(core, IRIS_HW_CLK); 288 + err_disable_power: 289 + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); 290 + 291 + return ret; 292 + } 293 + 294 + int iris_vpu_power_on(struct iris_core *core) 295 + { 296 + u32 freq; 297 + int ret; 298 + 299 + ret = iris_set_icc_bw(core, INT_MAX); 300 + if (ret) 301 + goto err; 302 + 303 + ret = iris_vpu_power_on_controller(core); 304 + if (ret) 305 + goto err_unvote_icc; 306 + 307 + ret = iris_vpu_power_on_hw(core); 308 + if (ret) 309 + goto err_power_off_ctrl; 310 + 311 + freq = core->power.clk_freq ? core->power.clk_freq : 312 + (u32)ULONG_MAX; 313 + 314 + dev_pm_opp_set_rate(core->dev, freq); 315 + 316 + core->iris_platform_data->set_preset_registers(core); 317 + 318 + iris_vpu_interrupt_init(core); 319 + core->intr_status = 0; 320 + enable_irq(core->irq); 321 + 322 + return 0; 323 + 324 + err_power_off_ctrl: 325 + iris_vpu_power_off_controller(core); 326 + err_unvote_icc: 327 + iris_unset_icc_bw(core); 328 + err: 329 + dev_err(core->dev, "power on failed\n"); 330 + 331 + return ret; 169 332 }
+11
drivers/media/platform/qcom/iris/iris_vpu_common.h
··· 8 8 9 9 struct iris_core; 10 10 11 + extern const struct vpu_ops iris_vpu2_ops; 12 + extern const struct vpu_ops iris_vpu3_ops; 13 + 14 + struct vpu_ops { 15 + void (*power_off_hw)(struct iris_core *core); 16 + }; 17 + 11 18 int iris_vpu_boot_firmware(struct iris_core *core); 12 19 void iris_vpu_raise_interrupt(struct iris_core *core); 13 20 void iris_vpu_clear_interrupt(struct iris_core *core); 14 21 int iris_vpu_watchdog(struct iris_core *core, u32 intr_status); 22 + int iris_vpu_prepare_pc(struct iris_core *core); 23 + int iris_vpu_power_on(struct iris_core *core); 24 + void iris_vpu_power_off_hw(struct iris_core *core); 25 + void iris_vpu_power_off(struct iris_core *core); 15 26 16 27 #endif
+17
drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. 4 + */ 5 + 6 + #ifndef __IRIS_VPU_REGISTER_DEFINES_H__ 7 + #define __IRIS_VPU_REGISTER_DEFINES_H__ 8 + 9 + #define VCODEC_BASE_OFFS 0x00000000 10 + #define CPU_BASE_OFFS 0x000A0000 11 + #define WRAPPER_BASE_OFFS 0x000B0000 12 + 13 + #define CPU_CS_BASE_OFFS (CPU_BASE_OFFS) 14 + 15 + #define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS + 0x80) 16 + 17 + #endif