@@ -13,10 +13,12 @@ iris-objs += iris_probe.o \
iris_hfi_response.o \
iris_hfi_packet.o \
iris_buffer.o \
+ iris_power.o \
resources.o \
vpu_common.o \
vpu_iris3.o \
vpu_iris3_buffer.o \
+ vpu_iris3_power.o \
platform_common.o \
platform_sm8550.o
@@ -12,6 +12,7 @@
#define HFI_CMD_BEGIN 0x01000000
#define HFI_CMD_INIT 0x01000001
+#define HFI_CMD_POWER_COLLAPSE 0x01000002
#define HFI_CMD_OPEN 0x01000003
#define HFI_CMD_CLOSE 0x01000004
#define HFI_CMD_START 0x01000005
@@ -46,10 +47,16 @@ enum hfi_property_mode_type {
#define HFI_BITMASK_FRAME_MBS_ONLY_FLAG 0x00000001
+#define HFI_TRUE 0x00000001
+
+#define HFI_FALSE 0x00000000
+
#define HFI_PROP_BEGIN 0x03000000
#define HFI_PROP_IMAGE_VERSION 0x03000001
+#define HFI_PROP_INTRA_FRAME_POWER_COLLAPSE 0x03000002
+
enum hfi_codec_type {
HFI_CODEC_DECODE_AVC = 1,
HFI_CODEC_ENCODE_AVC = 2,
@@ -28,6 +28,8 @@
#define MAX_DPB_LIST_ARRAY_SIZE (16 * 4)
#define MAX_DPB_LIST_PAYLOAD_SIZE (16 * 4 * 4)
+#define INPUT_TIMER_LIST_SIZE 30
+
enum codec_type {
H264 = BIT(0),
HEVC = BIT(1),
@@ -128,4 +130,9 @@ struct iris_hfi_frame_info {
u32 overflow;
};
+struct iris_input_timer {
+ struct list_head list;
+ u64 time_us;
+};
+
#endif
@@ -101,6 +101,10 @@ struct iris_core {
struct list_head instances;
u32 intr_status;
char fw_version[IRIS_VERSION_LENGTH];
+ struct mutex pm_lock; /* lock for pm operations */
+ u32 skip_pc_count;
+ bool power_enabled;
+ struct iris_core_power power;
};
int iris_core_init(struct iris_core *core);
@@ -3,12 +3,15 @@
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
+#include <linux/pm_runtime.h>
+
#include "hfi_defines.h"
#include "iris_core.h"
#include "iris_helpers.h"
#include "iris_hfi.h"
#include "iris_hfi_packet.h"
#include "iris_instance.h"
+#include "iris_power.h"
#include "iris_vidc.h"
int check_core_lock(struct iris_core *core)
@@ -479,6 +482,8 @@ int queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_typ
if (!buffers)
return -EINVAL;
+ iris_scale_power(inst);
+
list_for_each_entry(buf, &buffers->list, list) {
if (!(buf->attr & BUF_ATTR_DEFERRED))
continue;
@@ -775,6 +780,9 @@ int session_streamoff(struct iris_inst *inst, u32 plane)
if (ret)
goto error;
+ if (plane == INPUT_MPLANE)
+ iris_flush_input_timer(inst);
+
/* no more queued buffers after streamoff */
count = get_num_queued_buffers(inst, buffer_type);
if (count) {
@@ -797,3 +805,61 @@ int session_streamoff(struct iris_inst *inst, u32 plane)
return ret;
}
+
+int iris_pm_get(struct iris_core *core)
+{
+ int ret;
+
+ mutex_lock(&core->pm_lock);
+ ret = pm_runtime_resume_and_get(core->dev);
+ mutex_unlock(&core->pm_lock);
+
+ return ret;
+}
+
+int iris_pm_put(struct iris_core *core, bool autosuspend)
+{
+ int ret;
+
+ mutex_lock(&core->pm_lock);
+
+ if (autosuspend)
+ ret = pm_runtime_put_autosuspend(core->dev);
+ else
+ ret = pm_runtime_put_sync(core->dev);
+ if (ret > 0)
+ ret = 0;
+
+ mutex_unlock(&core->pm_lock);
+
+ return ret;
+}
+
+int iris_pm_get_put(struct iris_core *core)
+{
+ int ret = 0;
+
+ mutex_lock(&core->pm_lock);
+
+ if (pm_runtime_suspended(core->dev)) {
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ goto error;
+
+ ret = pm_runtime_put_autosuspend(core->dev);
+ }
+ if (ret > 0)
+ ret = 0;
+
+error:
+ mutex_unlock(&core->pm_lock);
+
+ return ret;
+}
+
+void iris_pm_touch(struct iris_core *core)
+{
+ mutex_lock(&core->pm_lock);
+ pm_runtime_mark_last_busy(core->dev);
+ mutex_unlock(&core->pm_lock);
+}
@@ -51,5 +51,9 @@ int iris_vb2_buffer_done(struct iris_inst *inst,
int iris_release_nonref_buffers(struct iris_inst *inst);
void iris_destroy_buffers(struct iris_inst *inst);
int session_streamoff(struct iris_inst *inst, u32 plane);
+int iris_pm_get(struct iris_core *core);
+int iris_pm_put(struct iris_core *core, bool autosuspend);
+int iris_pm_get_put(struct iris_core *core);
+void iris_pm_touch(struct iris_core *core);
#endif
@@ -67,6 +67,41 @@ static bool validate_session(struct iris_core *core,
return valid;
}
+static int iris_power_off(struct iris_core *core)
+{
+ int ret;
+
+ if (!core->power_enabled)
+ return 0;
+
+ ret = call_vpu_op(core, power_off, core);
+ if (ret) {
+ dev_err(core->dev, "Failed to power off, err: %d\n", ret);
+ return ret;
+ }
+ core->power_enabled = false;
+
+ return ret;
+}
+
+static int iris_power_on(struct iris_core *core)
+{
+ int ret;
+
+ if (core->power_enabled)
+ return 0;
+
+ ret = call_vpu_op(core, power_on, core);
+ if (ret) {
+ dev_err(core->dev, "Failed to power on, err: %d\n", ret);
+ return ret;
+ }
+
+ core->power_enabled = true;
+
+ return ret;
+}
+
static int sys_init(struct iris_core *core)
{
int ret;
@@ -113,20 +148,24 @@ int iris_hfi_core_init(struct iris_core *core)
if (ret)
goto error;
+ ret = iris_power_on(core);
+ if (ret)
+ goto error;
+
core->use_tz = use_tz(core->dev);
if (!core->use_tz)
- goto error;
+ goto error_power_off;
pas_id = core->platform_data->pas_id;
fw_name = core->platform_data->fwname;
ret = load_fw(core->dev, fw_name, &mem_phys, &mem_size,
pas_id, core->use_tz);
if (ret)
- goto error;
+ goto error_power_off;
ret = auth_reset_fw(pas_id);
if (ret)
- goto error;
+ goto error_power_off;
cp_start = core->cap[CP_START].value;
cp_size = core->cap[CP_SIZE].value;
@@ -135,22 +174,24 @@ int iris_hfi_core_init(struct iris_core *core)
ret = protect_secure_region(cp_start, cp_size, cp_nonpixel_start,
cp_nonpixel_size, pas_id);
if (ret)
- goto error;
+ goto error_power_off;
ret = call_vpu_op(core, boot_firmware, core);
if (ret)
- goto error;
+ goto error_power_off;
ret = sys_init(core);
if (ret)
- goto error;
+ goto error_power_off;
ret = sys_image_version(core);
if (ret)
- goto error;
+ goto error_power_off;
return ret;
+error_power_off:
+ iris_power_off(core);
error:
dev_err(core->dev, "%s(): failed with ret %d\n", __func__, ret);
@@ -170,6 +211,8 @@ int iris_hfi_core_deinit(struct iris_core *core)
unload_fw(core->platform_data->pas_id);
+ iris_power_off(core);
+
return ret;
}
@@ -573,16 +616,24 @@ irqreturn_t iris_hfi_isr(int irq, void *data)
irqreturn_t iris_hfi_isr_handler(int irq, void *data)
{
struct iris_core *core = data;
+ int ret;
if (!core)
return IRQ_NONE;
+ ret = iris_pm_get(core);
+ if (ret)
+ goto exit;
+
mutex_lock(&core->lock);
call_vpu_op(core, clear_interrupt, core);
mutex_unlock(&core->lock);
__response_handler(core);
+ iris_pm_put(core, true);
+
+exit:
if (!call_vpu_op(core, watchdog, core, core->intr_status))
enable_irq(irq);
@@ -701,3 +752,113 @@ int iris_hfi_release_buffer(struct iris_inst *inst,
return ret;
}
+
+int prepare_pc(struct iris_core *core)
+{
+ int ret;
+
+ ret = hfi_packet_sys_pc_prep(core, core->packet, core->packet_size);
+ if (ret)
+ goto err_pc_prep;
+
+ ret = iris_hfi_queue_cmd_write(core, core->packet);
+ if (ret)
+ goto err_pc_prep;
+
+ return ret;
+
+err_pc_prep:
+ dev_err(core->dev, "Failed to prepare iris for power off\n");
+
+ return ret;
+}
+
+int iris_hfi_pm_suspend(struct iris_core *core)
+{
+ int ret;
+
+ ret = check_core_lock(core);
+ if (ret)
+ return ret;
+
+ if (!core_in_valid_state(core))
+ return -EINVAL;
+
+ if (!core->power_enabled)
+ return 0;
+
+ if (core->skip_pc_count >= MAX_PC_SKIP_COUNT) {
+ dev_err(core->dev, "Failed to PC for %d times\n", core->skip_pc_count);
+ core->skip_pc_count = 0;
+ iris_change_core_state(core, IRIS_CORE_ERROR);
+ iris_core_deinit_locked(core);
+ return -EINVAL;
+ }
+
+ iris_flush_debug_queue(core, core->packet, core->packet_size);
+
+ ret = call_vpu_op(core, prepare_pc, core);
+ if (ret) {
+ core->skip_pc_count++;
+ iris_pm_touch(core);
+ return -EAGAIN;
+ }
+
+ ret = set_hw_state(false);
+ if (ret)
+ return ret;
+
+ ret = iris_power_off(core);
+ if (ret)
+ return ret;
+
+ core->skip_pc_count = 0;
+
+ return ret;
+}
+
+int iris_hfi_pm_resume(struct iris_core *core)
+{
+ int ret;
+
+ ret = check_core_lock(core);
+ if (ret)
+ return ret;
+
+ if (!core_in_valid_state(core))
+ return -EINVAL;
+
+ if (core->power_enabled)
+ return 0;
+
+ ret = iris_power_on(core);
+ if (ret)
+ goto error;
+
+ ret = set_hw_state(true);
+ if (ret)
+ goto err_power_off;
+
+ ret = call_vpu_op(core, boot_firmware, core);
+ if (ret)
+ goto err_suspend_hw;
+
+ ret = hfi_packet_sys_interframe_powercollapse(core, core->packet, core->packet_size);
+ if (ret)
+ goto err_suspend_hw;
+
+ ret = iris_hfi_queue_cmd_write(core, core->packet);
+ if (ret)
+ goto err_suspend_hw;
+
+ return ret;
+
+err_suspend_hw:
+ set_hw_state(false);
+err_power_off:
+ iris_power_off(core);
+error:
+ dev_err(core->dev, "Failed to Resume\n");
+
+ return -EBUSY;
+}
@@ -9,6 +9,8 @@
#include "iris_instance.h"
#include "iris_core.h"
+#define MAX_PC_SKIP_COUNT 10
+
int iris_hfi_core_init(struct iris_core *core);
int iris_hfi_core_deinit(struct iris_core *core);
int iris_hfi_session_open(struct iris_inst *inst);
@@ -32,6 +34,9 @@ int iris_hfi_queue_buffer(struct iris_inst *inst,
struct iris_buffer *buffer);
int iris_hfi_release_buffer(struct iris_inst *inst,
struct iris_buffer *buffer);
+int iris_hfi_pm_suspend(struct iris_core *core);
+int iris_hfi_pm_resume(struct iris_core *core);
+int prepare_pc(struct iris_core *core);
irqreturn_t iris_hfi_isr(int irq, void *data);
irqreturn_t iris_hfi_isr_handler(int irq, void *data);
@@ -622,3 +622,51 @@ int hfi_packet_session_property(struct iris_inst *inst,
return ret;
}
+
+int hfi_packet_sys_interframe_powercollapse(struct iris_core *core,
+ u8 *pkt, u32 pkt_size)
+{
+ u32 payload = 0;
+ int ret;
+
+ ret = hfi_create_header(pkt, pkt_size,
+ 0 /*session_id*/,
+ core->header_id++);
+ if (ret)
+ return ret;
+
+ payload = HFI_TRUE;
+
+ ret = hfi_create_packet(pkt, pkt_size,
+ HFI_PROP_INTRA_FRAME_POWER_COLLAPSE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ return ret;
+}
+
+int hfi_packet_sys_pc_prep(struct iris_core *core,
+ u8 *pkt, u32 pkt_size)
+{
+ int ret;
+
+ ret = hfi_create_header(pkt, pkt_size,
+ 0 /*session_id*/,
+ core->header_id++);
+ if (ret)
+ return ret;
+
+ ret = hfi_create_packet(pkt, pkt_size,
+ HFI_CMD_POWER_COLLAPSE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_NONE,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ NULL, 0);
+
+ return ret;
+}
@@ -104,5 +104,9 @@ int hfi_packet_session_command(struct iris_inst *inst, u32 pkt_type,
int hfi_packet_session_property(struct iris_inst *inst,
u32 pkt_type, u32 flags, u32 port,
u32 payload_type, void *payload, u32 payload_size);
+int hfi_packet_sys_interframe_powercollapse(struct iris_core *core,
+ u8 *pkt, u32 pkt_size);
+int hfi_packet_sys_pc_prep(struct iris_core *core,
+ u8 *pkt, u32 pkt_size);
#endif
@@ -928,6 +928,8 @@ static int handle_response(struct iris_core *core, void *response)
struct hfi_header *hdr;
int ret;
+ iris_pm_touch(core);
+
hdr = (struct hfi_header *)response;
ret = validate_hdr_packet(core, hdr);
if (ret)
@@ -47,6 +47,11 @@
* @dst_subcr_params: subscription params to fw on output port
* @dpb_list_payload: array of dpb buffers
* @once_per_session_set: boolean to set once per session property
+ * @max_rate: max input rate
+ * @max_input_data_size: max size of input data
+ * @power: structure of power info
+ * @bus_data: structure of bus data
+ * @input_timer_list: list head of input timer
*/
struct iris_inst {
@@ -80,6 +85,11 @@ struct iris_inst {
struct subscription_params dst_subcr_params;
u32 dpb_list_payload[MAX_DPB_LIST_ARRAY_SIZE];
bool once_per_session_set;
+ u32 max_rate;
+ u32 max_input_data_size;
+ struct iris_inst_power power;
+ struct bus_vote_data bus_data;
+ struct list_head input_timer_list;
};
#endif
new file mode 100644
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_power.h"
+#include "iris_helpers.h"
+#include "resources.h"
+
+static int iris_set_buses(struct iris_inst *inst)
+{
+ struct iris_inst *instance;
+ struct iris_core *core;
+ u64 total_bw_ddr = 0;
+ int ret;
+
+ core = inst->core;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list) {
+ if (!instance->max_input_data_size)
+ continue;
+
+ total_bw_ddr += instance->power.bus_bw;
+ }
+
+ ret = vote_buses(core, total_bw_ddr);
+
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int iris_vote_buses(struct iris_inst *inst)
+{
+ struct v4l2_format *out_f, *inp_f;
+ struct bus_vote_data *vote_data;
+ struct iris_core *core;
+
+ core = inst->core;
+
+ vote_data = &inst->bus_data;
+
+ out_f = inst->fmt_dst;
+ inp_f = inst->fmt_src;
+
+ vote_data->width = inp_f->fmt.pix_mp.width;
+ vote_data->height = inp_f->fmt.pix_mp.height;
+ vote_data->fps = inst->max_rate;
+
+ if (is_linear_colorformat(out_f->fmt.pix_mp.pixelformat)) {
+ vote_data->color_formats[0] = V4L2_PIX_FMT_NV12;
+ vote_data->color_formats[1] = out_f->fmt.pix_mp.pixelformat;
+ } else {
+ vote_data->color_formats[0] = out_f->fmt.pix_mp.pixelformat;
+ }
+
+ call_session_op(core, calc_bw, inst, vote_data);
+
+ inst->power.bus_bw = vote_data->bus_bw;
+
+ return iris_set_buses(inst);
+}
+
+static int iris_set_clocks(struct iris_inst *inst)
+{
+ struct iris_inst *instance;
+ struct iris_core *core;
+ int ret = 0;
+ u64 freq;
+
+ core = inst->core;
+
+ mutex_lock(&core->lock);
+
+ freq = 0;
+ list_for_each_entry(instance, &core->instances, list) {
+ if (!instance->max_input_data_size)
+ continue;
+
+ freq += instance->power.min_freq;
+ }
+
+ core->power.clk_freq = freq;
+
+ ret = opp_set_rate(core, freq);
+
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int iris_scale_clocks(struct iris_inst *inst)
+{
+ struct iris_buffer *vbuf;
+ struct iris_core *core;
+ u32 data_size = 0;
+
+ core = inst->core;
+
+ list_for_each_entry(vbuf, &inst->buffers.input.list, list)
+ data_size = max(data_size, vbuf->data_size);
+
+ inst->max_input_data_size = data_size;
+
+ inst->max_rate = inst->cap[QUEUED_RATE].value >> 16;
+
+ if (!inst->max_input_data_size)
+ return 0;
+
+ inst->power.min_freq = call_session_op(core, calc_freq, inst,
+ inst->max_input_data_size);
+ iris_set_clocks(inst);
+
+ return 0;
+}
+
+int iris_scale_power(struct iris_inst *inst)
+{
+ iris_scale_clocks(inst);
+ iris_vote_buses(inst);
+
+ return 0;
+}
+
+int iris_update_input_rate(struct iris_inst *inst, u64 time_us)
+{
+ struct iris_input_timer *prev_timer = NULL;
+ struct iris_input_timer *input_timer;
+ u64 input_timer_sum_us = 0;
+ u64 counter = 0;
+
+ input_timer = kzalloc(sizeof(*input_timer), GFP_KERNEL);
+ if (!input_timer)
+ return -ENOMEM;
+
+ input_timer->time_us = time_us;
+ INIT_LIST_HEAD(&input_timer->list);
+ list_add_tail(&input_timer->list, &inst->input_timer_list);
+ list_for_each_entry(input_timer, &inst->input_timer_list, list) {
+ if (prev_timer) {
+ input_timer_sum_us += input_timer->time_us - prev_timer->time_us;
+ counter++;
+ }
+ prev_timer = input_timer;
+ }
+
+ if (input_timer_sum_us && counter >= INPUT_TIMER_LIST_SIZE)
+ inst->cap[QUEUED_RATE].value =
+ (s32)(DIV64_U64_ROUND_CLOSEST(counter * 1000000,
+ input_timer_sum_us) << 16);
+
+ if (counter >= INPUT_TIMER_LIST_SIZE) {
+ input_timer = list_first_entry(&inst->input_timer_list,
+ struct iris_input_timer, list);
+ list_del_init(&input_timer->list);
+ kfree(input_timer);
+ }
+
+ return 0;
+}
+
+int iris_flush_input_timer(struct iris_inst *inst)
+{
+ struct iris_input_timer *input_timer, *dummy_timer;
+
+ list_for_each_entry_safe(input_timer, dummy_timer, &inst->input_timer_list, list) {
+ list_del_init(&input_timer->list);
+ kfree(input_timer);
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _IRIS_POWER_H_
+#define _IRIS_POWER_H_
+
+#include "iris_instance.h"
+
+int iris_scale_power(struct iris_inst *inst);
+int iris_update_input_rate(struct iris_inst *inst, u64 time_us);
+int iris_flush_input_timer(struct iris_inst *inst);
+
+#endif
@@ -6,14 +6,15 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include "../hfi_queue.h"
#include "iris_core.h"
+#include "iris_ctrls.h"
#include "iris_helpers.h"
#include "iris_hfi.h"
-#include "resources.h"
#include "iris_vidc.h"
-#include "iris_ctrls.h"
+#include "resources.h"
static int init_iris_isr(struct iris_core *core)
{
@@ -70,6 +71,8 @@ static void iris_remove(struct platform_device *pdev)
if (!core)
return;
+ iris_pm_get(core);
+
iris_core_deinit(core);
hfi_queue_deinit(core->dev, &core->iface_q_table, &core->sfr,
&core->command_queue, &core->message_queue,
@@ -79,7 +82,10 @@ static void iris_remove(struct platform_device *pdev)
v4l2_device_unregister(&core->v4l2_dev);
+ iris_pm_put(core, false);
+
mutex_destroy(&core->lock);
+ mutex_destroy(&core->pm_lock);
core->state = IRIS_CORE_DEINIT;
}
@@ -97,6 +103,7 @@ static int iris_probe(struct platform_device *pdev)
core->state = IRIS_CORE_DEINIT;
mutex_init(&core->lock);
+ mutex_init(&core->pm_lock);
core->packet_size = IFACEQ_CORE_PKT_SIZE;
core->packet = devm_kzalloc(core->dev, core->packet_size, GFP_KERNEL);
@@ -117,58 +124,66 @@ static int iris_probe(struct platform_device *pdev)
if (core->irq < 0)
return core->irq;
+ 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) {
+ dev_err(core->dev, "failed to enable runtime pm\n");
+ goto err_runtime_disable;
+ }
+
ret = init_iris_isr(core);
if (ret) {
dev_err_probe(core->dev, ret,
"%s: Failed to init isr with %d\n", __func__, ret);
- return ret;
+ goto err_runtime_disable;
}
ret = init_platform(core);
if (ret) {
dev_err_probe(core->dev, ret,
"%s: init platform failed with %d\n", __func__, ret);
- return ret;
+ goto err_runtime_disable;
}
ret = init_vpu(core);
if (ret) {
dev_err_probe(core->dev, ret,
"%s: init vpu failed with %d\n", __func__, ret);
- return ret;
+ goto err_runtime_disable;
}
ret = init_ops(core);
if (ret) {
dev_err_probe(core->dev, ret,
"%s: init ops failed with %d\n", __func__, ret);
- return ret;
+ goto err_runtime_disable;
}
ret = init_resources(core);
if (ret) {
dev_err_probe(core->dev, ret,
"%s: init resource failed with %d\n", __func__, ret);
- return ret;
+ goto err_runtime_disable;
}
ret = iris_init_core_caps(core);
if (ret) {
dev_err_probe(core->dev, ret,
"%s: init core caps failed with %d\n", __func__, ret);
- return ret;
+ goto err_runtime_disable;
}
ret = iris_init_instance_caps(core);
if (ret) {
dev_err_probe(core->dev, ret,
"%s: init inst caps failed with %d\n", __func__, ret);
- return ret;
+ goto err_runtime_disable;
}
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret)
- return ret;
+ goto err_runtime_disable;
ret = iris_register_video_device(core);
if (ret)
@@ -194,14 +209,29 @@ static int iris_probe(struct platform_device *pdev)
goto err_vdev_unreg;
}
+ ret = iris_pm_get(core);
+ if (ret) {
+ dev_err_probe(core->dev, ret, "%s: failed to get runtime pm\n", __func__);
+ goto err_queue_deinit;
+ }
+
ret = iris_core_init(core);
if (ret) {
dev_err_probe(core->dev, ret, "%s: core init failed\n", __func__);
goto err_queue_deinit;
}
+ ret = iris_pm_put(core, false);
+ if (ret) {
+ pm_runtime_get_noresume(core->dev);
+ dev_err_probe(core->dev, ret, "%s: failed to put runtime pm\n", __func__);
+ goto err_core_deinit;
+ }
+
return ret;
+err_core_deinit:
+ iris_core_deinit(core);
err_queue_deinit:
hfi_queue_deinit(core->dev, &core->iface_q_table, &core->sfr,
&core->command_queue, &core->message_queue,
@@ -210,10 +240,77 @@ static int iris_probe(struct platform_device *pdev)
video_unregister_device(core->vdev_dec);
err_v4l2_unreg:
v4l2_device_unregister(&core->v4l2_dev);
+err_runtime_disable:
+ pm_runtime_put_noidle(core->dev);
+ pm_runtime_set_suspended(core->dev);
return ret;
}
+static int iris_pm_suspend(struct device *dev)
+{
+ struct iris_core *core;
+ int ret;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ core = dev_get_drvdata(dev);
+
+ mutex_lock(&core->lock);
+ if (!core_in_valid_state(core)) {
+ ret = 0;
+ goto unlock;
+ }
+
+ if (!core->power_enabled) {
+ ret = 0;
+ goto unlock;
+ }
+
+ ret = iris_hfi_pm_suspend(core);
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int iris_pm_resume(struct device *dev)
+{
+ struct iris_core *core;
+ int ret;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ core = dev_get_drvdata(dev);
+
+ mutex_lock(&core->lock);
+ if (!core_in_valid_state(core)) {
+ ret = 0;
+ goto unlock;
+ }
+
+ if (core->power_enabled) {
+ ret = 0;
+ goto unlock;
+ }
+
+ ret = iris_hfi_pm_resume(core);
+
+unlock:
+ 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",
@@ -229,6 +326,7 @@ static struct platform_driver qcom_iris_driver = {
.driver = {
.name = "qcom-iris",
.of_match_table = iris_dt_match,
+ .pm = &iris_pm_ops,
},
};
@@ -10,6 +10,7 @@
#include "iris_helpers.h"
#include "iris_hfi.h"
#include "iris_instance.h"
+#include "iris_power.h"
#include "iris_vb2.h"
#include "iris_vdec.h"
@@ -111,39 +112,52 @@ int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
goto error;
}
+ ret = iris_pm_get(inst->core);
+ if (ret)
+ goto error;
+
if (!inst->once_per_session_set) {
inst->once_per_session_set = true;
ret = iris_hfi_session_set_codec(inst);
if (ret)
- goto error;
+ goto err_pm_get;
ret = iris_hfi_session_set_default_header(inst);
if (ret)
- goto error;
+ goto err_pm_get;
ret = iris_alloc_and_queue_session_int_bufs(inst, BUF_PERSIST);
if (ret)
- goto error;
+ goto err_pm_get;
}
+ iris_scale_power(inst);
+
if (q->type == INPUT_MPLANE)
ret = vdec_streamon_input(inst);
else if (q->type == OUTPUT_MPLANE)
ret = vdec_streamon_output(inst);
if (ret)
- goto error;
+ goto err_pm_get;
buf_type = v4l2_type_to_driver(q->type);
if (!buf_type) {
ret = -EINVAL;
- goto error;
+ goto err_pm_get;
}
ret = queue_deferred_buffers(inst, buf_type);
if (ret)
+ goto err_pm_get;
+
+ ret = iris_pm_put(inst->core, true);
+ if (ret)
goto error;
return ret;
+
+err_pm_get:
+ iris_pm_put(inst->core, false);
error:
iris_inst_change_state(inst, IRIS_INST_ERROR);
@@ -165,6 +179,10 @@ void iris_vb2_stop_streaming(struct vb2_queue *q)
if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE)
goto error;
+ ret = iris_pm_get_put(inst->core);
+ if (ret)
+ goto error;
+
if (q->type == INPUT_MPLANE)
ret = session_streamoff(inst, INPUT_MPLANE);
else if (q->type == OUTPUT_MPLANE)
@@ -181,6 +199,8 @@ void iris_vb2_stop_streaming(struct vb2_queue *q)
void iris_vb2_buf_queue(struct vb2_buffer *vb2)
{
+ u64 ktime_ns = ktime_get_ns();
+ struct iris_core *core;
struct iris_inst *inst;
int ret;
@@ -188,11 +208,23 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2)
if (!inst || !inst->core)
return;
+ core = inst->core;
+
if (!vb2->planes[0].bytesused && vb2->type == INPUT_MPLANE) {
ret = -EINVAL;
goto exit;
}
+ if (vb2->type == INPUT_MPLANE) {
+ ret = iris_update_input_rate(inst, div_u64(ktime_ns, 1000));
+ if (ret)
+ goto exit;
+ }
+
+ ret = iris_pm_get_put(core);
+ if (ret)
+ goto exit;
+
ret = vdec_qbuf(inst, vb2);
exit:
@@ -12,6 +12,7 @@
#include "iris_helpers.h"
#include "iris_hfi.h"
#include "iris_hfi_packet.h"
+#include "iris_power.h"
#include "iris_vdec.h"
#define UNSPECIFIED_COLOR_FORMAT 5
@@ -1069,6 +1070,8 @@ static int process_streamon_input(struct iris_inst *inst)
enum iris_inst_sub_state set_sub_state = IRIS_INST_SUB_NONE;
int ret;
+ iris_scale_power(inst);
+
ret = iris_hfi_start(inst, INPUT_MPLANE);
if (ret)
return ret;
@@ -1150,6 +1153,8 @@ static int process_streamon_output(struct iris_inst *inst)
bool drain_pending = false;
int ret;
+ iris_scale_power(inst);
+
if (inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST)
clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
@@ -1287,6 +1292,8 @@ int vdec_qbuf(struct iris_inst *inst, struct vb2_buffer *vb2)
return 0;
}
+ iris_scale_power(inst);
+
ret = queue_buffer(inst, buf);
if (ret)
return ret;
@@ -9,12 +9,13 @@
#include "iris_buffer.h"
#include "iris_common.h"
+#include "iris_ctrls.h"
#include "iris_helpers.h"
#include "iris_hfi.h"
#include "iris_instance.h"
+#include "iris_power.h"
#include "iris_vdec.h"
#include "iris_vidc.h"
-#include "iris_ctrls.h"
#include "iris_vb2.h"
#define VIDC_DRV_NAME "iris_driver"
@@ -162,17 +163,23 @@ int vidc_open(struct file *filp)
int i = 0;
int ret;
- ret = iris_core_init(core);
+ ret = iris_pm_get(core);
if (ret)
return ret;
+ ret = iris_core_init(core);
+ if (ret)
+ goto fail_pm_put;
+
ret = iris_core_init_wait(core);
if (ret)
- return ret;
+ goto fail_pm_put;
inst = kzalloc(sizeof(*inst), GFP_KERNEL);
- if (!inst)
- return -ENOMEM;
+ if (!inst) {
+ ret = -ENOMEM;
+ goto fail_pm_put;
+ }
inst->core = core;
inst->session_id = hash32_ptr(inst);
@@ -198,6 +205,7 @@ int vidc_open(struct file *filp)
INIT_LIST_HEAD(&inst->buffers.persist.list);
INIT_LIST_HEAD(&inst->buffers.vpss.list);
INIT_LIST_HEAD(&inst->caps_list);
+ INIT_LIST_HEAD(&inst->input_timer_list);
for (i = 0; i < MAX_SIGNAL; i++)
init_completion(&inst->completions[i]);
@@ -211,11 +219,16 @@ int vidc_open(struct file *filp)
if (ret)
goto fail_inst_deinit;
+ iris_scale_power(inst);
+
ret = iris_hfi_session_open(inst);
if (ret) {
dev_err(core->dev, "%s: session open failed\n", __func__);
goto fail_core_deinit;
}
+
+ iris_pm_put(core, true);
+
filp->private_data = &inst->fh;
return 0;
@@ -233,6 +246,8 @@ int vidc_open(struct file *filp)
mutex_destroy(&inst->ctx_q_lock);
mutex_destroy(&inst->lock);
kfree(inst);
+fail_pm_put:
+ iris_pm_put(core, false);
return ret;
}
@@ -240,20 +255,25 @@ int vidc_open(struct file *filp)
int vidc_close(struct file *filp)
{
struct iris_inst *inst;
+ struct iris_core *core;
inst = get_vidc_inst(filp, NULL);
if (!inst)
return -EINVAL;
+ core = inst->core;
+
v4l2_ctrl_handler_free(&inst->ctrl_handler);
vdec_inst_deinit(inst);
mutex_lock(&inst->lock);
+ iris_pm_get(core);
close_session(inst);
iris_inst_change_state(inst, IRIS_INST_CLOSE);
vidc_vb2_queue_deinit(inst);
vidc_v4l2_fh_deinit(inst);
iris_destroy_buffers(inst);
vidc_remove_session(inst);
+ iris_pm_put(core, false);
mutex_unlock(&inst->lock);
mutex_destroy(&inst->ctx_q_lock);
mutex_destroy(&inst->lock);
@@ -965,11 +985,17 @@ static int vidc_dec_cmd(struct file *filp, void *fh,
goto unlock;
}
+ ret = iris_pm_get(inst->core);
+ if (ret)
+ goto unlock;
+
if (dec->cmd == V4L2_DEC_CMD_START)
ret = vdec_start_cmd(inst);
else if (dec->cmd == V4L2_DEC_CMD_STOP)
ret = vdec_stop_cmd(inst);
+ iris_pm_put(inst->core, true);
+
unlock:
mutex_unlock(&inst->lock);
@@ -15,6 +15,7 @@ struct iris_core;
struct iris_inst;
#define HW_RESPONSE_TIMEOUT_VALUE (1000)
+#define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500)
#define BIT_DEPTH_8 (8 << 16 | 8)
#define BIT_DEPTH_10 (10 << 16 | 10)
@@ -64,6 +65,11 @@ struct reg_preset_info {
u32 mask;
};
+struct iris_core_power {
+ u64 clk_freq;
+ u64 bus_bw;
+};
+
struct ubwc_config_data {
u32 max_channels;
u32 mal_length;
@@ -74,6 +80,18 @@ struct ubwc_config_data {
u32 bank_spreading;
};
+struct bus_vote_data {
+ u32 color_formats[2];
+ int height, width;
+ u32 fps;
+ u64 bus_bw;
+};
+
+struct iris_inst_power {
+ u64 min_freq;
+ u32 bus_bw;
+};
+
enum plat_core_cap_type {
CORE_CAP_NONE = 0,
DEC_CODECS,
@@ -4,6 +4,8 @@
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
#include <linux/interconnect.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
@@ -15,6 +17,8 @@
#include "platform_common.h"
#include "resources.h"
+#define BW_THRESHOLD 50000
+
static void iris_pd_release(void *res)
{
struct device *pd = (struct device *)res;
@@ -59,6 +63,34 @@ static int iris_opp_dl_get(struct device *dev, struct device *supplier)
return ret;
}
+int opp_set_rate(struct iris_core *core, u64 freq)
+{
+ unsigned long opp_freq = 0;
+ struct dev_pm_opp *opp;
+ int ret;
+
+ opp_freq = freq;
+
+ opp = dev_pm_opp_find_freq_ceil(core->dev, &opp_freq);
+ if (IS_ERR(opp)) {
+ opp = dev_pm_opp_find_freq_floor(core->dev, &opp_freq);
+ if (IS_ERR(opp)) {
+ dev_err(core->dev,
+ "unable to find freq %lld in opp table\n", freq);
+ return -EINVAL;
+ }
+ }
+ dev_pm_opp_put(opp);
+
+ ret = dev_pm_opp_set_rate(core->dev, opp_freq);
+ if (ret) {
+ dev_err(core->dev, "failed to set rate\n");
+ return ret;
+ }
+
+ return ret;
+}
+
static int init_bus(struct iris_core *core)
{
const struct bus_info *bus_tbl;
@@ -135,6 +167,10 @@ static int init_power_domains(struct iris_core *core)
}
}
+ ret = devm_pm_opp_set_clkname(core->dev, "vcodec_core");
+ if (ret)
+ return ret;
+
ret = devm_pm_opp_of_add_table(core->dev);
if (ret) {
dev_err(core->dev, "%s: failed to add opp table\n", __func__);
@@ -144,6 +180,56 @@ static int init_power_domains(struct iris_core *core)
return ret;
}
+int enable_power_domains(struct iris_core *core, const char *name)
+{
+ struct power_domain_info *pdinfo = NULL;
+ int ret;
+ u32 i;
+
+ ret = opp_set_rate(core, ULONG_MAX);
+ if (ret)
+ return ret;
+
+ core->pd_count = core->platform_data->pd_tbl_size;
+ for (i = 0; i < (core->pd_count - 1); i++) {
+ pdinfo = &core->power_domain_tbl[i];
+ if (strcmp(pdinfo->name, name))
+ continue;
+ ret = pm_runtime_get_sync(pdinfo->genpd_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = opp_set_rate(core, ULONG_MAX);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+int disable_power_domains(struct iris_core *core, const char *name)
+{
+ struct power_domain_info *pdinfo = NULL;
+ int ret;
+ u32 i;
+
+ ret = opp_set_rate(core, 0);
+ if (ret)
+ return ret;
+
+ core->pd_count = core->platform_data->pd_tbl_size;
+ for (i = 0; i < (core->pd_count - 1); i++) {
+ pdinfo = &core->power_domain_tbl[i];
+ if (strcmp(pdinfo->name, name))
+ continue;
+ ret = pm_runtime_put_sync(pdinfo->genpd_dev);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
static int init_clocks(struct iris_core *core)
{
const struct clock_info *clk_tbl;
@@ -204,6 +290,200 @@ static int init_reset_clocks(struct iris_core *core)
return 0;
}
+int unvote_buses(struct iris_core *core)
+{
+ struct bus_info *bus = NULL;
+ int ret = 0;
+ u32 i;
+
+ core->power.bus_bw = 0;
+ core->bus_count = core->platform_data->bus_tbl_size;
+
+ for (i = 0; i < core->bus_count; i++) {
+ bus = &core->bus_tbl[i];
+ if (!bus->icc)
+ return -EINVAL;
+
+ ret = icc_set_bw(bus->icc, 0, 0);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+int vote_buses(struct iris_core *core, unsigned long bus_bw)
+{
+ unsigned long bw_kbps = 0, bw_prev = 0;
+ struct bus_info *bus = NULL;
+ int ret = 0;
+ u32 i;
+
+ core->bus_count = core->platform_data->bus_tbl_size;
+
+ for (i = 0; i < core->bus_count; i++) {
+ bus = &core->bus_tbl[i];
+ if (bus && bus->icc) {
+ if (!strcmp(bus->name, "iris-ddr")) {
+ bw_kbps = bus_bw;
+ bw_prev = core->power.bus_bw;
+ } else {
+ bw_kbps = bus->bw_max_kbps;
+ bw_prev = core->power.bus_bw ?
+ bw_kbps : 0;
+ }
+
+ bw_kbps = clamp_t(typeof(bw_kbps), bw_kbps,
+ bus->bw_min_kbps, bus->bw_max_kbps);
+
+ if (abs(bw_kbps - bw_prev) < BW_THRESHOLD && bw_prev)
+ continue;
+
+ ret = icc_set_bw(bus->icc, bw_kbps, 0);
+ if (ret)
+ return ret;
+
+ if (!strcmp(bus->name, "iris-ddr"))
+ core->power.bus_bw = bw_kbps;
+ }
+ }
+
+ return ret;
+}
+
+static int deassert_reset_control(struct iris_core *core)
+{
+ struct reset_info *rcinfo = NULL;
+ int ret = 0;
+ u32 i;
+
+ core->reset_count = core->platform_data->clk_rst_tbl_size;
+
+ for (i = 0; i < (core->reset_count - 1); i++) {
+ rcinfo = &core->reset_tbl[i];
+ ret = reset_control_deassert(rcinfo->rst);
+ if (ret) {
+ dev_err(core->dev, "deassert reset control failed. ret = %d\n", ret);
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+static int assert_reset_control(struct iris_core *core)
+{
+ struct reset_info *rcinfo = NULL;
+ int ret = 0, cnt = 0;
+ u32 i;
+
+ core->reset_count = core->platform_data->clk_rst_tbl_size;
+
+ for (i = 0; i < (core->reset_count - 1); i++) {
+ rcinfo = &core->reset_tbl[i];
+ if (!rcinfo->rst)
+ return -EINVAL;
+
+ ret = reset_control_assert(rcinfo->rst);
+ if (ret) {
+ dev_err(core->dev, "failed to assert reset control %s, ret = %d\n",
+ rcinfo->name, ret);
+ goto deassert_reset_control;
+ }
+ cnt++;
+
+ usleep_range(1000, 1100);
+ }
+
+ return ret;
+deassert_reset_control:
+ for (i = 0; i < cnt; i++) {
+ rcinfo = &core->reset_tbl[i];
+ reset_control_deassert(rcinfo->rst);
+ }
+
+ return ret;
+}
+
+int reset_ahb2axi_bridge(struct iris_core *core)
+{
+ int ret;
+
+ ret = assert_reset_control(core);
+ if (ret)
+ return ret;
+
+ ret = deassert_reset_control(core);
+
+ return ret;
+}
+
+int disable_unprepare_clock(struct iris_core *core, const char *clk_name)
+{
+ struct clock_info *cl;
+ bool found = false;
+ u32 i;
+
+ core->clk_count = core->platform_data->clk_tbl_size;
+
+ for (i = 0; i < core->clk_count; i++) {
+ cl = &core->clock_tbl[i];
+ if (!cl->clk)
+ return -EINVAL;
+
+ if (strcmp(cl->name, clk_name))
+ continue;
+
+ found = true;
+ clk_disable_unprepare(cl->clk);
+ cl->prev = 0;
+ break;
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return 0;
+}
+
+int prepare_enable_clock(struct iris_core *core, const char *clk_name)
+{
+ struct clock_info *cl;
+ bool found = false;
+ int ret = 0;
+ u32 i;
+
+ core->clk_count = core->platform_data->clk_tbl_size;
+
+ for (i = 0; i < core->clk_count; i++) {
+ cl = &core->clock_tbl[i];
+ if (!cl->clk)
+ return -EINVAL;
+
+ if (strcmp(cl->name, clk_name))
+ continue;
+
+ found = true;
+
+ ret = clk_prepare_enable(cl->clk);
+ if (ret) {
+ dev_err(core->dev, "failed to enable clock %s\n", cl->name);
+ return ret;
+ }
+
+ if (!__clk_is_enabled(cl->clk)) {
+ clk_disable_unprepare(cl->clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return ret;
+}
+
int init_resources(struct iris_core *core)
{
int ret;
@@ -31,6 +31,14 @@ struct reset_info {
const char *name;
};
+int enable_power_domains(struct iris_core *core, const char *name);
+int disable_power_domains(struct iris_core *core, const char *name);
+int unvote_buses(struct iris_core *core);
+int vote_buses(struct iris_core *core, unsigned long bus_bw);
+int reset_ahb2axi_bridge(struct iris_core *core);
+int opp_set_rate(struct iris_core *core, u64 freq);
+int disable_unprepare_clock(struct iris_core *core, const char *clk_name);
+int prepare_enable_clock(struct iris_core *core, const char *clk_name);
int init_resources(struct iris_core *core);
#endif
@@ -3,6 +3,8 @@
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
+#include <linux/iopoll.h>
+
#include "vpu_iris3.h"
#include "iris_core.h"
#include "iris_helpers.h"
@@ -17,6 +19,9 @@ int write_register(struct iris_core *core, u32 reg, u32 value)
if (ret)
return ret;
+ if (!core->power_enabled)
+ return -EINVAL;
+
base_addr = core->reg_base;
base_addr += reg;
writel_relaxed(value, base_addr);
@@ -27,10 +32,46 @@ int write_register(struct iris_core *core, u32 reg, u32 value)
return ret;
}
+int write_register_masked(struct iris_core *core, u32 reg, u32 value, u32 mask)
+{
+ void __iomem *base_addr;
+ u32 prev_val, new_val;
+ int ret;
+
+ ret = check_core_lock(core);
+ if (ret)
+ return ret;
+
+ if (!core->power_enabled)
+ return -EINVAL;
+
+ base_addr = core->reg_base;
+ base_addr += reg;
+
+ prev_val = readl_relaxed(base_addr);
+ /*
+ * Memory barrier to ensure register read is correct
+ */
+ rmb();
+
+ new_val = (prev_val & ~mask) | (value & mask);
+
+ writel_relaxed(new_val, base_addr);
+ /*
+ * Memory barrier to make sure value is written into the register.
+ */
+ wmb();
+
+ return ret;
+}
+
int read_register(struct iris_core *core, u32 reg, u32 *value)
{
void __iomem *base_addr;
+ if (!core->power_enabled)
+ return -EINVAL;
+
base_addr = core->reg_base;
*value = readl_relaxed(base_addr + reg);
@@ -41,6 +82,52 @@ int read_register(struct iris_core *core, u32 reg, u32 *value)
return 0;
}
+int read_register_with_poll_timeout(struct iris_core *core, u32 reg,
+ u32 mask, u32 exp_val, u32 sleep_us,
+ u32 timeout_us)
+{
+ void __iomem *base_addr;
+ u32 val = 0;
+ int ret;
+
+ if (!core->power_enabled)
+ return -EINVAL;
+
+ base_addr = core->reg_base;
+
+ ret = readl_relaxed_poll_timeout(base_addr + reg, val, ((val & mask) == exp_val),
+ sleep_us, timeout_us);
+ /*
+ * Memory barrier to make sure value is read correctly from the
+ * register.
+ */
+ rmb();
+
+ return ret;
+}
+
+int set_preset_registers(struct iris_core *core)
+{
+ const struct reg_preset_info *reg_prst;
+ unsigned int prst_count;
+ int cnt, ret = 0;
+
+ reg_prst = core->platform_data->reg_prst_tbl;
+ prst_count = core->platform_data->reg_prst_tbl_size;
+
+ if (!reg_prst || !prst_count)
+ return 0;
+
+ for (cnt = 0; cnt < prst_count; cnt++) {
+ ret = write_register_masked(core, reg_prst[cnt].reg,
+ reg_prst[cnt].value, reg_prst[cnt].mask);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
static const struct compat_handle compat_handle[] = {
{
.compat = "qcom,sm8550-iris",
@@ -24,6 +24,9 @@ struct vpu_ops {
int (*raise_interrupt)(struct iris_core *core);
int (*clear_interrupt)(struct iris_core *core);
int (*watchdog)(struct iris_core *core, u32 intr_status);
+ int (*power_on)(struct iris_core *core);
+ int (*power_off)(struct iris_core *core);
+ int (*prepare_pc)(struct iris_core *core);
};
#define call_session_op(c, op, ...) \
@@ -32,11 +35,18 @@ struct vpu_ops {
struct vpu_session_ops {
int (*int_buf_size)(struct iris_inst *inst, enum iris_buffer_type type);
+ u64 (*calc_freq)(struct iris_inst *inst, u32 data_size);
+ int (*calc_bw)(struct iris_inst *inst, struct bus_vote_data *data);
};
int init_vpu(struct iris_core *core);
int write_register(struct iris_core *core, u32 reg, u32 value);
+int write_register_masked(struct iris_core *core, u32 reg, u32 value, u32 mask);
int read_register(struct iris_core *core, u32 reg, u32 *value);
+int read_register_with_poll_timeout(struct iris_core *core, u32 reg,
+ u32 mask, u32 exp_val, u32 sleep_us,
+ u32 timeout_us);
+int set_preset_registers(struct iris_core *core);
#endif
@@ -5,73 +5,105 @@
#include <linux/delay.h>
+#include "iris_hfi.h"
#include "vpu_iris3.h"
#include "vpu_iris3_buffer.h"
+#include "vpu_iris3_power.h"
#define VIDEO_ARCH_LX 1
-#define CPU_BASE_OFFS_IRIS3 0x000A0000
-
-#define CPU_CS_BASE_OFFS_IRIS3 (CPU_BASE_OFFS_IRIS3)
-#define CPU_IC_BASE_OFFS_IRIS3 (CPU_BASE_OFFS_IRIS3)
-
-#define CPU_CS_VCICMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x24)
-#define CPU_CS_VCICMDARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x28)
-
-#define CPU_CS_A2HSOFTINTCLR_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x1C)
+#define VCODEC_BASE_OFFS_IRIS3 0x00000000
+#define AON_MVP_NOC_RESET 0x0001F000
+#define CPU_BASE_OFFS_IRIS3 0x000A0000
+#define AON_BASE_OFFS 0x000E0000
+#define CPU_CS_BASE_OFFS_IRIS3 (CPU_BASE_OFFS_IRIS3)
+#define CPU_IC_BASE_OFFS_IRIS3 (CPU_BASE_OFFS_IRIS3)
+#define CPU_CS_A2HSOFTINTCLR_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x1C)
+#define CPU_CS_VCICMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x24)
+#define CPU_CS_VCICMDARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x28)
/* HFI_CTRL_INIT */
-#define CPU_CS_SCIACMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x48)
-
+#define CPU_CS_SCIACMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x48)
/* HFI_CTRL_STATUS */
-#define CPU_CS_SCIACMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x4C)
-#define CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3 0x40000000
-
-#define CPU_CS_H2XSOFTINTEN_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x148)
-
-#define CPU_CS_X2RPMH_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x168)
-
-/* UC_REGION_ADDR */
-#define CPU_CS_SCIBARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x64)
-
-/* UC_REGION_ADDR */
-#define CPU_CS_SCIBARG2_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x68)
-
+#define CPU_CS_SCIACMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x4C)
/* HFI_QTBL_INFO */
-#define CPU_CS_SCIACMDARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x50)
-
+#define CPU_CS_SCIACMDARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x50)
/* HFI_QTBL_ADDR */
-#define CPU_CS_SCIACMDARG2_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x54)
-
+#define CPU_CS_SCIACMDARG2_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x54)
/* SFR_ADDR */
-#define CPU_CS_SCIBCMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x5C)
-
-#define UC_REGION_ADDR_IRIS3 CPU_CS_SCIBARG1_IRIS3
-#define UC_REGION_SIZE_IRIS3 CPU_CS_SCIBARG2_IRIS3
-
-#define QTBL_INFO_IRIS3 CPU_CS_SCIACMDARG1_IRIS3
-#define QTBL_ADDR_IRIS3 CPU_CS_SCIACMDARG2_IRIS3
-
-#define SFR_ADDR_IRIS3 CPU_CS_SCIBCMD_IRIS3
+#define CPU_CS_SCIBCMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x5C)
+#define CPU_CS_SCIBCMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x60)
+/* UC_REGION_ADDR */
+#define CPU_CS_SCIBARG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x64)
+/* UC_REGION_ADDR */
+#define CPU_CS_SCIBARG2_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x68)
+#define CPU_CS_H2XSOFTINTEN_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x148)
+#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS_IRIS3 + 0x160)
+#define CPU_CS_X2RPMH_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x168)
-#define CTRL_INIT_IRIS3 CPU_CS_SCIACMD_IRIS3
+#define CPU_IC_SOFTINT_IRIS3 (CPU_IC_BASE_OFFS_IRIS3 + 0x150)
+#define CPU_IC_SOFTINT_H2A_SHFT_IRIS3 0x0
-#define CTRL_STATUS_IRIS3 CPU_CS_SCIACMDARG0_IRIS3
-#define CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3 0xfe
-#define CTRL_ERROR_STATUS__M_IRIS3 \
- CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3
-#define CTRL_INIT_IDLE_MSG_BMSK_IRIS3 \
- CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3 0x40000000
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3 0xfe
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY_IRIS3 0x100
+
+#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
+#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004)
+
+#define WRAPPER_BASE_OFFS_IRIS3 0x000B0000
+#define WRAPPER_INTR_STATUS_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x0C)
+#define WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3 0x8
+#define WRAPPER_INTR_STATUS_A2H_BMSK_IRIS3 0x4
+
+#define WRAPPER_INTR_MASK_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x10)
+#define WRAPPER_INTR_MASK_A2HWD_BMSK_IRIS3 0x8
+#define WRAPPER_INTR_MASK_A2HCPU_BMSK_IRIS3 0x4
+
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x54)
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x58)
+#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS_IRIS3 + 0x5C)
+#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS_IRIS3 + 0x60)
+#define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS_IRIS3 + 0x80)
+#define WRAPPER_CORE_CLOCK_CONFIG_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x88)
+
+#define WRAPPER_TZ_BASE_OFFS 0x000C0000
+#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10)
+#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
+#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
+
+#define CTRL_INIT_IRIS3 CPU_CS_SCIACMD_IRIS3
+#define CTRL_STATUS_IRIS3 CPU_CS_SCIACMDARG0_IRIS3
+#define CTRL_ERROR_STATUS__M_IRIS3 CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3
+#define CTRL_INIT_IDLE_MSG_BMSK_IRIS3 CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3
+#define CTRL_STATUS_PC_READY_IRIS3 CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY_IRIS3
+
+#define QTBL_INFO_IRIS3 CPU_CS_SCIACMDARG1_IRIS3
+#define QTBL_ADDR_IRIS3 CPU_CS_SCIACMDARG2_IRIS3
+#define SFR_ADDR_IRIS3 CPU_CS_SCIBCMD_IRIS3
+#define UC_REGION_ADDR_IRIS3 CPU_CS_SCIBARG1_IRIS3
+#define UC_REGION_SIZE_IRIS3 CPU_CS_SCIBARG2_IRIS3
+
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
+
+#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS_IRIS3 + 0x70)
+
+static int interrupt_init_iris3(struct iris_core *core)
+{
+ u32 mask_val;
+ int ret;
-#define WRAPPER_BASE_OFFS_IRIS3 0x000B0000
-#define WRAPPER_INTR_STATUS_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x0C)
-#define WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3 0x8
-#define WRAPPER_INTR_STATUS_A2H_BMSK_IRIS3 0x4
+ ret = read_register(core, WRAPPER_INTR_MASK_IRIS3, &mask_val);
+ if (ret)
+ return ret;
-#define CPU_IC_SOFTINT_IRIS3 (CPU_IC_BASE_OFFS_IRIS3 + 0x150)
-#define CPU_IC_SOFTINT_H2A_SHFT_IRIS3 0x0
+ mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK_IRIS3 |
+ WRAPPER_INTR_MASK_A2HCPU_BMSK_IRIS3);
+ ret = write_register(core, WRAPPER_INTR_MASK_IRIS3, mask_val);
-#define WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3 0x8
+ return ret;
+}
static int setup_ucregion_memory_map_iris3(struct iris_core *core)
{
@@ -196,15 +228,335 @@ static int watchdog_iris3(struct iris_core *core, u32 intr_status)
return 0;
}
+static bool is_iris3_hw_power_collapsed(struct iris_core *core)
+{
+ u32 value = 0, pwr_status = 0;
+ int ret;
+
+ ret = read_register(core, WRAPPER_CORE_POWER_STATUS, &value);
+ if (ret)
+ return false;
+
+ pwr_status = value & BIT(1);
+
+ return pwr_status ? false : true;
+}
+
+static int power_off_iris3_hardware(struct iris_core *core)
+{
+ u32 value = 0;
+ int ret, i;
+
+ if (is_iris3_hw_power_collapsed(core))
+ goto disable_power;
+
+ dev_err(core->dev, "Video hw is power ON\n");
+
+ ret = read_register(core, WRAPPER_CORE_CLOCK_CONFIG_IRIS3, &value);
+ if (ret)
+ goto disable_power;
+
+ if (value) {
+ ret = write_register(core, WRAPPER_CORE_CLOCK_CONFIG_IRIS3, 0);
+ if (ret)
+ goto disable_power;
+ }
+
+ for (i = 0; i < core->cap[NUM_VPP_PIPE].value; i++) {
+ ret = read_register_with_poll_timeout(core, VCODEC_SS_IDLE_STATUSN + 4 * i,
+ 0x400000, 0x400000, 2000, 20000);
+ }
+
+ ret = write_register(core, AON_WRAPPER_MVP_NOC_RESET_REQ, 0x3);
+ if (ret)
+ goto disable_power;
+
+ ret = read_register_with_poll_timeout(core, AON_WRAPPER_MVP_NOC_RESET_ACK,
+ 0x3, 0x3, 200, 2000);
+ ret = write_register(core, AON_WRAPPER_MVP_NOC_RESET_REQ, 0x0);
+ if (ret)
+ goto disable_power;
+
+ ret = read_register_with_poll_timeout(core, AON_WRAPPER_MVP_NOC_RESET_ACK,
+ 0x3, 0x0, 200, 2000);
+
+ ret = write_register(core, CPU_CS_AHB_BRIDGE_SYNC_RESET, 0x3);
+ if (ret)
+ goto disable_power;
+ ret = write_register(core, CPU_CS_AHB_BRIDGE_SYNC_RESET, 0x2);
+ if (ret)
+ goto disable_power;
+ ret = write_register(core, CPU_CS_AHB_BRIDGE_SYNC_RESET, 0x0);
+ if (ret)
+ goto disable_power;
+
+disable_power:
+ ret = disable_power_domains(core, "vcodec");
+ if (ret) {
+ dev_err(core->dev, "disable power domain vcodec failed\n");
+ ret = 0;
+ }
+
+ disable_unprepare_clock(core, "vcodec_core");
+ if (ret) {
+ dev_err(core->dev, "disable unprepare vcodec_core failed\n");
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int power_off_iris3_controller(struct iris_core *core)
+{
+ int ret;
+
+ ret = write_register(core, CPU_CS_X2RPMH_IRIS3, 0x3);
+ if (ret)
+ goto disable_power;
+
+ ret = write_register_masked(core, AON_WRAPPER_MVP_NOC_LPI_CONTROL,
+ 0x1, BIT(0));
+ if (ret)
+ goto disable_power;
+
+ ret = read_register_with_poll_timeout(core, AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ 0x1, 0x1, 200, 2000);
+
+ ret = write_register_masked(core, WRAPPER_IRIS_CPU_NOC_LPI_CONTROL,
+ 0x1, BIT(0));
+ if (ret)
+ goto disable_power;
+
+ ret = read_register_with_poll_timeout(core, WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ 0x1, 0x1, 200, 2000);
+
+ ret = write_register(core, WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_IRIS3, 0x0);
+ if (ret)
+ goto disable_power;
+
+ ret = read_register_with_poll_timeout(core, WRAPPER_DEBUG_BRIDGE_LPI_STATUS_IRIS3,
+ 0xffffffff, 0x0, 200, 2000);
+
+ ret = write_register(core, WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG, 0x3);
+ if (ret)
+ goto disable_power;
+
+ ret = write_register(core, WRAPPER_TZ_QNS4PDXFIFO_RESET, 0x1);
+ if (ret)
+ goto disable_power;
+
+ ret = write_register(core, WRAPPER_TZ_QNS4PDXFIFO_RESET, 0x0);
+ if (ret)
+ goto disable_power;
+
+ ret = write_register(core, WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG, 0x0);
+ if (ret)
+ goto disable_power;
+
+disable_power:
+ disable_unprepare_clock(core, "core_clk");
+ if (ret) {
+ dev_err(core->dev, "disable unprepare core_clk failed\n");
+ ret = 0;
+ }
+
+ /* power down process */
+ ret = disable_power_domains(core, "iris-ctl");
+ if (ret) {
+ dev_err(core->dev, "disable power domain iris-ctl failed\n");
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int power_off_iris3(struct iris_core *core)
+{
+ if (!core->power_enabled)
+ return 0;
+
+ opp_set_rate(core, 0);
+ power_off_iris3_hardware(core);
+ power_off_iris3_controller(core);
+ unvote_buses(core);
+
+ if (!call_vpu_op(core, watchdog, core, core->intr_status))
+ disable_irq_nosync(core->irq);
+
+ core->power_enabled = false;
+
+ return 0;
+}
+
+static int power_on_iris3_controller(struct iris_core *core)
+{
+ int ret;
+
+ ret = enable_power_domains(core, "iris-ctl");
+ if (ret)
+ return ret;
+
+ ret = reset_ahb2axi_bridge(core);
+ if (ret)
+ goto err_disable_power;
+
+ ret = prepare_enable_clock(core, "gcc_video_axi0");
+ if (ret)
+ goto err_disable_power;
+
+ ret = prepare_enable_clock(core, "core_clk");
+ if (ret)
+ goto err_disable_clock;
+
+ return ret;
+
+err_disable_clock:
+ disable_unprepare_clock(core, "gcc_video_axi0");
+err_disable_power:
+ disable_power_domains(core, "iris-ctl");
+
+ return ret;
+}
+
+static int power_on_iris3_hardware(struct iris_core *core)
+{
+ int ret;
+
+ ret = enable_power_domains(core, "vcodec");
+ if (ret)
+ return ret;
+
+ ret = prepare_enable_clock(core, "vcodec_core");
+ if (ret)
+ goto err_disable_power;
+
+ return ret;
+
+err_disable_power:
+ disable_power_domains(core, "vcodec");
+
+ return ret;
+}
+
+static int power_on_iris3(struct iris_core *core)
+{
+ u32 freq = 0;
+ int ret;
+
+ if (core->power_enabled)
+ return 0;
+
+ if (!core_in_valid_state(core))
+ return -EINVAL;
+
+ ret = vote_buses(core, INT_MAX);
+ if (ret)
+ goto err;
+
+ ret = power_on_iris3_controller(core);
+ if (ret)
+ goto err_unvote_bus;
+
+ ret = power_on_iris3_hardware(core);
+ if (ret)
+ goto err_power_off_ctrl;
+
+ core->power_enabled = true;
+
+ freq = core->power.clk_freq ? core->power.clk_freq :
+ (u32)ULONG_MAX;
+
+ opp_set_rate(core, freq);
+
+ set_preset_registers(core);
+
+ interrupt_init_iris3(core);
+ core->intr_status = 0;
+ enable_irq(core->irq);
+
+ return ret;
+
+err_power_off_ctrl:
+ power_off_iris3_controller(core);
+err_unvote_bus:
+ unvote_buses(core);
+err:
+ core->power_enabled = false;
+
+ return ret;
+}
+
+static int prepare_pc_iris3(struct iris_core *core)
+{
+ u32 wfi_status = 0, idle_status = 0, pc_ready = 0;
+ u32 ctrl_status = 0;
+ int ret;
+
+ ret = read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
+ if (ret)
+ return ret;
+
+ pc_ready = ctrl_status & CTRL_STATUS_PC_READY_IRIS3;
+ idle_status = ctrl_status & BIT(30);
+
+ if (pc_ready)
+ return 0;
+
+ ret = read_register(core, WRAPPER_TZ_CPU_STATUS, &wfi_status);
+ if (ret)
+ return ret;
+
+ wfi_status &= BIT(0);
+ if (!wfi_status || !idle_status)
+ goto skip_power_off;
+
+ ret = prepare_pc(core);
+ if (ret)
+ goto skip_power_off;
+
+ ret = read_register_with_poll_timeout(core, CTRL_STATUS_IRIS3,
+ CTRL_STATUS_PC_READY_IRIS3,
+ CTRL_STATUS_PC_READY_IRIS3, 250, 2500);
+ if (ret)
+ goto skip_power_off;
+
+ ret = read_register_with_poll_timeout(core, WRAPPER_TZ_CPU_STATUS,
+ BIT(0), 0x1, 250, 2500);
+ if (ret)
+ goto skip_power_off;
+
+ return ret;
+
+skip_power_off:
+ ret = read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
+ if (ret)
+ return ret;
+
+ ret = read_register(core, WRAPPER_TZ_CPU_STATUS, &wfi_status);
+ if (ret)
+ return ret;
+
+ wfi_status &= BIT(0);
+ dev_err(core->dev, "Skip PC, wfi=%#x, idle=%#x, pcr=%#x, ctrl=%#x)\n",
+ wfi_status, idle_status, pc_ready, ctrl_status);
+
+ return -EAGAIN;
+}
+
static const struct vpu_ops iris3_ops = {
.boot_firmware = boot_firmware_iris3,
.raise_interrupt = raise_interrupt_iris3,
.clear_interrupt = clear_interrupt_iris3,
.watchdog = watchdog_iris3,
+ .power_on = power_on_iris3,
+ .power_off = power_off_iris3,
+ .prepare_pc = prepare_pc_iris3,
};
static const struct vpu_session_ops iris3_session_ops = {
.int_buf_size = iris_int_buf_size_iris3,
+ .calc_freq = iris_calc_freq_iris3,
+ .calc_bw = iris_calc_bw_iris3,
};
int init_iris3(struct iris_core *core)
new file mode 100644
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_core.h"
+#include "iris_instance.h"
+#include "iris_helpers.h"
+#include "platform_common.h"
+#include "vpu_iris3_power.h"
+
+u64 iris_calc_freq_iris3(struct iris_inst *inst, u32 data_size)
+{
+ u64 vsp_cycles = 0, vpp_cycles = 0, fw_cycles = 0;
+ u64 fw_vpp_cycles = 0, bitrate = 0, freq = 0;
+ u32 base_cycles = 0, fps, mbpf;
+ u32 height = 0, width = 0;
+ struct v4l2_format *inp_f;
+ u32 mbs_per_second;
+
+ inp_f = inst->fmt_src;
+ width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+ height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+
+ mbpf = NUM_MBS_PER_FRAME(height, width);
+ fps = inst->max_rate;
+ mbs_per_second = mbpf * fps;
+
+ fw_cycles = fps * inst->cap[MB_CYCLES_FW].value;
+ fw_vpp_cycles = fps * inst->cap[MB_CYCLES_FW_VPP].value;
+
+ vpp_cycles = mbs_per_second * inst->cap[MB_CYCLES_VPP].value /
+ inst->cap[PIPE].value;
+ vpp_cycles += max(vpp_cycles / 20, fw_vpp_cycles);
+
+ if (inst->cap[PIPE].value > 1)
+ vpp_cycles += div_u64(vpp_cycles * 59, 1000);
+
+ base_cycles = inst->cap[MB_CYCLES_VSP].value;
+ bitrate = fps * data_size * 8;
+ vsp_cycles = bitrate;
+
+ if (inst->codec == VP9) {
+ vsp_cycles = div_u64(vsp_cycles * 170, 100);
+ } else {
+ base_cycles = 0;
+ vsp_cycles = div_u64(vsp_cycles, 2);
+ }
+ vsp_cycles = div_u64(vsp_cycles * 21, 20);
+
+ if (inst->cap[STAGE].value == STAGE_1)
+ vsp_cycles = vsp_cycles * 3;
+
+ vsp_cycles += mbs_per_second * base_cycles;
+
+ freq = max3(vpp_cycles, vsp_cycles, fw_cycles);
+
+ return freq;
+}
+
+int iris_calc_bw_iris3(struct iris_inst *inst, struct bus_vote_data *data)
+{
+ const struct bw_info *bw_tbl = NULL;
+ unsigned int num_rows = 0;
+ unsigned int i, mbs, mbps;
+ struct iris_core *core;
+
+ if (!data)
+ return 0;
+
+ core = inst->core;
+
+ mbs = (ALIGN(data->height, 16) / 16) * (ALIGN(data->width, 16) / 16);
+ mbps = mbs * data->fps;
+ if (mbps == 0)
+ return 0;
+
+ bw_tbl = core->platform_data->bw_tbl_dec;
+ num_rows = core->platform_data->bw_tbl_dec_size;
+
+ if (!bw_tbl || num_rows == 0)
+ return 0;
+
+ for (i = 0; i < num_rows; i++) {
+ if (i != 0 && mbps > bw_tbl[i].mbs_per_sec)
+ break;
+
+ if (is_10bit_colorformat(data->color_formats[0]))
+ data->bus_bw = bw_tbl[i].bw_ddr_10bit;
+ else
+ data->bus_bw = bw_tbl[i].bw_ddr;
+ }
+
+ dev_info(core->dev, "bus_bw %llu\n", data->bus_bw);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __H_VPU_IRIS3_POWER_H__
+#define __H_VPU_IRIS3_POWER_H__
+
+u64 iris_calc_freq_iris3(struct iris_inst *inst, u32 data_size);
+int iris_calc_bw_iris3(struct iris_inst *inst,
+ struct bus_vote_data *vote_data);
+
+#endif