2023-07-28 15:32:29

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 00/33] Qualcomm video decoder/encoder driver

This patch series introduces support for Qualcomm new video acceleration
hardware architecture, used for video stream decoding/encoding. This driver
is based on new communication protocol between video hardware and application
processor.

This driver comes with below capabilities:
- V4L2 complaint video driver with M2M and STREAMING capability.
- Supports H264, H265, VP9 decoders.
- Supports H264, H265 encoders.

This driver comes with below features:
- Centralized resource and memory management.
- Centralized management of core and instance states.
- Defines platform specific capabilities and features. As a results, it provides
a single point of control to enable/disable a given feature depending on
specific platform capabilities.
- Handles hardware interdependent configurations. For a given configuration from
client, the driver checks for hardware dependent configuration/s and updates
the same.
- Handles multiple complex video scenarios involving state transitions - DRC,
Drain, Seek, back to back DRC, DRC during Drain sequence, DRC during Seek
sequence.
- Introduces a flexible way for driver to subscribe for any property with
hardware. Hardware would inform driver with those subscribed property with any
change in value.
- Introduces performance (clock and bus) model based on new hardware
architecture.
- Introduces multi thread safe design to handle communication between client and
hardware.
- Adapts encoder quality improvements available in new hardware architecture.
- Implements asynchronous communication with hardware to achieve better
experience in low latency usecases.
- Supports multi stage hardware architecture for encode/decode.
- Output and capture planes are controlled independently. Thereby providing a
way to reconfigure individual plane.
- Hardware packetization layer supports synchronization between configuration
packet and data packet.
- Introduces a flexibility to receive a hardware response for a given command
packet.
- Native hardware support of LAST flag which is mandatory to align with port
reconfiguration and DRAIN sequence as per V4L guidelines.
- Native hardware support for drain sequence.

I think that the driver is in good shape for mainline kernel, and I hope the
review comments will help to improve it, so please do review, and make comments.

Dikshita Agarwal (17):
iris: vidc: add core functions
iris: add vidc wrapper file
iris: vidc: add vb2 ops
iris: vidc: add helpers for memory management
iris: vidc: add helper functions for resource management
iris: vidc: add helper functions for power management
iris: add helpers for media format
iris: vidc: add PIL functionality for video firmware
iris: platform: add platform files
iris: platform: sm8550: add capability file for sm8550
iris: variant: add helper functions for register handling
iris: variant: iris3: add iris3 specific ops
iris: variant: iris3: add helpers for buffer size calculations
iris: variant: iris3: add helper for bus and clock calculation
iris: variant: iris: implement the logic to compute bus bandwidth
iris: variant: iris3: implement logic to compute clock frequency
iris: enable building of iris video driver

Vikash Garodia (16):
MAINTAINERS: Add Qualcomm Iris video accelerator driver
iris: vidc: add v4l2 wrapper file
iris: vidc: define video core and instance context
iris: iris: add video encoder files
iris: vidc: add video decoder files
iris: vidc: add control files
iris: vidc: add helper functions
iris: vidc: add helpers for state management
iris: add vidc buffer files
iris: vidc: define various structures and enum
iris: vidc: hfi: add Host Firmware Interface (HFI)
iris: vidc: hfi: add Host Firmware Interface (HFI) response handling
iris: vidc: hfi: add helpers for handling shared queues
iris: vidc: hfi: Add packetization layer
iris: vidc: hfi: defines HFI properties and enums
iris: vidc: add debug files

MAINTAINERS | 10 +
drivers/media/platform/qcom/Kconfig | 1 +
drivers/media/platform/qcom/Makefile | 1 +
drivers/media/platform/qcom/iris/Kconfig | 15 +
drivers/media/platform/qcom/iris/Makefile | 46 +
.../iris/platform/common/inc/msm_vidc_platform.h | 305 ++
.../iris/platform/common/src/msm_vidc_platform.c | 2499 ++++++++++++
.../iris/platform/sm8550/inc/msm_vidc_sm8550.h | 14 +
.../iris/platform/sm8550/src/msm_vidc_sm8550.c | 1727 ++++++++
.../iris/variant/common/inc/msm_vidc_variant.h | 22 +
.../iris/variant/common/src/msm_vidc_variant.c | 163 +
.../qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h | 1481 +++++++
.../iris/variant/iris3/inc/msm_vidc_buffer_iris3.h | 19 +
.../qcom/iris/variant/iris3/inc/msm_vidc_iris3.h | 15 +
.../iris/variant/iris3/inc/msm_vidc_power_iris3.h | 17 +
.../iris/variant/iris3/inc/perf_static_model.h | 229 ++
.../iris/variant/iris3/src/msm_vidc_buffer_iris3.c | 595 +++
.../iris/variant/iris3/src/msm_vidc_bus_iris3.c | 884 ++++
.../iris/variant/iris3/src/msm_vidc_clock_iris3.c | 627 +++
.../qcom/iris/variant/iris3/src/msm_vidc_iris3.c | 954 +++++
.../iris/variant/iris3/src/msm_vidc_power_iris3.c | 345 ++
.../media/platform/qcom/iris/vidc/inc/firmware.h | 18 +
.../platform/qcom/iris/vidc/inc/hfi_command.h | 190 +
.../media/platform/qcom/iris/vidc/inc/hfi_packet.h | 52 +
.../platform/qcom/iris/vidc/inc/hfi_property.h | 666 +++
.../platform/qcom/iris/vidc/inc/msm_media_info.h | 599 +++
.../media/platform/qcom/iris/vidc/inc/msm_vdec.h | 40 +
.../media/platform/qcom/iris/vidc/inc/msm_venc.h | 34 +
.../media/platform/qcom/iris/vidc/inc/msm_vidc.h | 60 +
.../platform/qcom/iris/vidc/inc/msm_vidc_buffer.h | 32 +
.../platform/qcom/iris/vidc/inc/msm_vidc_control.h | 26 +
.../platform/qcom/iris/vidc/inc/msm_vidc_core.h | 165 +
.../platform/qcom/iris/vidc/inc/msm_vidc_debug.h | 186 +
.../platform/qcom/iris/vidc/inc/msm_vidc_driver.h | 352 ++
.../platform/qcom/iris/vidc/inc/msm_vidc_inst.h | 207 +
.../qcom/iris/vidc/inc/msm_vidc_internal.h | 787 ++++
.../platform/qcom/iris/vidc/inc/msm_vidc_memory.h | 83 +
.../platform/qcom/iris/vidc/inc/msm_vidc_power.h | 94 +
.../platform/qcom/iris/vidc/inc/msm_vidc_state.h | 102 +
.../platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h | 77 +
.../platform/qcom/iris/vidc/inc/msm_vidc_vb2.h | 39 +
.../media/platform/qcom/iris/vidc/inc/resources.h | 259 ++
.../media/platform/qcom/iris/vidc/inc/venus_hfi.h | 66 +
.../platform/qcom/iris/vidc/inc/venus_hfi_queue.h | 89 +
.../qcom/iris/vidc/inc/venus_hfi_response.h | 26 +
.../media/platform/qcom/iris/vidc/src/firmware.c | 294 ++
.../media/platform/qcom/iris/vidc/src/hfi_packet.c | 657 +++
.../media/platform/qcom/iris/vidc/src/msm_vdec.c | 2091 ++++++++++
.../media/platform/qcom/iris/vidc/src/msm_venc.c | 1484 +++++++
.../media/platform/qcom/iris/vidc/src/msm_vidc.c | 841 ++++
.../platform/qcom/iris/vidc/src/msm_vidc_buffer.c | 290 ++
.../platform/qcom/iris/vidc/src/msm_vidc_control.c | 824 ++++
.../platform/qcom/iris/vidc/src/msm_vidc_debug.c | 581 +++
.../platform/qcom/iris/vidc/src/msm_vidc_driver.c | 4276 ++++++++++++++++++++
.../platform/qcom/iris/vidc/src/msm_vidc_memory.c | 448 ++
.../platform/qcom/iris/vidc/src/msm_vidc_power.c | 560 +++
.../platform/qcom/iris/vidc/src/msm_vidc_probe.c | 660 +++
.../platform/qcom/iris/vidc/src/msm_vidc_state.c | 1607 ++++++++
.../platform/qcom/iris/vidc/src/msm_vidc_v4l2.c | 953 +++++
.../platform/qcom/iris/vidc/src/msm_vidc_vb2.c | 605 +++
.../media/platform/qcom/iris/vidc/src/resources.c | 1321 ++++++
.../media/platform/qcom/iris/vidc/src/venus_hfi.c | 1503 +++++++
.../platform/qcom/iris/vidc/src/venus_hfi_queue.c | 537 +++
.../qcom/iris/vidc/src/venus_hfi_response.c | 1607 ++++++++
64 files changed, 35357 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/Kconfig
create mode 100644 drivers/media/platform/qcom/iris/Makefile
create mode 100644 drivers/media/platform/qcom/iris/platform/common/inc/msm_vidc_platform.h
create mode 100644 drivers/media/platform/qcom/iris/platform/common/src/msm_vidc_platform.c
create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/inc/msm_vidc_sm8550.h
create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/src/msm_vidc_sm8550.c
create mode 100644 drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h
create mode 100644 drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_buffer_iris3.h
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/perf_static_model.h
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_buffer_iris3.c
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_bus_iris3.c
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_clock_iris3.c
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/firmware.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_command.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_packet.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_property.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_media_info.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vdec.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_buffer.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_control.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_core.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_driver.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_inst.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_memory.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_power.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/resources.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_queue.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/firmware.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/hfi_packet.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vdec.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_venc.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_buffer.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_control.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_driver.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_memory.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_power.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_probe.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_v4l2.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/resources.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_queue.c
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c

--
2.7.4



2023-07-28 15:32:36

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 18/33] iris: vidc: hfi: add Host Firmware Interface (HFI)

This implements the interface for communication between
host driver and firmware through interface commands and messages.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../media/platform/qcom/iris/vidc/inc/venus_hfi.h | 66 +
.../media/platform/qcom/iris/vidc/src/venus_hfi.c | 1503 ++++++++++++++++++++
2 files changed, 1569 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c

diff --git a/drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h b/drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h
new file mode 100644
index 0000000..99c504d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _VENUS_HFI_H_
+#define _VENUS_HFI_H_
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/irqreturn.h>
+#include <linux/regulator/consumer.h>
+
+#include "msm_vidc_core.h"
+#include "msm_vidc_inst.h"
+#include "msm_vidc_internal.h"
+
+#define VIDC_MAX_PC_SKIP_COUNT 10
+
+struct vidc_buffer_addr_info {
+ enum msm_vidc_buffer_type buffer_type;
+ u32 buffer_size;
+ u32 num_buffers;
+ u32 align_device_addr;
+ u32 extradata_addr;
+ u32 extradata_size;
+ u32 response_required;
+};
+
+int __strict_check(struct msm_vidc_core *core,
+ const char *function);
+int venus_hfi_session_property(struct msm_vidc_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload,
+ u32 payload_size);
+int venus_hfi_session_command(struct msm_vidc_inst *inst,
+ u32 cmd, enum msm_vidc_port_type port,
+ u32 payload_type, void *payload, u32 payload_size);
+int venus_hfi_queue_buffer(struct msm_vidc_inst *inst,
+ struct msm_vidc_buffer *buffer);
+int venus_hfi_release_buffer(struct msm_vidc_inst *inst,
+ struct msm_vidc_buffer *buffer);
+int venus_hfi_start(struct msm_vidc_inst *inst, enum msm_vidc_port_type port);
+int venus_hfi_stop(struct msm_vidc_inst *inst, enum msm_vidc_port_type port);
+int venus_hfi_session_close(struct msm_vidc_inst *inst);
+int venus_hfi_session_open(struct msm_vidc_inst *inst);
+int venus_hfi_session_pause(struct msm_vidc_inst *inst, enum msm_vidc_port_type port);
+int venus_hfi_session_resume(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port, u32 payload);
+int venus_hfi_session_drain(struct msm_vidc_inst *inst, enum msm_vidc_port_type port);
+int venus_hfi_session_set_codec(struct msm_vidc_inst *inst);
+int venus_hfi_core_init(struct msm_vidc_core *core);
+int venus_hfi_core_deinit(struct msm_vidc_core *core, bool force);
+int venus_hfi_suspend(struct msm_vidc_core *core);
+int venus_hfi_reserve_hardware(struct msm_vidc_inst *inst, u32 duration);
+int venus_hfi_scale_clocks(struct msm_vidc_inst *inst, u64 freq);
+int venus_hfi_scale_buses(struct msm_vidc_inst *inst, u64 bw_ddr, u64 bw_llcc);
+int venus_hfi_set_ir_period(struct msm_vidc_inst *inst, u32 ir_type,
+ enum msm_vidc_inst_capability_type cap_id);
+void venus_hfi_pm_work_handler(struct work_struct *work);
+irqreturn_t venus_hfi_isr(int irq, void *data);
+irqreturn_t venus_hfi_isr_handler(int irq, void *data);
+int __prepare_pc(struct msm_vidc_core *core);
+
+#endif // _VENUS_HFI_H_
diff --git a/drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c b/drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c
new file mode 100644
index 0000000..87cac76
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c
@@ -0,0 +1,1503 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/firmware.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/irqreturn.h>
+#include <linux/of_address.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/soc/qcom/mdt_loader.h>
+#include <linux/soc/qcom/smem.h>
+
+#include "firmware.h"
+#include "hfi_packet.h"
+#include "msm_vidc_control.h"
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_memory.h"
+#include "msm_vidc_platform.h"
+#include "msm_vidc_power.h"
+#include "msm_vidc_state.h"
+#include "venus_hfi.h"
+#include "venus_hfi_queue.h"
+#include "venus_hfi_response.h"
+
+#define update_offset(offset, val) ((offset) += (val))
+#define update_timestamp(ts, val) \
+ do { \
+ do_div((ts), NSEC_PER_USEC); \
+ (ts) += (val); \
+ (ts) *= NSEC_PER_USEC; \
+ } while (0)
+
+static int __resume(struct msm_vidc_core *core);
+static int __suspend(struct msm_vidc_core *core);
+
+static void __fatal_error(bool fatal)
+{
+ WARN_ON(fatal);
+}
+
+int __strict_check(struct msm_vidc_core *core, const char *function)
+{
+ bool fatal = !mutex_is_locked(&core->lock);
+
+ __fatal_error(fatal);
+
+ if (fatal)
+ d_vpr_e("%s: strict check failed\n", function);
+
+ return fatal ? -EINVAL : 0;
+}
+
+static bool __valdiate_session(struct msm_vidc_core *core,
+ struct msm_vidc_inst *inst, const char *func)
+{
+ bool valid = false;
+ struct msm_vidc_inst *temp;
+ int rc = 0;
+
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return false;
+
+ list_for_each_entry(temp, &core->instances, list) {
+ if (temp == inst) {
+ valid = true;
+ break;
+ }
+ }
+ if (!valid)
+ i_vpr_e(inst, "%s: invalid session\n", func);
+
+ return valid;
+}
+
+static void __schedule_power_collapse_work(struct msm_vidc_core *core)
+{
+ if (!core->capabilities[SW_PC].value) {
+ d_vpr_l("software power collapse not enabled\n");
+ return;
+ }
+
+ if (!mod_delayed_work(core->pm_workq, &core->pm_work,
+ msecs_to_jiffies(core->capabilities[SW_PC_DELAY].value))) {
+ d_vpr_h("power collapse already scheduled\n");
+ } else {
+ d_vpr_l("power collapse scheduled for %d ms\n",
+ core->capabilities[SW_PC_DELAY].value);
+ }
+}
+
+static void __cancel_power_collapse_work(struct msm_vidc_core *core)
+{
+ if (!core->capabilities[SW_PC].value)
+ return;
+
+ cancel_delayed_work(&core->pm_work);
+}
+
+static void __flush_debug_queue(struct msm_vidc_core *core,
+ u8 *packet, u32 packet_size)
+{
+ u8 *log;
+ struct hfi_debug_header *pkt;
+ bool local_packet = false;
+ enum vidc_msg_prio_fw log_level_fw = msm_fw_debug;
+
+ if (!packet || !packet_size) {
+ packet = vzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE);
+ if (!packet) {
+ d_vpr_e("%s: allocation failed\n", __func__);
+ return;
+ }
+ packet_size = VIDC_IFACEQ_VAR_HUGE_PKT_SIZE;
+
+ local_packet = true;
+
+ /*
+ * Local packet is used when error occurred.
+ * It is good to print these logs to printk as well.
+ */
+ log_level_fw |= FW_PRINTK;
+ }
+
+ while (!venus_hfi_queue_dbg_read(core, packet)) {
+ pkt = (struct hfi_debug_header *)packet;
+
+ if (pkt->size < sizeof(struct hfi_debug_header)) {
+ d_vpr_e("%s: invalid pkt size %d\n",
+ __func__, pkt->size);
+ continue;
+ }
+ if (pkt->size >= packet_size) {
+ d_vpr_e("%s: pkt size[%d] >= packet_size[%d]\n",
+ __func__, pkt->size, packet_size);
+ continue;
+ }
+
+ packet[pkt->size] = '\0';
+ /*
+ * All fw messages starts with new line character. This
+ * causes dprintk to print this message in two lines
+ * in the kernel log. Ignoring the first character
+ * from the message fixes this to print it in a single
+ * line.
+ */
+ log = (u8 *)packet + sizeof(struct hfi_debug_header) + 1;
+ dprintk_firmware(log_level_fw, "%s", log);
+ }
+
+ if (local_packet)
+ vfree(packet);
+}
+
+static int __cmdq_write(struct msm_vidc_core *core, void *pkt)
+{
+ int rc;
+
+ rc = __resume(core);
+ if (rc)
+ return rc;
+
+ rc = venus_hfi_queue_cmd_write(core, pkt);
+ if (!rc)
+ __schedule_power_collapse_work(core);
+
+ return rc;
+}
+
+static int __sys_set_debug(struct msm_vidc_core *core, u32 debug)
+{
+ int rc = 0;
+
+ rc = hfi_packet_sys_debug_config(core, core->packet,
+ core->packet_size, debug);
+ if (rc)
+ goto exit;
+
+ rc = __cmdq_write(core, core->packet);
+ if (rc)
+ goto exit;
+
+exit:
+ if (rc)
+ d_vpr_e("Debug mode setting to FW failed\n");
+
+ return rc;
+}
+
+static int __sys_set_power_control(struct msm_vidc_core *core, bool enable)
+{
+ int rc = 0;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF)) {
+ d_vpr_e("%s: skipping as power control hanfoff was not done\n",
+ __func__);
+ return rc;
+ }
+
+ if (!core_in_valid_state(core)) {
+ d_vpr_e("%s: invalid core state %s\n",
+ __func__, core_state_name(core->state));
+ return rc;
+ }
+
+ rc = hfi_packet_sys_intraframe_powercollapse(core, core->packet,
+ core->packet_size, enable);
+ if (rc)
+ return rc;
+
+ rc = __cmdq_write(core, core->packet);
+ if (rc)
+ return rc;
+
+ rc = msm_vidc_change_core_sub_state(core, 0, CORE_SUBSTATE_FW_PWR_CTRL, __func__);
+ if (rc)
+ return rc;
+
+ d_vpr_h("%s: set hardware power control successful\n", __func__);
+
+ return rc;
+}
+
+int __prepare_pc(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = hfi_packet_sys_pc_prep(core, core->packet, core->packet_size);
+ if (rc) {
+ d_vpr_e("Failed to create sys pc prep pkt\n");
+ goto err_pc_prep;
+ }
+
+ if (__cmdq_write(core, core->packet))
+ rc = -ENOTEMPTY;
+ if (rc)
+ d_vpr_e("Failed to prepare venus for power off");
+err_pc_prep:
+ return rc;
+}
+
+static int __power_collapse(struct msm_vidc_core *core, bool force)
+{
+ int rc = 0;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE)) {
+ d_vpr_h("%s: Power already disabled\n", __func__);
+ goto exit;
+ }
+
+ if (!core_in_valid_state(core)) {
+ d_vpr_e("%s: Core not in init state\n", __func__);
+ return -EINVAL;
+ }
+
+ __flush_debug_queue(core, (!force ? core->packet : NULL), core->packet_size);
+
+ rc = call_iris_op(core, prepare_pc, core);
+ if (rc)
+ goto skip_power_off;
+
+ rc = __suspend(core);
+ if (rc)
+ d_vpr_e("Failed __suspend\n");
+
+exit:
+ return rc;
+
+skip_power_off:
+ d_vpr_e("%s: skipped\n", __func__);
+ return -EAGAIN;
+}
+
+static int __release_subcaches(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ struct subcache_info *sinfo;
+ struct hfi_buffer buf;
+
+ if (!is_sys_cache_present(core))
+ return 0;
+
+ if (!core->resource->subcache_set.set_to_fw) {
+ d_vpr_h("Subcaches not set to Venus\n");
+ return 0;
+ }
+
+ rc = hfi_create_header(core->packet, core->packet_size,
+ 0, core->header_id++);
+ if (rc)
+ return rc;
+
+ memset(&buf, 0, sizeof(struct hfi_buffer));
+ buf.type = HFI_BUFFER_SUBCACHE;
+ buf.flags = HFI_BUF_HOST_FLAG_RELEASE;
+
+ venus_hfi_for_each_subcache_reverse(core, sinfo) {
+ if (!sinfo->isactive)
+ continue;
+
+ buf.index = sinfo->subcache->slice_id;
+ buf.buffer_size = sinfo->subcache->slice_size;
+
+ rc = hfi_create_packet(core->packet,
+ core->packet_size,
+ HFI_CMD_BUFFER,
+ HFI_BUF_HOST_FLAG_NONE,
+ HFI_PAYLOAD_STRUCTURE,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &buf,
+ sizeof(buf));
+ if (rc)
+ return rc;
+ }
+
+ /* Set resource to Venus for activated subcaches */
+ rc = __cmdq_write(core, core->packet);
+ if (rc)
+ return rc;
+
+ venus_hfi_for_each_subcache_reverse(core, sinfo) {
+ if (!sinfo->isactive)
+ continue;
+
+ d_vpr_h("%s: release Subcache id %d size %lu done\n",
+ __func__, sinfo->subcache->slice_id,
+ sinfo->subcache->slice_size);
+ }
+ core->resource->subcache_set.set_to_fw = false;
+
+ return 0;
+}
+
+static int __set_subcaches(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ struct subcache_info *sinfo;
+ struct hfi_buffer buf;
+
+ if (!is_sys_cache_present(core))
+ return 0;
+
+ if (core->resource->subcache_set.set_to_fw) {
+ d_vpr_h("Subcaches already set to Venus\n");
+ return 0;
+ }
+
+ rc = hfi_create_header(core->packet, core->packet_size,
+ 0, core->header_id++);
+ if (rc)
+ goto err_fail_set_subacaches;
+
+ memset(&buf, 0, sizeof(struct hfi_buffer));
+ buf.type = HFI_BUFFER_SUBCACHE;
+ buf.flags = HFI_BUF_HOST_FLAG_NONE;
+
+ venus_hfi_for_each_subcache(core, sinfo) {
+ if (!sinfo->isactive)
+ continue;
+ buf.index = sinfo->subcache->slice_id;
+ buf.buffer_size = sinfo->subcache->slice_size;
+
+ rc = hfi_create_packet(core->packet,
+ core->packet_size,
+ HFI_CMD_BUFFER,
+ HFI_BUF_HOST_FLAG_NONE,
+ HFI_PAYLOAD_STRUCTURE,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &buf,
+ sizeof(buf));
+ if (rc)
+ goto err_fail_set_subacaches;
+ }
+
+ /* Set resource to Venus for activated subcaches */
+ rc = __cmdq_write(core, core->packet);
+ if (rc)
+ goto err_fail_set_subacaches;
+
+ venus_hfi_for_each_subcache(core, sinfo) {
+ if (!sinfo->isactive)
+ continue;
+ d_vpr_h("%s: set Subcache id %d size %lu done\n",
+ __func__, sinfo->subcache->slice_id,
+ sinfo->subcache->slice_size);
+ }
+ core->resource->subcache_set.set_to_fw = true;
+
+ return 0;
+
+err_fail_set_subacaches:
+ call_res_op(core, llcc, core, false);
+ return rc;
+}
+
+static int __venus_power_off(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE))
+ return 0;
+
+ rc = call_iris_op(core, power_off, core);
+ if (rc) {
+ d_vpr_e("Failed to power off, err: %d\n", rc);
+ return rc;
+ }
+ msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE, 0, __func__);
+
+ return rc;
+}
+
+static int __venus_power_on(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ if (is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE))
+ return 0;
+
+ rc = call_iris_op(core, power_on, core);
+ if (rc) {
+ d_vpr_e("Failed to power on, err: %d\n", rc);
+ return rc;
+ }
+
+ rc = msm_vidc_change_core_sub_state(core, 0, CORE_SUBSTATE_POWER_ENABLE, __func__);
+
+ return rc;
+}
+
+static int __suspend(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE)) {
+ d_vpr_h("Power already disabled\n");
+ return 0;
+ }
+
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return rc;
+
+ d_vpr_h("Entering suspend\n");
+
+ rc = fw_suspend(core);
+ if (rc) {
+ d_vpr_e("Failed to suspend video core %d\n", rc);
+ goto err_tzbsp_suspend;
+ }
+
+ call_res_op(core, llcc, core, false);
+
+ __venus_power_off(core);
+ d_vpr_h("Venus power off\n");
+ return rc;
+
+err_tzbsp_suspend:
+ return rc;
+}
+
+static int __resume(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ if (is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE)) {
+ goto exit;
+ } else if (!core_in_valid_state(core)) {
+ d_vpr_e("%s: core not in valid state\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return rc;
+
+ d_vpr_h("Resuming from power collapse\n");
+ /* reset handoff done from core sub_state */
+ rc = msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
+ if (rc)
+ return rc;
+ /* reset hw pwr ctrl from core sub_state */
+ rc = msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_FW_PWR_CTRL, 0, __func__);
+ if (rc)
+ return rc;
+
+ rc = __venus_power_on(core);
+ if (rc) {
+ d_vpr_e("Failed to power on venus\n");
+ goto err_venus_power_on;
+ }
+
+ /* Reboot the firmware */
+ rc = fw_resume(core);
+ if (rc) {
+ d_vpr_e("Failed to resume video core %d\n", rc);
+ goto err_set_video_state;
+ }
+
+ /*
+ * Hand off control of regulators to h/w _after_ loading fw.
+ * Note that the GDSC will turn off when switching from normal
+ * (s/w triggered) to fast (HW triggered) unless the h/w vote is
+ * present.
+ */
+ call_res_op(core, gdsc_hw_ctrl, core);
+
+ /* Wait for boot completion */
+ rc = call_iris_op(core, boot_firmware, core);
+ if (rc) {
+ d_vpr_e("Failed to reset venus core\n");
+ goto err_reset_core;
+ }
+
+ __sys_set_debug(core, (msm_fw_debug & FW_LOGMASK) >> FW_LOGSHIFT);
+
+ rc = call_res_op(core, llcc, core, true);
+ if (rc) {
+ d_vpr_e("Failed to activate subcache\n");
+ goto err_reset_core;
+ }
+ __set_subcaches(core);
+
+ rc = __sys_set_power_control(core, true);
+ if (rc) {
+ d_vpr_e("%s: set power control failed\n", __func__);
+ call_res_op(core, gdsc_sw_ctrl, core);
+ rc = 0;
+ }
+
+ d_vpr_h("Resumed from power collapse\n");
+exit:
+ /* Don't reset skip_pc_count for SYS_PC_PREP cmd */
+ //if (core->last_packet_type != HFI_CMD_SYS_PC_PREP)
+ // core->skip_pc_count = 0;
+ return rc;
+err_reset_core:
+ fw_suspend(core);
+err_set_video_state:
+ __venus_power_off(core);
+err_venus_power_on:
+ d_vpr_e("Failed to resume from power collapse\n");
+ return rc;
+}
+
+int __load_fw(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ /* clear all substates */
+ msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_MAX - 1, 0, __func__);
+
+ rc = __venus_power_on(core);
+ if (rc) {
+ d_vpr_e("%s: power on failed\n", __func__);
+ goto fail_power;
+ }
+
+ rc = fw_load(core);
+ if (rc)
+ goto fail_load_fw;
+
+ /*
+ * Hand off control of regulators to h/w _after_ loading fw.
+ * Note that the GDSC will turn off when switching from normal
+ * (s/w triggered) to fast (HW triggered) unless the h/w vote is
+ * present.
+ */
+ call_res_op(core, gdsc_hw_ctrl, core);
+
+ return rc;
+fail_load_fw:
+ __venus_power_off(core);
+fail_power:
+ return rc;
+}
+
+void __unload_fw(struct msm_vidc_core *core)
+{
+ if (!core->resource->fw_cookie)
+ return;
+
+ cancel_delayed_work(&core->pm_work);
+ fw_unload(core);
+ __venus_power_off(core);
+
+ /* clear all substates */
+ msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_MAX - 1, 0, __func__);
+}
+
+static int __response_handler(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ if (call_iris_op(core, watchdog, core, core->intr_status)) {
+ struct hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT};
+
+ core_lock(core, __func__);
+ msm_vidc_change_core_state(core, MSM_VIDC_CORE_ERROR, __func__);
+ /* mark cpu watchdog error */
+ msm_vidc_change_core_sub_state(core, 0, CORE_SUBSTATE_CPU_WATCHDOG, __func__);
+ d_vpr_e("%s: CPU WD error received\n", __func__);
+ core_unlock(core, __func__);
+
+ return handle_system_error(core, &pkt);
+ }
+
+ memset(core->response_packet, 0, core->packet_size);
+ while (!venus_hfi_queue_msg_read(core, core->response_packet)) {
+ rc = handle_response(core, core->response_packet);
+ if (rc)
+ continue;
+ /* check for system error */
+ if (core->state != MSM_VIDC_CORE_INIT)
+ break;
+ memset(core->response_packet, 0, core->packet_size);
+ }
+
+ __schedule_power_collapse_work(core);
+ __flush_debug_queue(core, core->response_packet, core->packet_size);
+
+ return rc;
+}
+
+irqreturn_t venus_hfi_isr(int irq, void *data)
+{
+ disable_irq_nosync(irq);
+ return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t venus_hfi_isr_handler(int irq, void *data)
+{
+ struct msm_vidc_core *core = data;
+ int num_responses = 0, rc = 0;
+
+ if (!core) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return IRQ_NONE;
+ }
+
+ core_lock(core, __func__);
+ rc = __resume(core);
+ if (rc) {
+ d_vpr_e("%s: Power on failed\n", __func__);
+ core_unlock(core, __func__);
+ goto exit;
+ }
+ call_iris_op(core, clear_interrupt, core);
+ core_unlock(core, __func__);
+
+ num_responses = __response_handler(core);
+
+exit:
+ if (!call_iris_op(core, watchdog, core, core->intr_status))
+ enable_irq(irq);
+
+ return IRQ_HANDLED;
+}
+
+void venus_hfi_pm_work_handler(struct work_struct *work)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ core = container_of(work, struct msm_vidc_core, pm_work.work);
+
+ core_lock(core, __func__);
+ d_vpr_h("%s: try power collapse\n", __func__);
+ /*
+ * It is ok to check this variable outside the lock since
+ * it is being updated in this context only
+ */
+ if (core->skip_pc_count >= VIDC_MAX_PC_SKIP_COUNT) {
+ d_vpr_e("Failed to PC for %d times\n",
+ core->skip_pc_count);
+ core->skip_pc_count = 0;
+ msm_vidc_change_core_state(core, MSM_VIDC_CORE_ERROR, __func__);
+ /* mark video hw unresponsive */
+ msm_vidc_change_core_sub_state(core, 0,
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE, __func__);
+ /* do core deinit to handle error */
+ msm_vidc_core_deinit_locked(core, true);
+ goto unlock;
+ }
+
+ /* core already deinited - skip power collapse */
+ if (is_core_state(core, MSM_VIDC_CORE_DEINIT)) {
+ d_vpr_e("%s: invalid core state %s\n",
+ __func__, core_state_name(core->state));
+ goto unlock;
+ }
+
+ rc = __power_collapse(core, false);
+ switch (rc) {
+ case 0:
+ core->skip_pc_count = 0;
+ /* Cancel pending delayed works if any */
+ __cancel_power_collapse_work(core);
+ d_vpr_h("%s: power collapse successful!\n", __func__);
+ break;
+ case -EBUSY:
+ core->skip_pc_count = 0;
+ d_vpr_h("%s: retry PC as dsp is busy\n", __func__);
+ __schedule_power_collapse_work(core);
+ break;
+ case -EAGAIN:
+ core->skip_pc_count++;
+ d_vpr_e("%s: retry power collapse (count %d)\n",
+ __func__, core->skip_pc_count);
+ __schedule_power_collapse_work(core);
+ break;
+ default:
+ d_vpr_e("%s: power collapse failed\n", __func__);
+ break;
+ }
+unlock:
+ core_unlock(core, __func__);
+}
+
+static int __sys_init(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = hfi_packet_sys_init(core, core->packet, core->packet_size);
+ if (rc)
+ return rc;
+
+ rc = __cmdq_write(core, core->packet);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int __sys_image_version(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = hfi_packet_image_version(core, core->packet, core->packet_size);
+ if (rc)
+ return rc;
+
+ rc = __cmdq_write(core, core->packet);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+int venus_hfi_core_init(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ d_vpr_h("%s(): core %pK\n", __func__, core);
+
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return rc;
+
+ rc = venus_hfi_queue_init(core);
+ if (rc)
+ goto error;
+
+ rc = __load_fw(core);
+ if (rc)
+ goto error;
+
+ rc = call_iris_op(core, boot_firmware, core);
+ if (rc)
+ goto error;
+
+ rc = call_res_op(core, llcc, core, true);
+ if (rc)
+ goto error;
+
+ rc = __sys_init(core);
+ if (rc)
+ goto error;
+
+ rc = __sys_image_version(core);
+ if (rc)
+ goto error;
+
+ rc = __sys_set_debug(core, (msm_fw_debug & FW_LOGMASK) >> FW_LOGSHIFT);
+ if (rc)
+ goto error;
+
+ rc = __set_subcaches(core);
+ if (rc)
+ goto error;
+
+ rc = __sys_set_power_control(core, true);
+ if (rc) {
+ d_vpr_e("%s: set power control failed\n", __func__);
+ call_res_op(core, gdsc_sw_ctrl, core);
+ rc = 0;
+ }
+
+ d_vpr_h("%s(): successful\n", __func__);
+ return 0;
+
+error:
+ d_vpr_e("%s(): failed\n", __func__);
+ return rc;
+}
+
+int venus_hfi_core_deinit(struct msm_vidc_core *core, bool force)
+{
+ int rc = 0;
+
+ d_vpr_h("%s(): core %pK\n", __func__, core);
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return rc;
+
+ if (is_core_state(core, MSM_VIDC_CORE_DEINIT))
+ return 0;
+ __resume(core);
+ __flush_debug_queue(core, (!force ? core->packet : NULL), core->packet_size);
+ __release_subcaches(core);
+ call_res_op(core, llcc, core, false);
+ __unload_fw(core);
+ /**
+ * coredump need to be called after firmware unload, coredump also
+ * copying queues memory. So need to be called before queues deinit.
+ */
+ if (msm_vidc_fw_dump)
+ fw_coredump(core);
+
+ return 0;
+}
+
+int venus_hfi_suspend(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return rc;
+
+ if (!core->capabilities[SW_PC].value) {
+ d_vpr_h("Skip suspending venus\n");
+ return 0;
+ }
+
+ d_vpr_h("Suspending Venus\n");
+ rc = __power_collapse(core, true);
+ if (!rc) {
+ /* Cancel pending delayed works if any */
+ __cancel_power_collapse_work(core);
+ } else {
+ d_vpr_e("%s: Venus is busy\n", __func__);
+ rc = -EBUSY;
+ }
+
+ return rc;
+}
+
+int venus_hfi_session_open(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ __sys_set_debug(core, (msm_fw_debug & FW_LOGMASK) >> FW_LOGSHIFT);
+
+ rc = hfi_packet_session_command(inst,
+ HFI_CMD_OPEN,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ HFI_PORT_NONE,
+ 0, /* session_id */
+ HFI_PAYLOAD_U32,
+ &inst->session_id, /* payload */
+ sizeof(u32));
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_session_set_codec(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+ u32 codec;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ rc = hfi_create_header(inst->packet, inst->packet_size,
+ inst->session_id, core->header_id++);
+ if (rc)
+ goto unlock;
+
+ codec = get_hfi_codec(inst);
+ rc = hfi_create_packet(inst->packet, inst->packet_size,
+ HFI_PROP_CODEC,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32_ENUM,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &codec,
+ sizeof(u32));
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_session_property(struct msm_vidc_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload, u32 payload_size)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ rc = hfi_create_header(inst->packet, inst->packet_size,
+ inst->session_id, core->header_id++);
+ if (rc)
+ goto unlock;
+ rc = hfi_create_packet(inst->packet, inst->packet_size,
+ pkt_type,
+ flags,
+ payload_type,
+ port,
+ core->packet_id++,
+ payload,
+ payload_size);
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_session_close(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ rc = hfi_packet_session_command(inst,
+ HFI_CMD_CLOSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ HFI_PORT_NONE,
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_start(struct msm_vidc_inst *inst, enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (port != INPUT_PORT && port != OUTPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ goto unlock;
+ }
+
+ rc = hfi_packet_session_command(inst,
+ HFI_CMD_START,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ get_hfi_port(inst, port),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_stop(struct msm_vidc_inst *inst, enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (port != INPUT_PORT && port != OUTPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ goto unlock;
+ }
+
+ rc = hfi_packet_session_command(inst,
+ HFI_CMD_STOP,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ get_hfi_port(inst, port),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_session_pause(struct msm_vidc_inst *inst, enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (port != INPUT_PORT && port != OUTPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ goto unlock;
+ }
+
+ rc = hfi_packet_session_command(inst,
+ HFI_CMD_PAUSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ get_hfi_port(inst, port),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_session_resume(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port, u32 payload)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (port != INPUT_PORT && port != OUTPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ goto unlock;
+ }
+
+ rc = hfi_packet_session_command(inst,
+ HFI_CMD_RESUME,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ get_hfi_port(inst, port),
+ inst->session_id,
+ HFI_PAYLOAD_U32,
+ &payload,
+ sizeof(u32));
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_session_drain(struct msm_vidc_inst *inst, enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ if (port != INPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ goto unlock;
+ }
+
+ rc = hfi_packet_session_command(inst,
+ HFI_CMD_DRAIN,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ get_hfi_port(inst, port),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_session_command(struct msm_vidc_inst *inst,
+ u32 cmd, enum msm_vidc_port_type port, u32 payload_type,
+ void *payload, u32 payload_size)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ rc = hfi_create_header(inst->packet, inst->packet_size,
+ inst->session_id,
+ core->header_id++);
+ if (rc)
+ goto unlock;
+
+ rc = hfi_create_packet(inst->packet, inst->packet_size,
+ cmd,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ payload_type,
+ get_hfi_port(inst, port),
+ core->packet_id++,
+ payload,
+ payload_size);
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_queue_buffer(struct msm_vidc_inst *inst,
+ struct msm_vidc_buffer *buffer)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+ struct hfi_buffer hfi_buffer;
+
+ if (!inst->packet) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ rc = get_hfi_buffer(inst, buffer, &hfi_buffer);
+ if (rc)
+ goto unlock;
+
+ rc = hfi_create_header(inst->packet, inst->packet_size,
+ inst->session_id, core->header_id++);
+ if (rc)
+ goto unlock;
+
+ rc = hfi_create_packet(inst->packet,
+ inst->packet_size,
+ HFI_CMD_BUFFER,
+ HFI_HOST_FLAGS_INTR_REQUIRED,
+ HFI_PAYLOAD_STRUCTURE,
+ get_hfi_port_from_buffer_type(inst, buffer->type),
+ core->packet_id++,
+ &hfi_buffer,
+ sizeof(hfi_buffer));
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_release_buffer(struct msm_vidc_inst *inst,
+ struct msm_vidc_buffer *buffer)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+ struct hfi_buffer hfi_buffer;
+
+ if (!inst->packet || !buffer) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+ core = inst->core;
+ core_lock(core, __func__);
+
+ if (!__valdiate_session(core, inst, __func__)) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+
+ rc = get_hfi_buffer(inst, buffer, &hfi_buffer);
+ if (rc)
+ goto unlock;
+
+ /* add release flag */
+ hfi_buffer.flags |= HFI_BUF_HOST_FLAG_RELEASE;
+
+ rc = hfi_create_header(inst->packet, inst->packet_size,
+ inst->session_id, core->header_id++);
+ if (rc)
+ goto unlock;
+
+ rc = hfi_create_packet(inst->packet,
+ inst->packet_size,
+ HFI_CMD_BUFFER,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ HFI_PAYLOAD_STRUCTURE,
+ get_hfi_port_from_buffer_type(inst, buffer->type),
+ core->packet_id++,
+ &hfi_buffer,
+ sizeof(hfi_buffer));
+ if (rc)
+ goto unlock;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc)
+ goto unlock;
+
+unlock:
+ core_unlock(core, __func__);
+ return rc;
+}
+
+int venus_hfi_scale_clocks(struct msm_vidc_inst *inst, u64 freq)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ core = inst->core;
+
+ core_lock(core, __func__);
+ rc = __resume(core);
+ if (rc) {
+ i_vpr_e(inst, "%s: Resume from power collapse failed\n", __func__);
+ goto exit;
+ }
+ rc = call_res_op(core, set_clks, core, freq);
+ if (rc)
+ goto exit;
+
+exit:
+ core_unlock(core, __func__);
+
+ return rc;
+}
+
+int venus_hfi_scale_buses(struct msm_vidc_inst *inst, u64 bw_ddr, u64 bw_llcc)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ core = inst->core;
+
+ core_lock(core, __func__);
+ rc = __resume(core);
+ if (rc) {
+ i_vpr_e(inst, "%s: Resume from power collapse failed\n", __func__);
+ goto exit;
+ }
+ rc = call_res_op(core, set_bw, core, bw_ddr, bw_llcc);
+ if (rc)
+ goto exit;
+
+exit:
+ core_unlock(core, __func__);
+
+ return rc;
+}
+
+int venus_hfi_set_ir_period(struct msm_vidc_inst *inst, u32 ir_type,
+ enum msm_vidc_inst_capability_type cap_id)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+ u32 ir_period, sync_frame_req = 0;
+
+ core = inst->core;
+
+ core_lock(core, __func__);
+
+ ir_period = inst->capabilities[cap_id].value;
+
+ rc = hfi_create_header(inst->packet, inst->packet_size,
+ inst->session_id, core->header_id++);
+ if (rc)
+ goto exit;
+
+ /* Request sync frame if ir period enabled dynamically */
+ if (!inst->ir_enabled) {
+ inst->ir_enabled = ((ir_period > 0) ? true : false);
+ if (inst->ir_enabled && inst->bufq[OUTPUT_PORT].vb2q->streaming) {
+ sync_frame_req = HFI_SYNC_FRAME_REQUEST_WITH_PREFIX_SEQ_HDR;
+ rc = hfi_create_packet(inst->packet, inst->packet_size,
+ HFI_PROP_REQUEST_SYNC_FRAME,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32_ENUM,
+ msm_vidc_get_port_info(inst, REQUEST_I_FRAME),
+ core->packet_id++,
+ &sync_frame_req,
+ sizeof(u32));
+ if (rc)
+ goto exit;
+ }
+ }
+
+ rc = hfi_create_packet(inst->packet, inst->packet_size,
+ ir_type,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ msm_vidc_get_port_info(inst, cap_id),
+ core->packet_id++,
+ &ir_period,
+ sizeof(u32));
+ if (rc)
+ goto exit;
+
+ rc = __cmdq_write(inst->core, inst->packet);
+ if (rc) {
+ i_vpr_e(inst, "%s: failed to set inst->capabilities[%d] %s to fw\n",
+ __func__, cap_id, cap_name(cap_id));
+ goto exit;
+ }
+
+exit:
+ core_unlock(core, __func__);
+
+ return rc;
+}
--
2.7.4


2023-07-28 15:33:35

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 12/33] iris: vidc: add helper functions for resource management

From: Dikshita Agarwal <[email protected]>

This implements ops to initialize, enable and disable extrenal
resources needed by video driver like power domains, clocks etc.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../media/platform/qcom/iris/vidc/inc/resources.h | 259 ++++
.../media/platform/qcom/iris/vidc/src/resources.c | 1321 ++++++++++++++++++++
2 files changed, 1580 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/resources.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/resources.c

diff --git a/drivers/media/platform/qcom/iris/vidc/inc/resources.h b/drivers/media/platform/qcom/iris/vidc/inc/resources.h
new file mode 100644
index 0000000..9b2588e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/inc/resources.h
@@ -0,0 +1,259 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2022, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _MSM_VIDC_RESOURCES_H_
+#define _MSM_VIDC_RESOURCES_H_
+
+struct icc_path;
+struct regulator;
+struct clk;
+struct reset_control;
+struct llcc_slice_desc;
+struct iommu_domain;
+struct device;
+struct msm_vidc_core;
+
+/*
+ * These are helper macros to iterate over various lists within
+ * msm_vidc_core->resource. The intention is to cut down on a lot
+ * of boiler-plate code
+ */
+
+/* Read as "for each 'thing' in a set of 'thingies'" */
+#define venus_hfi_for_each_thing(__device, __thing, __thingy) \
+ venus_hfi_for_each_thing_continue(__device, __thing, __thingy, 0)
+
+#define venus_hfi_for_each_thing_reverse(__device, __thing, __thingy) \
+ venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \
+ (__device)->resource->__thingy##_set.count - 1)
+
+/* TODO: the __from parameter technically not required since we can figure it
+ * out with some pointer magic (i.e. __thing - __thing##_tbl[0]). If this macro
+ * sees extensive use, probably worth cleaning it up but for now omitting it
+ * since it introduces unnecessary complexity.
+ */
+#define venus_hfi_for_each_thing_continue(__device, __thing, __thingy, __from) \
+ for (__thing = &(__device)->resource->\
+ __thingy##_set.__thingy##_tbl[__from]; \
+ __thing < &(__device)->resource->__thingy##_set.__thingy##_tbl[0] + \
+ ((__device)->resource->__thingy##_set.count - __from); \
+ ++__thing)
+
+#define venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \
+ __from) \
+ for (__thing = &(__device)->resource->\
+ __thingy##_set.__thingy##_tbl[__from]; \
+ __thing >= &(__device)->resource->__thingy##_set.__thingy##_tbl[0]; \
+ --__thing)
+
+/* Bus set helpers */
+#define venus_hfi_for_each_bus(__device, __binfo) \
+ venus_hfi_for_each_thing(__device, __binfo, bus)
+#define venus_hfi_for_each_bus_reverse(__device, __binfo) \
+ venus_hfi_for_each_thing_reverse(__device, __binfo, bus)
+
+/* Regular set helpers */
+#define venus_hfi_for_each_regulator(__device, __rinfo) \
+ venus_hfi_for_each_thing(__device, __rinfo, regulator)
+#define venus_hfi_for_each_regulator_reverse(__device, __rinfo) \
+ venus_hfi_for_each_thing_reverse(__device, __rinfo, regulator)
+#define venus_hfi_for_each_regulator_reverse_continue(__device, __rinfo, \
+ __from) \
+ venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \
+ regulator, __from)
+
+/* Power domain set helpers */
+#define venus_hfi_for_each_power_domain(__device, __pdinfo) \
+ venus_hfi_for_each_thing(__device, __pdinfo, power_domain)
+
+/* Clock set helpers */
+#define venus_hfi_for_each_clock(__device, __cinfo) \
+ venus_hfi_for_each_thing(__device, __cinfo, clock)
+#define venus_hfi_for_each_clock_reverse(__device, __cinfo) \
+ venus_hfi_for_each_thing_reverse(__device, __cinfo, clock)
+
+/* Reset clock set helpers */
+#define venus_hfi_for_each_reset_clock(__device, __rcinfo) \
+ venus_hfi_for_each_thing(__device, __rcinfo, reset)
+#define venus_hfi_for_each_reset_clock_reverse(__device, __rcinfo) \
+ venus_hfi_for_each_thing_reverse(__device, __rcinfo, reset)
+#define venus_hfi_for_each_reset_clock_reverse_continue(__device, __rinfo, \
+ __from) \
+ venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \
+ reset, __from)
+
+/* Subcache set helpers */
+#define venus_hfi_for_each_subcache(__device, __sinfo) \
+ venus_hfi_for_each_thing(__device, __sinfo, subcache)
+#define venus_hfi_for_each_subcache_reverse(__device, __sinfo) \
+ venus_hfi_for_each_thing_reverse(__device, __sinfo, subcache)
+
+/* Contextbank set helpers */
+#define venus_hfi_for_each_context_bank(__device, __sinfo) \
+ venus_hfi_for_each_thing(__device, __sinfo, context_bank)
+#define venus_hfi_for_each_context_bank_reverse(__device, __sinfo) \
+ venus_hfi_for_each_thing_reverse(__device, __sinfo, context_bank)
+
+enum msm_vidc_branch_mem_flags {
+ MSM_VIDC_CLKFLAG_RETAIN_PERIPH,
+ MSM_VIDC_CLKFLAG_NORETAIN_PERIPH,
+ MSM_VIDC_CLKFLAG_RETAIN_MEM,
+ MSM_VIDC_CLKFLAG_NORETAIN_MEM,
+ MSM_VIDC_CLKFLAG_PERIPH_OFF_SET,
+ MSM_VIDC_CLKFLAG_PERIPH_OFF_CLEAR,
+};
+
+struct bus_info {
+ struct icc_path *icc;
+ const char *name;
+ u32 min_kbps;
+ u32 max_kbps;
+};
+
+struct bus_set {
+ struct bus_info *bus_tbl;
+ u32 count;
+};
+
+struct regulator_info {
+ struct regulator *regulator;
+ const char *name;
+ bool hw_power_collapse;
+};
+
+struct regulator_set {
+ struct regulator_info *regulator_tbl;
+ u32 count;
+};
+
+struct power_domain_info {
+ struct device *genpd_dev;
+ const char *name;
+};
+
+struct power_domain_set {
+ struct power_domain_info *power_domain_tbl;
+ u32 count;
+};
+
+struct clock_info {
+ struct clk *clk;
+ const char *name;
+ u32 clk_id;
+ bool has_scaling;
+ u64 prev;
+};
+
+struct clock_set {
+ struct clock_info *clock_tbl;
+ u32 count;
+};
+
+struct reset_info {
+ struct reset_control *rst;
+ const char *name;
+ bool exclusive_release;
+};
+
+struct reset_set {
+ struct reset_info *reset_tbl;
+ u32 count;
+};
+
+struct subcache_info {
+ struct llcc_slice_desc *subcache;
+ const char *name;
+ u32 llcc_id;
+ bool isactive;
+};
+
+struct subcache_set {
+ struct subcache_info *subcache_tbl;
+ u32 count;
+ bool set_to_fw;
+};
+
+struct addr_range {
+ u32 start;
+ u32 size;
+};
+
+struct context_bank_info {
+ const char *name;
+ struct addr_range addr_range;
+ bool secure;
+ bool dma_coherant;
+ struct device *dev;
+ struct iommu_domain *domain;
+ u32 region;
+ u64 dma_mask;
+};
+
+struct context_bank_set {
+ struct context_bank_info *context_bank_tbl;
+ u32 count;
+};
+
+struct frequency_table {
+ unsigned long freq;
+};
+
+struct freq_set {
+ struct frequency_table *freq_tbl;
+ u32 count;
+};
+
+struct msm_vidc_resource {
+ u8 __iomem *register_base_addr;
+ int irq;
+ struct bus_set bus_set;
+ struct regulator_set regulator_set;
+ struct power_domain_set power_domain_set;
+ struct clock_set clock_set;
+ struct reset_set reset_set;
+ struct subcache_set subcache_set;
+ struct context_bank_set context_bank_set;
+ struct freq_set freq_set;
+ int fw_cookie;
+};
+
+#define call_res_op(c, op, ...) \
+ (((c) && (c)->res_ops && (c)->res_ops->op) ? \
+ ((c)->res_ops->op(__VA_ARGS__)) : 0)
+
+struct msm_vidc_resources_ops {
+ int (*init)(struct msm_vidc_core *core);
+
+ int (*reset_bridge)(struct msm_vidc_core *core);
+ int (*reset_control_acquire)(struct msm_vidc_core *core,
+ const char *name);
+ int (*reset_control_release)(struct msm_vidc_core *core,
+ const char *name);
+ int (*reset_control_assert)(struct msm_vidc_core *core,
+ const char *name);
+ int (*reset_control_deassert)(struct msm_vidc_core *core,
+ const char *name);
+
+ int (*gdsc_init)(struct msm_vidc_core *core);
+ int (*gdsc_on)(struct msm_vidc_core *core, const char *name);
+ int (*gdsc_off)(struct msm_vidc_core *core, const char *name);
+ int (*gdsc_hw_ctrl)(struct msm_vidc_core *core);
+ int (*gdsc_sw_ctrl)(struct msm_vidc_core *core);
+
+ int (*llcc)(struct msm_vidc_core *core, bool enable);
+ int (*set_bw)(struct msm_vidc_core *core, unsigned long bw_ddr,
+ unsigned long bw_llcc);
+ int (*set_clks)(struct msm_vidc_core *core, u64 rate);
+
+ int (*clk_disable)(struct msm_vidc_core *core, const char *name);
+ int (*clk_enable)(struct msm_vidc_core *core, const char *name);
+ int (*clk_set_flag)(struct msm_vidc_core *core,
+ const char *name, enum msm_vidc_branch_mem_flags flag);
+};
+
+const struct msm_vidc_resources_ops *get_resources_ops(void);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/vidc/src/resources.c b/drivers/media/platform/qcom/iris/vidc/src/resources.c
new file mode 100644
index 0000000..b0800b9
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/src/resources.c
@@ -0,0 +1,1321 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 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 <linux/soc/qcom/llcc-qcom.h>
+#include <linux/sort.h>
+
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_platform.h"
+#include "msm_vidc_power.h"
+#include "venus_hfi.h"
+
+/* Less than 50MBps is treated as trivial BW change */
+#define TRIVIAL_BW_THRESHOLD 50000
+#define TRIVIAL_BW_CHANGE(a, b) \
+ ((a) > (b) ? (a) - (b) < TRIVIAL_BW_THRESHOLD : \
+ (b) - (a) < TRIVIAL_BW_THRESHOLD)
+
+enum reset_state {
+ INIT = 1,
+ ASSERT,
+ DEASSERT,
+};
+
+/* A comparator to compare loads (needed later on) */
+static inline int cmp(const void *a, const void *b)
+{
+ /* want to sort in reverse so flip the comparison */
+ return ((struct freq_table *)b)->freq -
+ ((struct freq_table *)a)->freq;
+}
+
+static void __fatal_error(bool fatal)
+{
+ WARN_ON(fatal);
+}
+
+static void devm_llcc_release(void *res)
+{
+ llcc_slice_putd((struct llcc_slice_desc *)res);
+}
+
+static struct llcc_slice_desc *devm_llcc_get(struct device *dev, u32 id)
+{
+ struct llcc_slice_desc *llcc = NULL;
+ int rc = 0;
+
+ llcc = llcc_slice_getd(id);
+ if (!llcc)
+ return NULL;
+
+ /**
+ * register release callback with devm, so that when device goes
+ * out of scope(during remove sequence), devm will take care of
+ * de-register part by invoking release callback.
+ */
+ rc = devm_add_action_or_reset(dev, devm_llcc_release, (void *)llcc);
+ if (rc)
+ return NULL;
+
+ return llcc;
+}
+
+static void devm_pd_release(void *res)
+{
+ struct device *pd = (struct device *)res;
+
+ d_vpr_h("%s(): %s\n", __func__, dev_name(pd));
+ dev_pm_domain_detach(pd, true);
+}
+
+static struct device *devm_pd_get(struct device *dev, const char *name)
+{
+ struct device *pd = NULL;
+ int rc = 0;
+
+ pd = dev_pm_domain_attach_by_name(dev, name);
+ if (!pd) {
+ d_vpr_e("%s: pm domain attach failed %s\n", __func__, name);
+ return NULL;
+ }
+
+ rc = devm_add_action_or_reset(dev, devm_pd_release, (void *)pd);
+ if (rc) {
+ d_vpr_e("%s: add action or reset failed %s\n", __func__, name);
+ return NULL;
+ }
+
+ return pd;
+}
+
+static void devm_opp_dl_release(void *res)
+{
+ struct device_link *link = (struct device_link *)res;
+
+ d_vpr_h("%s(): %s\n", __func__, dev_name(&link->link_dev));
+ device_link_del(link);
+}
+
+static int devm_opp_dl_get(struct device *dev, struct device *supplier)
+{
+ u32 flag = DL_FLAG_RPM_ACTIVE | DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS;
+ struct device_link *link = NULL;
+ int rc = 0;
+
+ link = device_link_add(dev, supplier, flag);
+ if (!link) {
+ d_vpr_e("%s: device link add failed\n", __func__);
+ return -EINVAL;
+ }
+
+ rc = devm_add_action_or_reset(dev, devm_opp_dl_release, (void *)link);
+ if (rc) {
+ d_vpr_e("%s: add action or reset failed\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void devm_pm_runtime_put_sync(void *res)
+{
+ struct device *dev = (struct device *)res;
+
+ d_vpr_h("%s(): %s\n", __func__, dev_name(dev));
+ pm_runtime_put_sync(dev);
+}
+
+static int devm_pm_runtime_get_sync(struct device *dev)
+{
+ int rc = 0;
+
+ rc = pm_runtime_get_sync(dev);
+ if (rc < 0) {
+ d_vpr_e("%s: pm domain get sync failed\n", __func__);
+ return rc;
+ }
+
+ rc = devm_add_action_or_reset(dev, devm_pm_runtime_put_sync, (void *)dev);
+ if (rc) {
+ d_vpr_e("%s: add action or reset failed\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int __opp_set_rate(struct msm_vidc_core *core, u64 freq)
+{
+ unsigned long opp_freq = 0;
+ struct dev_pm_opp *opp;
+ int rc = 0;
+
+ opp_freq = freq;
+
+ /* find max(ceil) freq from opp table */
+ opp = dev_pm_opp_find_freq_ceil(&core->pdev->dev, &opp_freq);
+ if (IS_ERR(opp)) {
+ opp = dev_pm_opp_find_freq_floor(&core->pdev->dev, &opp_freq);
+ if (IS_ERR(opp)) {
+ d_vpr_e("%s: unable to find freq %lld in opp table\n", __func__, freq);
+ return -EINVAL;
+ }
+ }
+ dev_pm_opp_put(opp);
+
+ /* print freq value */
+ d_vpr_h("%s: set rate %lu (requested %llu)\n",
+ __func__, opp_freq, freq);
+
+ /* scale freq to power up mxc & mmcx */
+ rc = dev_pm_opp_set_rate(&core->pdev->dev, opp_freq);
+ if (rc) {
+ d_vpr_e("%s: failed to set rate\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int __init_register_base(struct msm_vidc_core *core)
+{
+ struct msm_vidc_resource *res;
+
+ res = core->resource;
+
+ res->register_base_addr = devm_platform_ioremap_resource(core->pdev, 0);
+ if (IS_ERR(res->register_base_addr)) {
+ d_vpr_e("%s: map reg addr failed %ld\n",
+ __func__, PTR_ERR(res->register_base_addr));
+ return -EINVAL;
+ }
+ d_vpr_h("%s: reg_base %p\n", __func__, res->register_base_addr);
+
+ return 0;
+}
+
+static int __init_irq(struct msm_vidc_core *core)
+{
+ struct msm_vidc_resource *res;
+ int rc = 0;
+
+ res = core->resource;
+
+ res->irq = platform_get_irq(core->pdev, 0);
+
+ if (res->irq < 0)
+ d_vpr_e("%s: get irq failed, %d\n", __func__, res->irq);
+
+ d_vpr_h("%s: irq %d\n", __func__, res->irq);
+
+ rc = devm_request_threaded_irq(&core->pdev->dev, res->irq, venus_hfi_isr,
+ venus_hfi_isr_handler, IRQF_TRIGGER_HIGH, "msm-vidc", core);
+ if (rc) {
+ d_vpr_e("%s: Failed to allocate venus IRQ\n", __func__);
+ return rc;
+ }
+ disable_irq_nosync(res->irq);
+
+ return rc;
+}
+
+static int __init_bus(struct msm_vidc_core *core)
+{
+ const struct bw_table *bus_tbl;
+ struct bus_set *interconnects;
+ struct bus_info *binfo = NULL;
+ u32 bus_count = 0, cnt = 0;
+ int rc = 0;
+
+ interconnects = &core->resource->bus_set;
+
+ bus_tbl = core->platform->data.bw_tbl;
+ bus_count = core->platform->data.bw_tbl_size;
+
+ if (!bus_tbl || !bus_count) {
+ d_vpr_e("%s: invalid bus tbl %p or count %d\n",
+ __func__, bus_tbl, bus_count);
+ return -EINVAL;
+ }
+
+ /* allocate bus_set */
+ interconnects->bus_tbl = devm_kzalloc(&core->pdev->dev,
+ sizeof(*interconnects->bus_tbl) * bus_count,
+ GFP_KERNEL);
+ if (!interconnects->bus_tbl) {
+ d_vpr_e("%s: failed to alloc memory for bus table\n", __func__);
+ return -ENOMEM;
+ }
+ interconnects->count = bus_count;
+
+ /* populate bus field from platform data */
+ for (cnt = 0; cnt < interconnects->count; cnt++) {
+ interconnects->bus_tbl[cnt].name = bus_tbl[cnt].name;
+ interconnects->bus_tbl[cnt].min_kbps = bus_tbl[cnt].min_kbps;
+ interconnects->bus_tbl[cnt].max_kbps = bus_tbl[cnt].max_kbps;
+ }
+
+ /* print bus fields */
+ venus_hfi_for_each_bus(core, binfo) {
+ d_vpr_h("%s: name %s min_kbps %u max_kbps %u\n",
+ __func__, binfo->name, binfo->min_kbps, binfo->max_kbps);
+ }
+
+ /* get interconnect handle */
+ venus_hfi_for_each_bus(core, binfo) {
+ binfo->icc = devm_of_icc_get(&core->pdev->dev, binfo->name);
+ if (IS_ERR_OR_NULL(binfo->icc)) {
+ d_vpr_e("%s: failed to get bus: %s\n", __func__, binfo->name);
+ rc = PTR_ERR_OR_ZERO(binfo->icc) ?
+ PTR_ERR_OR_ZERO(binfo->icc) : -EBADHANDLE;
+ binfo->icc = NULL;
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int __init_power_domains(struct msm_vidc_core *core)
+{
+ struct power_domain_info *pdinfo = NULL;
+ const struct pd_table *pd_tbl;
+ struct power_domain_set *pds;
+ struct device **opp_vdevs = NULL;
+ const char * const *opp_tbl;
+ u32 pd_count = 0, opp_count = 0, cnt = 0;
+ int rc = 0;
+
+ pds = &core->resource->power_domain_set;
+
+ pd_tbl = core->platform->data.pd_tbl;
+ pd_count = core->platform->data.pd_tbl_size;
+
+ /* skip init if power domain not supported */
+ if (!pd_count) {
+ d_vpr_h("%s: power domain entries not available in db\n", __func__);
+ return 0;
+ }
+
+ /* sanitize power domain table */
+ if (!pd_tbl) {
+ d_vpr_e("%s: invalid power domain tbl\n", __func__);
+ return -EINVAL;
+ }
+
+ /* allocate power_domain_set */
+ pds->power_domain_tbl = devm_kzalloc(&core->pdev->dev,
+ sizeof(*pds->power_domain_tbl) * pd_count,
+ GFP_KERNEL);
+ if (!pds->power_domain_tbl) {
+ d_vpr_e("%s: failed to alloc memory for pd table\n", __func__);
+ return -ENOMEM;
+ }
+ pds->count = pd_count;
+
+ /* populate power domain fields */
+ for (cnt = 0; cnt < pds->count; cnt++)
+ pds->power_domain_tbl[cnt].name = pd_tbl[cnt].name;
+
+ /* print power domain fields */
+ venus_hfi_for_each_power_domain(core, pdinfo)
+ d_vpr_h("%s: pd name %s\n", __func__, pdinfo->name);
+
+ /* get power domain handle */
+ venus_hfi_for_each_power_domain(core, pdinfo) {
+ pdinfo->genpd_dev = devm_pd_get(&core->pdev->dev, pdinfo->name);
+ if (IS_ERR_OR_NULL(pdinfo->genpd_dev)) {
+ rc = PTR_ERR_OR_ZERO(pdinfo->genpd_dev) ?
+ PTR_ERR_OR_ZERO(pdinfo->genpd_dev) : -EBADHANDLE;
+ d_vpr_e("%s: failed to get pd: %s\n", __func__, pdinfo->name);
+ pdinfo->genpd_dev = NULL;
+ return rc;
+ }
+ }
+
+ opp_tbl = core->platform->data.opp_tbl;
+ opp_count = core->platform->data.opp_tbl_size;
+
+ /* skip init if opp not supported */
+ if (opp_count < 2) {
+ d_vpr_h("%s: opp entries not available\n", __func__);
+ return 0;
+ }
+
+ /* sanitize opp table */
+ if (!opp_tbl) {
+ d_vpr_e("%s: invalid opp table\n", __func__);
+ return -EINVAL;
+ }
+
+ /* ignore NULL entry at the end of table */
+ opp_count -= 1;
+
+ /* print opp table entries */
+ for (cnt = 0; cnt < opp_count; cnt++)
+ d_vpr_h("%s: opp name %s\n", __func__, opp_tbl[cnt]);
+
+ /* populate opp power domains(for rails) */
+ rc = devm_pm_opp_attach_genpd(&core->pdev->dev, opp_tbl, &opp_vdevs);
+ if (rc)
+ return rc;
+
+ /* create device_links b/w consumer(dev) and multiple suppliers(mx, mmcx) */
+ for (cnt = 0; cnt < opp_count; cnt++) {
+ rc = devm_opp_dl_get(&core->pdev->dev, opp_vdevs[cnt]);
+ if (rc) {
+ d_vpr_e("%s: failed to create dl: %s\n",
+ __func__, dev_name(opp_vdevs[cnt]));
+ return rc;
+ }
+ }
+
+ /* initialize opp table from device tree */
+ rc = devm_pm_opp_of_add_table(&core->pdev->dev);
+ if (rc) {
+ d_vpr_e("%s: failed to add opp table\n", __func__);
+ return rc;
+ }
+
+ /**
+ * 1. power up mx & mmcx supply for RCG(mvs0_clk_src)
+ * 2. power up gdsc0c for mvs0c branch clk
+ * 3. power up gdsc0 for mvs0 branch clk
+ */
+
+ /**
+ * power up mxc, mmcx rails to enable supply for
+ * RCG(video_cc_mvs0_clk_src)
+ */
+ /* enable runtime pm */
+ rc = devm_pm_runtime_enable(&core->pdev->dev);
+ if (rc) {
+ d_vpr_e("%s: failed to enable runtime pm\n", __func__);
+ return rc;
+ }
+ /* power up rails(mxc & mmcx) */
+ rc = devm_pm_runtime_get_sync(&core->pdev->dev);
+ if (rc) {
+ d_vpr_e("%s: failed to get sync runtime pm\n", __func__);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int __init_clocks(struct msm_vidc_core *core)
+{
+ const struct clk_table *clk_tbl;
+ struct clock_set *clocks;
+ struct clock_info *cinfo = NULL;
+ u32 clk_count = 0, cnt = 0;
+ int rc = 0;
+
+ clocks = &core->resource->clock_set;
+
+ clk_tbl = core->platform->data.clk_tbl;
+ clk_count = core->platform->data.clk_tbl_size;
+
+ if (!clk_tbl || !clk_count) {
+ d_vpr_e("%s: invalid clock tbl %p or count %d\n",
+ __func__, clk_tbl, clk_count);
+ return -EINVAL;
+ }
+
+ /* allocate clock_set */
+ clocks->clock_tbl = devm_kzalloc(&core->pdev->dev,
+ sizeof(*clocks->clock_tbl) * clk_count,
+ GFP_KERNEL);
+ if (!clocks->clock_tbl) {
+ d_vpr_e("%s: failed to alloc memory for clock table\n", __func__);
+ return -ENOMEM;
+ }
+ clocks->count = clk_count;
+
+ /* populate clock field from platform data */
+ for (cnt = 0; cnt < clocks->count; cnt++) {
+ clocks->clock_tbl[cnt].name = clk_tbl[cnt].name;
+ clocks->clock_tbl[cnt].clk_id = clk_tbl[cnt].clk_id;
+ clocks->clock_tbl[cnt].has_scaling = clk_tbl[cnt].scaling;
+ }
+
+ /* print clock fields */
+ venus_hfi_for_each_clock(core, cinfo) {
+ d_vpr_h("%s: clock name %s clock id %#x scaling %d\n",
+ __func__, cinfo->name, cinfo->clk_id, cinfo->has_scaling);
+ }
+
+ /* get clock handle */
+ venus_hfi_for_each_clock(core, cinfo) {
+ cinfo->clk = devm_clk_get(&core->pdev->dev, cinfo->name);
+ if (IS_ERR_OR_NULL(cinfo->clk)) {
+ d_vpr_e("%s: failed to get clock: %s\n", __func__, cinfo->name);
+ rc = PTR_ERR_OR_ZERO(cinfo->clk) ?
+ PTR_ERR_OR_ZERO(cinfo->clk) : -EINVAL;
+ cinfo->clk = NULL;
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int __init_reset_clocks(struct msm_vidc_core *core)
+{
+ const struct clk_rst_table *rst_tbl;
+ struct reset_set *rsts;
+ struct reset_info *rinfo = NULL;
+ u32 rst_count = 0, cnt = 0;
+ int rc = 0;
+
+ rsts = &core->resource->reset_set;
+
+ rst_tbl = core->platform->data.clk_rst_tbl;
+ rst_count = core->platform->data.clk_rst_tbl_size;
+
+ if (!rst_tbl || !rst_count) {
+ d_vpr_e("%s: invalid reset tbl %p or count %d\n",
+ __func__, rst_tbl, rst_count);
+ return -EINVAL;
+ }
+
+ /* allocate reset_set */
+ rsts->reset_tbl = devm_kzalloc(&core->pdev->dev,
+ sizeof(*rsts->reset_tbl) * rst_count,
+ GFP_KERNEL);
+ if (!rsts->reset_tbl) {
+ d_vpr_e("%s: failed to alloc memory for reset table\n", __func__);
+ return -ENOMEM;
+ }
+ rsts->count = rst_count;
+
+ /* populate clock field from platform data */
+ for (cnt = 0; cnt < rsts->count; cnt++) {
+ rsts->reset_tbl[cnt].name = rst_tbl[cnt].name;
+ rsts->reset_tbl[cnt].exclusive_release = rst_tbl[cnt].exclusive_release;
+ }
+
+ /* print reset clock fields */
+ venus_hfi_for_each_reset_clock(core, rinfo) {
+ d_vpr_h("%s: reset clk %s, exclusive %d\n",
+ __func__, rinfo->name, rinfo->exclusive_release);
+ }
+
+ /* get reset clock handle */
+ venus_hfi_for_each_reset_clock(core, rinfo) {
+ if (rinfo->exclusive_release)
+ rinfo->rst = devm_reset_control_get_exclusive_released(&core->pdev->dev,
+ rinfo->name);
+ else
+ rinfo->rst = devm_reset_control_get(&core->pdev->dev, rinfo->name);
+ if (IS_ERR_OR_NULL(rinfo->rst)) {
+ d_vpr_e("%s: failed to get reset clock: %s\n", __func__, rinfo->name);
+ rc = PTR_ERR_OR_ZERO(rinfo->rst) ?
+ PTR_ERR_OR_ZERO(rinfo->rst) : -EINVAL;
+ rinfo->rst = NULL;
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int __init_subcaches(struct msm_vidc_core *core)
+{
+ const struct subcache_table *llcc_tbl;
+ struct subcache_set *caches;
+ struct subcache_info *sinfo = NULL;
+ u32 llcc_count = 0, cnt = 0;
+ int rc = 0;
+
+ caches = &core->resource->subcache_set;
+
+ /* skip init if subcache not available */
+ if (!is_sys_cache_present(core))
+ return 0;
+
+ llcc_tbl = core->platform->data.subcache_tbl;
+ llcc_count = core->platform->data.subcache_tbl_size;
+
+ if (!llcc_tbl || !llcc_count) {
+ d_vpr_e("%s: invalid llcc tbl %p or count %d\n",
+ __func__, llcc_tbl, llcc_count);
+ return -EINVAL;
+ }
+
+ /* allocate clock_set */
+ caches->subcache_tbl = devm_kzalloc(&core->pdev->dev,
+ sizeof(*caches->subcache_tbl) * llcc_count,
+ GFP_KERNEL);
+ if (!caches->subcache_tbl) {
+ d_vpr_e("%s: failed to alloc memory for subcache table\n", __func__);
+ return -ENOMEM;
+ }
+ caches->count = llcc_count;
+
+ /* populate subcache fields from platform data */
+ for (cnt = 0; cnt < caches->count; cnt++) {
+ caches->subcache_tbl[cnt].name = llcc_tbl[cnt].name;
+ caches->subcache_tbl[cnt].llcc_id = llcc_tbl[cnt].llcc_id;
+ }
+
+ /* print subcache fields */
+ venus_hfi_for_each_subcache(core, sinfo) {
+ d_vpr_h("%s: name %s subcache id %d\n",
+ __func__, sinfo->name, sinfo->llcc_id);
+ }
+
+ /* get subcache/llcc handle */
+ venus_hfi_for_each_subcache(core, sinfo) {
+ sinfo->subcache = devm_llcc_get(&core->pdev->dev, sinfo->llcc_id);
+ if (IS_ERR_OR_NULL(sinfo->subcache)) {
+ d_vpr_e("%s: failed to get subcache: %d\n", __func__, sinfo->llcc_id);
+ rc = PTR_ERR_OR_ZERO(sinfo->subcache) ?
+ PTR_ERR_OR_ZERO(sinfo->subcache) : -EBADHANDLE;
+ sinfo->subcache = NULL;
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int __init_freq_table(struct msm_vidc_core *core)
+{
+ struct freq_table *freq_tbl;
+ struct freq_set *clks;
+ u32 freq_count = 0, cnt = 0;
+ int rc = 0;
+
+ clks = &core->resource->freq_set;
+
+ freq_tbl = core->platform->data.freq_tbl;
+ freq_count = core->platform->data.freq_tbl_size;
+
+ if (!freq_tbl || !freq_count) {
+ d_vpr_e("%s: invalid freq tbl %p or count %d\n",
+ __func__, freq_tbl, freq_count);
+ return -EINVAL;
+ }
+
+ /* allocate freq_set */
+ clks->freq_tbl = devm_kzalloc(&core->pdev->dev,
+ sizeof(*clks->freq_tbl) * freq_count,
+ GFP_KERNEL);
+ if (!clks->freq_tbl) {
+ d_vpr_e("%s: failed to alloc memory for freq table\n", __func__);
+ return -ENOMEM;
+ }
+ clks->count = freq_count;
+
+ /* populate freq field from platform data */
+ for (cnt = 0; cnt < clks->count; cnt++)
+ clks->freq_tbl[cnt].freq = freq_tbl[cnt].freq;
+
+ /* sort freq table */
+ sort(clks->freq_tbl, clks->count, sizeof(*clks->freq_tbl), cmp, NULL);
+
+ /* print freq field freq_set */
+ d_vpr_h("%s: updated freq table\n", __func__);
+ for (cnt = 0; cnt < clks->count; cnt++)
+ d_vpr_h("%s:\t %lu\n", __func__, clks->freq_tbl[cnt].freq);
+
+ return rc;
+}
+
+static int __init_context_banks(struct msm_vidc_core *core)
+{
+ const struct context_bank_table *cb_tbl;
+ struct context_bank_set *cbs;
+ struct context_bank_info *cbinfo = NULL;
+ u32 cb_count = 0, cnt = 0;
+ int rc = 0;
+
+ cbs = &core->resource->context_bank_set;
+
+ cb_tbl = core->platform->data.context_bank_tbl;
+ cb_count = core->platform->data.context_bank_tbl_size;
+
+ if (!cb_tbl || !cb_count) {
+ d_vpr_e("%s: invalid context bank tbl %p or count %d\n",
+ __func__, cb_tbl, cb_count);
+ return -EINVAL;
+ }
+
+ /* allocate context_bank table */
+ cbs->context_bank_tbl = devm_kzalloc(&core->pdev->dev,
+ sizeof(*cbs->context_bank_tbl) * cb_count,
+ GFP_KERNEL);
+ if (!cbs->context_bank_tbl) {
+ d_vpr_e("%s: failed to alloc memory for context_bank table\n", __func__);
+ return -ENOMEM;
+ }
+ cbs->count = cb_count;
+
+ /**
+ * populate context bank field from platform data except
+ * dev & domain which are assigned as part of context bank
+ * probe sequence
+ */
+ for (cnt = 0; cnt < cbs->count; cnt++) {
+ cbs->context_bank_tbl[cnt].name = cb_tbl[cnt].name;
+ cbs->context_bank_tbl[cnt].addr_range.start = cb_tbl[cnt].start;
+ cbs->context_bank_tbl[cnt].addr_range.size = cb_tbl[cnt].size;
+ cbs->context_bank_tbl[cnt].secure = cb_tbl[cnt].secure;
+ cbs->context_bank_tbl[cnt].dma_coherant = cb_tbl[cnt].dma_coherant;
+ cbs->context_bank_tbl[cnt].region = cb_tbl[cnt].region;
+ cbs->context_bank_tbl[cnt].dma_mask = cb_tbl[cnt].dma_mask;
+ }
+
+ /* print context_bank fiels */
+ venus_hfi_for_each_context_bank(core, cbinfo) {
+ d_vpr_h("%s: name %s addr start %#x size %#x secure %d\n",
+ __func__, cbinfo->name, cbinfo->addr_range.start,
+ cbinfo->addr_range.size, cbinfo->secure);
+
+ d_vpr_h("%s: coherant %d region %d dma_mask %llu\n",
+ __func__, cbinfo->dma_coherant, cbinfo->region,
+ cbinfo->dma_mask);
+ }
+
+ return rc;
+}
+
+static int __enable_power_domains(struct msm_vidc_core *core, const char *name)
+{
+ struct power_domain_info *pdinfo = NULL;
+ int rc = 0;
+
+ /* power up rails(mxc & mmcx) to enable RCG(video_cc_mvs0_clk_src) */
+ rc = __opp_set_rate(core, ULONG_MAX);
+ if (rc) {
+ d_vpr_e("%s: opp setrate failed\n", __func__);
+ return rc;
+ }
+
+ /* power up (gdsc0/gdsc0c) to enable (mvs0/mvs0c) branch clock */
+ venus_hfi_for_each_power_domain(core, pdinfo) {
+ if (strcmp(pdinfo->name, name))
+ continue;
+
+ rc = pm_runtime_get_sync(pdinfo->genpd_dev);
+ if (rc < 0) {
+ d_vpr_e("%s: failed to get sync: %s\n", __func__, pdinfo->name);
+ return rc;
+ }
+ d_vpr_h("%s: enabled power doamin %s\n", __func__, pdinfo->name);
+ }
+
+ return rc;
+}
+
+static int __disable_power_domains(struct msm_vidc_core *core, const char *name)
+{
+ struct power_domain_info *pdinfo = NULL;
+ int rc = 0;
+
+ /* power down (gdsc0/gdsc0c) to disable (mvs0/mvs0c) branch clock */
+ venus_hfi_for_each_power_domain(core, pdinfo) {
+ if (strcmp(pdinfo->name, name))
+ continue;
+
+ rc = pm_runtime_put_sync(pdinfo->genpd_dev);
+ if (rc) {
+ d_vpr_e("%s: failed to put sync: %s\n", __func__, pdinfo->name);
+ return rc;
+ }
+ d_vpr_h("%s: disabled power doamin %s\n", __func__, pdinfo->name);
+ }
+
+ /* power down rails(mxc & mmcx) to disable RCG(video_cc_mvs0_clk_src) */
+ rc = __opp_set_rate(core, 0);
+ if (rc) {
+ d_vpr_e("%s: opp setrate failed\n", __func__);
+ return rc;
+ }
+ msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
+
+ return rc;
+}
+
+static int __hand_off_power_domains(struct msm_vidc_core *core)
+{
+ msm_vidc_change_core_sub_state(core, 0, CORE_SUBSTATE_GDSC_HANDOFF, __func__);
+
+ return 0;
+}
+
+static int __acquire_power_domains(struct msm_vidc_core *core)
+{
+ msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_GDSC_HANDOFF, 0, __func__);
+
+ return 0;
+}
+
+static int __disable_subcaches(struct msm_vidc_core *core)
+{
+ struct subcache_info *sinfo;
+ int rc = 0;
+
+ if (!is_sys_cache_present(core))
+ return 0;
+
+ /* De-activate subcaches */
+ venus_hfi_for_each_subcache_reverse(core, sinfo) {
+ if (!sinfo->isactive)
+ continue;
+
+ d_vpr_h("%s: De-activate subcache %s\n", __func__, sinfo->name);
+ rc = llcc_slice_deactivate(sinfo->subcache);
+ if (rc) {
+ d_vpr_e("Failed to de-activate %s: %d\n",
+ sinfo->name, rc);
+ }
+ sinfo->isactive = false;
+ }
+
+ return 0;
+}
+
+static int __enable_subcaches(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ u32 c = 0;
+ struct subcache_info *sinfo;
+
+ if (!is_sys_cache_present(core))
+ return 0;
+
+ /* Activate subcaches */
+ venus_hfi_for_each_subcache(core, sinfo) {
+ rc = llcc_slice_activate(sinfo->subcache);
+ if (rc) {
+ d_vpr_e("Failed to activate %s: %d\n", sinfo->name, rc);
+ __fatal_error(true);
+ goto err_activate_fail;
+ }
+ sinfo->isactive = true;
+ d_vpr_h("Activated subcache %s\n", sinfo->name);
+ c++;
+ }
+
+ d_vpr_h("Activated %d Subcaches to Venus\n", c);
+
+ return 0;
+
+err_activate_fail:
+ __disable_subcaches(core);
+ return rc;
+}
+
+static int llcc_enable(struct msm_vidc_core *core, bool enable)
+{
+ int ret;
+
+ if (enable)
+ ret = __enable_subcaches(core);
+ else
+ ret = __disable_subcaches(core);
+
+ return ret;
+}
+
+static int __vote_bandwidth(struct bus_info *bus, unsigned long bw_kbps)
+{
+ int rc = 0;
+
+ if (!bus->icc) {
+ d_vpr_e("%s: invalid bus\n", __func__);
+ return -EINVAL;
+ }
+
+ d_vpr_p("Voting bus %s to ab %lu kBps\n", bus->name, bw_kbps);
+
+ rc = icc_set_bw(bus->icc, bw_kbps, 0);
+ if (rc)
+ d_vpr_e("Failed voting bus %s to ab %lu, rc=%d\n",
+ bus->name, bw_kbps, rc);
+
+ return rc;
+}
+
+static int __unvote_buses(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ struct bus_info *bus = NULL;
+
+ core->power.bw_ddr = 0;
+ core->power.bw_llcc = 0;
+
+ venus_hfi_for_each_bus(core, bus) {
+ rc = __vote_bandwidth(bus, 0);
+ if (rc)
+ goto err_unknown_device;
+ }
+
+err_unknown_device:
+ return rc;
+}
+
+static int __vote_buses(struct msm_vidc_core *core,
+ unsigned long bw_ddr, unsigned long bw_llcc)
+{
+ int rc = 0;
+ struct bus_info *bus = NULL;
+ unsigned long bw_kbps = 0, bw_prev = 0;
+ enum vidc_bus_type type;
+
+ venus_hfi_for_each_bus(core, bus) {
+ if (bus && bus->icc) {
+ type = get_type_frm_name(bus->name);
+
+ if (type == DDR) {
+ bw_kbps = bw_ddr;
+ bw_prev = core->power.bw_ddr;
+ } else if (type == LLCC) {
+ bw_kbps = bw_llcc;
+ bw_prev = core->power.bw_llcc;
+ } else {
+ bw_kbps = bus->max_kbps;
+ bw_prev = core->power.bw_ddr ?
+ bw_kbps : 0;
+ }
+
+ /* ensure freq is within limits */
+ bw_kbps = clamp_t(typeof(bw_kbps), bw_kbps,
+ bus->min_kbps, bus->max_kbps);
+
+ if (TRIVIAL_BW_CHANGE(bw_kbps, bw_prev) && bw_prev) {
+ d_vpr_l("Skip voting bus %s to %lu kBps\n",
+ bus->name, bw_kbps);
+ continue;
+ }
+
+ rc = __vote_bandwidth(bus, bw_kbps);
+
+ if (type == DDR)
+ core->power.bw_ddr = bw_kbps;
+ else if (type == LLCC)
+ core->power.bw_llcc = bw_kbps;
+ } else {
+ d_vpr_e("No BUS to Vote\n");
+ }
+ }
+
+ return rc;
+}
+
+static int set_bw(struct msm_vidc_core *core, unsigned long bw_ddr,
+ unsigned long bw_llcc)
+{
+ if (!bw_ddr && !bw_llcc)
+ return __unvote_buses(core);
+
+ return __vote_buses(core, bw_ddr, bw_llcc);
+}
+
+static int __set_clk_rate(struct msm_vidc_core *core, struct clock_info *cl,
+ u64 rate)
+{
+ int rc = 0;
+
+ /* bail early if requested clk rate is not changed */
+ if (rate == cl->prev)
+ return 0;
+
+ d_vpr_p("Scaling clock %s to %llu, prev %llu\n",
+ cl->name, rate, cl->prev);
+
+ rc = clk_set_rate(cl->clk, rate);
+ if (rc) {
+ d_vpr_e("%s: Failed to set clock rate %llu %s: %d\n",
+ __func__, rate, cl->name, rc);
+ return rc;
+ }
+
+ cl->prev = rate;
+
+ return rc;
+}
+
+static int __set_clocks(struct msm_vidc_core *core, u64 freq)
+{
+ struct clock_info *cl;
+ int rc = 0;
+
+ /* scale mxc & mmcx rails */
+ rc = __opp_set_rate(core, freq);
+ if (rc) {
+ d_vpr_e("%s: opp setrate failed %lld\n", __func__, freq);
+ return rc;
+ }
+
+ venus_hfi_for_each_clock(core, cl) {
+ if (cl->has_scaling) {
+ rc = __set_clk_rate(core, cl, freq);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int __disable_unprepare_clock(struct msm_vidc_core *core,
+ const char *clk_name)
+{
+ int rc = 0;
+ struct clock_info *cl;
+ bool found;
+
+ found = false;
+ venus_hfi_for_each_clock(core, cl) {
+ if (!cl->clk) {
+ d_vpr_e("%s: invalid clock %s\n", __func__, cl->name);
+ return -EINVAL;
+ }
+ if (strcmp(cl->name, clk_name))
+ continue;
+ found = true;
+ clk_disable_unprepare(cl->clk);
+ if (cl->has_scaling)
+ __set_clk_rate(core, cl, 0);
+ cl->prev = 0;
+ d_vpr_h("%s: clock %s disable unprepared\n", __func__, cl->name);
+ break;
+ }
+ if (!found) {
+ d_vpr_e("%s: clock %s not found\n", __func__, clk_name);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __prepare_enable_clock(struct msm_vidc_core *core,
+ const char *clk_name)
+{
+ int rc = 0;
+ struct clock_info *cl;
+ bool found;
+ u64 rate = 0;
+
+ found = false;
+ venus_hfi_for_each_clock(core, cl) {
+ if (!cl->clk) {
+ d_vpr_e("%s: invalid clock\n", __func__);
+ return -EINVAL;
+ }
+ if (strcmp(cl->name, clk_name))
+ continue;
+ found = true;
+ /*
+ * For the clocks we control, set the rate prior to preparing
+ * them. Since we don't really have a load at this point, scale
+ * it to the lowest frequency possible
+ */
+ if (cl->has_scaling) {
+ rate = clk_round_rate(cl->clk, 0);
+ /**
+ * source clock is already multipled with scaling ratio and __set_clk_rate
+ * attempts to multiply again. So divide scaling ratio before calling
+ * __set_clk_rate.
+ */
+ rate = rate / MSM_VIDC_CLOCK_SOURCE_SCALING_RATIO;
+ __set_clk_rate(core, cl, rate);
+ }
+
+ rc = clk_prepare_enable(cl->clk);
+ if (rc) {
+ d_vpr_e("%s: failed to enable clock %s\n",
+ __func__, cl->name);
+ return rc;
+ }
+ if (!__clk_is_enabled(cl->clk)) {
+ d_vpr_e("%s: clock %s not enabled\n",
+ __func__, cl->name);
+ clk_disable_unprepare(cl->clk);
+ if (cl->has_scaling)
+ __set_clk_rate(core, cl, 0);
+ return -EINVAL;
+ }
+ d_vpr_h("%s: clock %s prepare enabled\n", __func__, cl->name);
+ break;
+ }
+ if (!found) {
+ d_vpr_e("%s: clock %s not found\n", __func__, clk_name);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __init_resources(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = __init_register_base(core);
+ if (rc)
+ return rc;
+
+ rc = __init_irq(core);
+ if (rc)
+ return rc;
+
+ rc = __init_bus(core);
+ if (rc)
+ return rc;
+
+ rc = call_res_op(core, gdsc_init, core);
+ if (rc)
+ return rc;
+
+ rc = __init_clocks(core);
+ if (rc)
+ return rc;
+
+ rc = __init_reset_clocks(core);
+ if (rc)
+ return rc;
+
+ rc = __init_subcaches(core);
+ if (rc)
+ return rc;
+
+ rc = __init_freq_table(core);
+ if (rc)
+ return rc;
+
+ rc = __init_context_banks(core);
+
+ return rc;
+}
+
+static int __reset_control_acquire_name(struct msm_vidc_core *core,
+ const char *name)
+{
+ struct reset_info *rcinfo = NULL;
+ int rc = 0;
+ bool found = false;
+
+ venus_hfi_for_each_reset_clock(core, rcinfo) {
+ if (strcmp(rcinfo->name, name))
+ continue;
+
+ /* this function is valid only for exclusive_release reset clocks*/
+ if (!rcinfo->exclusive_release) {
+ d_vpr_e("%s: unsupported reset control (%s), exclusive %d\n",
+ __func__, name, rcinfo->exclusive_release);
+ return -EINVAL;
+ }
+
+ found = true;
+
+ rc = reset_control_acquire(rcinfo->rst);
+ if (rc)
+ d_vpr_e("%s: failed to acquire reset control (%s), rc = %d\n",
+ __func__, rcinfo->name, rc);
+ else
+ d_vpr_h("%s: acquire reset control (%s)\n",
+ __func__, rcinfo->name);
+ break;
+ }
+ if (!found) {
+ d_vpr_e("%s: reset control (%s) not found\n", __func__, name);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __reset_control_release_name(struct msm_vidc_core *core,
+ const char *name)
+{
+ struct reset_info *rcinfo = NULL;
+ int rc = 0;
+ bool found = false;
+
+ venus_hfi_for_each_reset_clock(core, rcinfo) {
+ if (strcmp(rcinfo->name, name))
+ continue;
+
+ /* this function is valid only for exclusive_release reset clocks*/
+ if (!rcinfo->exclusive_release) {
+ d_vpr_e("%s: unsupported reset control (%s), exclusive %d\n",
+ __func__, name, rcinfo->exclusive_release);
+ return -EINVAL;
+ }
+
+ found = true;
+
+ reset_control_release(rcinfo->rst);
+ if (rc)
+ d_vpr_e("%s: release reset control (%s) failed\n",
+ __func__, rcinfo->name);
+ else
+ d_vpr_h("%s: release reset control (%s) done\n",
+ __func__, rcinfo->name);
+ break;
+ }
+ if (!found) {
+ d_vpr_e("%s: reset control (%s) not found\n", __func__, name);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __reset_control_assert_name(struct msm_vidc_core *core,
+ const char *name)
+{
+ struct reset_info *rcinfo = NULL;
+ int rc = 0;
+ bool found = false;
+
+ venus_hfi_for_each_reset_clock(core, rcinfo) {
+ if (strcmp(rcinfo->name, name))
+ continue;
+
+ found = true;
+ rc = reset_control_assert(rcinfo->rst);
+ if (rc)
+ d_vpr_e("%s: failed to assert reset control (%s), rc = %d\n",
+ __func__, rcinfo->name, rc);
+ else
+ d_vpr_h("%s: assert reset control (%s)\n",
+ __func__, rcinfo->name);
+ break;
+ }
+ if (!found) {
+ d_vpr_e("%s: reset control (%s) not found\n", __func__, name);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __reset_control_deassert_name(struct msm_vidc_core *core,
+ const char *name)
+{
+ struct reset_info *rcinfo = NULL;
+ int rc = 0;
+ bool found = false;
+
+ venus_hfi_for_each_reset_clock(core, rcinfo) {
+ if (strcmp(rcinfo->name, name))
+ continue;
+ found = true;
+ rc = reset_control_deassert(rcinfo->rst);
+ if (rc)
+ d_vpr_e("%s: deassert reset control for (%s) failed, rc %d\n",
+ __func__, rcinfo->name, rc);
+ else
+ d_vpr_h("%s: deassert reset control (%s)\n",
+ __func__, rcinfo->name);
+ break;
+ }
+ if (!found) {
+ d_vpr_e("%s: reset control (%s) not found\n", __func__, name);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static int __reset_control_deassert(struct msm_vidc_core *core)
+{
+ struct reset_info *rcinfo = NULL;
+ int rc = 0;
+
+ venus_hfi_for_each_reset_clock(core, rcinfo) {
+ rc = reset_control_deassert(rcinfo->rst);
+ if (rc) {
+ d_vpr_e("%s: deassert reset control failed. rc = %d\n", __func__, rc);
+ continue;
+ }
+ d_vpr_h("%s: deassert reset control %s\n", __func__, rcinfo->name);
+ }
+
+ return rc;
+}
+
+static int __reset_control_assert(struct msm_vidc_core *core)
+{
+ struct reset_info *rcinfo = NULL;
+ int rc = 0, cnt = 0;
+
+ venus_hfi_for_each_reset_clock(core, rcinfo) {
+ if (!rcinfo->rst) {
+ d_vpr_e("%s: invalid reset clock %s\n",
+ __func__, rcinfo->name);
+ return -EINVAL;
+ }
+ rc = reset_control_assert(rcinfo->rst);
+ if (rc) {
+ d_vpr_e("%s: failed to assert reset control %s, rc = %d\n",
+ __func__, rcinfo->name, rc);
+ goto deassert_reset_control;
+ }
+ cnt++;
+ d_vpr_h("%s: assert reset control %s, count %d\n", __func__, rcinfo->name, cnt);
+
+ usleep_range(1000, 1100);
+ }
+
+ return rc;
+deassert_reset_control:
+ venus_hfi_for_each_reset_clock_reverse_continue(core, rcinfo, cnt) {
+ d_vpr_e("%s: deassert reset control %s\n", __func__, rcinfo->name);
+ reset_control_deassert(rcinfo->rst);
+ }
+
+ return rc;
+}
+
+static int __reset_ahb2axi_bridge(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = __reset_control_assert(core);
+ if (rc)
+ return rc;
+
+ rc = __reset_control_deassert(core);
+
+ return rc;
+}
+
+static const struct msm_vidc_resources_ops res_ops = {
+ .init = __init_resources,
+ .reset_bridge = __reset_ahb2axi_bridge,
+ .reset_control_acquire = __reset_control_acquire_name,
+ .reset_control_release = __reset_control_release_name,
+ .reset_control_assert = __reset_control_assert_name,
+ .reset_control_deassert = __reset_control_deassert_name,
+ .gdsc_init = __init_power_domains,
+ .gdsc_on = __enable_power_domains,
+ .gdsc_off = __disable_power_domains,
+ .gdsc_hw_ctrl = __hand_off_power_domains,
+ .gdsc_sw_ctrl = __acquire_power_domains,
+ .llcc = llcc_enable,
+ .set_bw = set_bw,
+ .set_clks = __set_clocks,
+ .clk_enable = __prepare_enable_clock,
+ .clk_disable = __disable_unprepare_clock,
+};
+
+const struct msm_vidc_resources_ops *get_resources_ops(void)
+{
+ return &res_ops;
+}
--
2.7.4


2023-07-28 15:34:05

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 14/33] iris: vidc: add helpers for state management

This implements the functions to handle different core
and instance state transitions.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../platform/qcom/iris/vidc/inc/msm_vidc_state.h | 102 ++
.../platform/qcom/iris/vidc/src/msm_vidc_state.c | 1607 ++++++++++++++++++++
2 files changed, 1709 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c

diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h
new file mode 100644
index 0000000..7fe4fcc
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _MSM_VIDC_STATE_H_
+#define _MSM_VIDC_STATE_H_
+
+#include "msm_vidc_internal.h"
+
+enum msm_vidc_core_state {
+ MSM_VIDC_CORE_DEINIT,
+ MSM_VIDC_CORE_INIT_WAIT,
+ MSM_VIDC_CORE_INIT,
+ MSM_VIDC_CORE_ERROR,
+};
+
+enum msm_vidc_core_sub_state {
+ CORE_SUBSTATE_NONE = 0x0,
+ CORE_SUBSTATE_POWER_ENABLE = BIT(0),
+ CORE_SUBSTATE_GDSC_HANDOFF = BIT(1),
+ CORE_SUBSTATE_PM_SUSPEND = BIT(2),
+ CORE_SUBSTATE_FW_PWR_CTRL = BIT(3),
+ CORE_SUBSTATE_PAGE_FAULT = BIT(4),
+ CORE_SUBSTATE_CPU_WATCHDOG = BIT(5),
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE = BIT(6),
+ CORE_SUBSTATE_MAX = BIT(7),
+};
+
+enum msm_vidc_core_event_type {
+ CORE_EVENT_NONE = BIT(0),
+ CORE_EVENT_UPDATE_SUB_STATE = BIT(1),
+};
+
+enum msm_vidc_state {
+ MSM_VIDC_OPEN,
+ MSM_VIDC_INPUT_STREAMING,
+ MSM_VIDC_OUTPUT_STREAMING,
+ MSM_VIDC_STREAMING,
+ MSM_VIDC_CLOSE,
+ MSM_VIDC_ERROR,
+};
+
+#define MSM_VIDC_SUB_STATE_NONE 0
+#define MSM_VIDC_MAX_SUB_STATES 6
+/*
+ * max value of inst->sub_state if all
+ * the 6 valid bits are set i.e 111111==>63
+ */
+#define MSM_VIDC_MAX_SUB_STATE_VALUE ((1 << MSM_VIDC_MAX_SUB_STATES) - 1)
+
+enum msm_vidc_sub_state {
+ MSM_VIDC_DRAIN = BIT(0),
+ MSM_VIDC_DRC = BIT(1),
+ MSM_VIDC_DRAIN_LAST_BUFFER = BIT(2),
+ MSM_VIDC_DRC_LAST_BUFFER = BIT(3),
+ MSM_VIDC_INPUT_PAUSE = BIT(4),
+ MSM_VIDC_OUTPUT_PAUSE = BIT(5),
+};
+
+enum msm_vidc_event {
+ MSM_VIDC_TRY_FMT,
+ MSM_VIDC_S_FMT,
+ MSM_VIDC_REQBUFS,
+ MSM_VIDC_S_CTRL,
+ MSM_VIDC_STREAMON,
+ MSM_VIDC_STREAMOFF,
+ MSM_VIDC_CMD_START,
+ MSM_VIDC_CMD_STOP,
+ MSM_VIDC_BUF_QUEUE,
+};
+
+/* core statemachine functions */
+enum msm_vidc_allow msm_vidc_allow_core_state_change(struct msm_vidc_core *core,
+ enum msm_vidc_core_state req_state);
+int msm_vidc_update_core_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_state request_state, const char *func);
+bool core_in_valid_state(struct msm_vidc_core *core);
+bool is_core_state(struct msm_vidc_core *core, enum msm_vidc_core_state state);
+bool is_core_sub_state(struct msm_vidc_core *core, enum msm_vidc_core_sub_state sub_state);
+const char *core_state_name(enum msm_vidc_core_state state);
+const char *core_sub_state_name(enum msm_vidc_core_sub_state sub_state);
+
+/* inst statemachine functions */
+bool is_drc_pending(struct msm_vidc_inst *inst);
+bool is_drain_pending(struct msm_vidc_inst *inst);
+int msm_vidc_update_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_state request_state, const char *func);
+int msm_vidc_change_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_state request_state, const char *func);
+int msm_vidc_change_sub_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_sub_state clear_sub_state,
+ enum msm_vidc_sub_state set_sub_state,
+ const char *func);
+const char *state_name(enum msm_vidc_state state);
+const char *sub_state_name(enum msm_vidc_sub_state sub_state);
+bool is_state(struct msm_vidc_inst *inst, enum msm_vidc_state state);
+bool is_sub_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_sub_state sub_state);
+
+#endif // _MSM_VIDC_STATE_H_
diff --git a/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c
new file mode 100644
index 0000000..0361e69
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c
@@ -0,0 +1,1607 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "msm_vidc.h"
+#include "msm_vidc_control.h"
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_state.h"
+#include "msm_vidc_vb2.h"
+#include "venus_hfi.h"
+
+bool core_in_valid_state(struct msm_vidc_core *core)
+{
+ return (core->state == MSM_VIDC_CORE_INIT ||
+ core->state == MSM_VIDC_CORE_INIT_WAIT);
+}
+
+bool is_core_state(struct msm_vidc_core *core, enum msm_vidc_core_state state)
+{
+ return core->state == state;
+}
+
+bool is_drc_pending(struct msm_vidc_inst *inst)
+{
+ return is_sub_state(inst, MSM_VIDC_DRC) &&
+ is_sub_state(inst, MSM_VIDC_DRC_LAST_BUFFER);
+}
+
+bool is_drain_pending(struct msm_vidc_inst *inst)
+{
+ return is_sub_state(inst, MSM_VIDC_DRAIN) &&
+ is_sub_state(inst, MSM_VIDC_DRAIN_LAST_BUFFER);
+}
+
+static const char * const core_state_name_arr[] = {
+ [MSM_VIDC_CORE_DEINIT] = "CORE_DEINIT",
+ [MSM_VIDC_CORE_INIT_WAIT] = "CORE_INIT_WAIT",
+ [MSM_VIDC_CORE_INIT] = "CORE_INIT",
+ [MSM_VIDC_CORE_ERROR] = "CORE_ERROR",
+};
+
+const char *core_state_name(enum msm_vidc_core_state state)
+{
+ const char *name = "UNKNOWN STATE";
+
+ if (state >= ARRAY_SIZE(core_state_name_arr))
+ goto exit;
+
+ name = core_state_name_arr[state];
+
+exit:
+ return name;
+}
+
+static const char * const event_name_arr[] = {
+ [MSM_VIDC_TRY_FMT] = "TRY_FMT",
+ [MSM_VIDC_S_FMT] = "S_FMT",
+ [MSM_VIDC_REQBUFS] = "REQBUFS",
+ [MSM_VIDC_S_CTRL] = "S_CTRL",
+ [MSM_VIDC_STREAMON] = "STREAMON",
+ [MSM_VIDC_STREAMOFF] = "STREAMOFF",
+ [MSM_VIDC_CMD_START] = "CMD_START",
+ [MSM_VIDC_CMD_STOP] = "CMD_STOP",
+ [MSM_VIDC_BUF_QUEUE] = "BUF_QUEUE",
+};
+
+static const char *event_name(enum msm_vidc_event event)
+{
+ const char *name = "UNKNOWN EVENT";
+
+ if (event >= ARRAY_SIZE(event_name_arr))
+ goto exit;
+
+ name = event_name_arr[event];
+
+exit:
+ return name;
+}
+
+static int __strict_inst_check(struct msm_vidc_inst *inst, const char *function)
+{
+ bool fatal = !mutex_is_locked(&inst->lock);
+
+ WARN_ON(fatal);
+
+ return fatal ? -EINVAL : 0;
+}
+
+struct msm_vidc_core_sub_state_allow {
+ enum msm_vidc_core_state state;
+ enum msm_vidc_allow allow;
+ u32 sub_state_mask;
+};
+
+static u32 msm_vidc_core_sub_state_mask(enum msm_vidc_core_state state,
+ u32 allow)
+{
+ int cnt;
+ u32 sub_state_mask = 0;
+ static struct msm_vidc_core_sub_state_allow sub_state[] = {
+ /* state, allow, sub_state */
+ {MSM_VIDC_CORE_DEINIT, MSM_VIDC_ALLOW, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ {MSM_VIDC_CORE_DEINIT, MSM_VIDC_IGNORE, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ {MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_ALLOW, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ {MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_DISALLOW, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ {MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_IGNORE, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ {MSM_VIDC_CORE_INIT, MSM_VIDC_ALLOW, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ {MSM_VIDC_CORE_ERROR, MSM_VIDC_ALLOW, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ {MSM_VIDC_CORE_ERROR, MSM_VIDC_DISALLOW, CORE_SUBSTATE_POWER_ENABLE |
+ CORE_SUBSTATE_GDSC_HANDOFF |
+ CORE_SUBSTATE_PM_SUSPEND |
+ CORE_SUBSTATE_FW_PWR_CTRL |
+ CORE_SUBSTATE_PAGE_FAULT |
+ CORE_SUBSTATE_CPU_WATCHDOG |
+ CORE_SUBSTATE_VIDEO_UNRESPONSIVE },
+ };
+
+ for (cnt = 0; cnt < ARRAY_SIZE(sub_state); cnt++) {
+ if (sub_state[cnt].state == state && sub_state[cnt].allow == allow) {
+ sub_state_mask = sub_state[cnt].sub_state_mask;
+ break;
+ }
+ }
+
+ return sub_state_mask;
+}
+
+static int msm_vidc_core_deinit_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_event_type type,
+ struct msm_vidc_event_data *data)
+{
+ int rc = 0;
+
+ switch (type) {
+ case CORE_EVENT_UPDATE_SUB_STATE:
+ {
+ u32 req_sub_state = data->edata.uval;
+ u32 allow_mask = msm_vidc_core_sub_state_mask(core->state, MSM_VIDC_ALLOW);
+
+ req_sub_state = data->edata.uval;
+
+ /* none of the requested substate supported */
+ if (!(req_sub_state & allow_mask)) {
+ d_vpr_e("%s: invalid substate update request %#x\n",
+ __func__, req_sub_state);
+ return -EINVAL;
+ }
+
+ /* update core substate */
+ core->sub_state |= req_sub_state & allow_mask;
+ return rc;
+ }
+ default: {
+ d_vpr_e("%s: unexpected core event type %u\n",
+ __func__, type);
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_core_init_wait_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_event_type type,
+ struct msm_vidc_event_data *data)
+{
+ int rc = 0;
+
+ switch (type) {
+ case CORE_EVENT_UPDATE_SUB_STATE:
+ {
+ u32 req_sub_state = data->edata.uval;
+ u32 allow_mask = msm_vidc_core_sub_state_mask(core->state, MSM_VIDC_ALLOW);
+
+ req_sub_state = data->edata.uval;
+
+ /* none of the requested substate supported */
+ if (!(req_sub_state & allow_mask)) {
+ d_vpr_e("%s: invalid substate update request %#x\n",
+ __func__, req_sub_state);
+ return -EINVAL;
+ }
+
+ /* update core substate */
+ core->sub_state |= req_sub_state & allow_mask;
+ return rc;
+ }
+ default: {
+ d_vpr_e("%s: unexpected core event type %u\n",
+ __func__, type);
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_core_init_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_event_type type,
+ struct msm_vidc_event_data *data)
+{
+ int rc = 0;
+
+ switch (type) {
+ case CORE_EVENT_UPDATE_SUB_STATE:
+ {
+ u32 req_sub_state = data->edata.uval;
+ u32 allow_mask = msm_vidc_core_sub_state_mask(core->state, MSM_VIDC_ALLOW);
+
+ req_sub_state = data->edata.uval;
+
+ /* none of the requested substate supported */
+ if (!(req_sub_state & allow_mask)) {
+ d_vpr_e("%s: invalid substate update request %#x\n",
+ __func__, req_sub_state);
+ return -EINVAL;
+ }
+
+ /* update core substate */
+ core->sub_state |= req_sub_state & allow_mask;
+ return rc;
+ }
+ default: {
+ d_vpr_e("%s: unexpected core event type %u\n",
+ __func__, type);
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_core_error_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_event_type type,
+ struct msm_vidc_event_data *data)
+{
+ int rc = 0;
+
+ switch (type) {
+ case CORE_EVENT_UPDATE_SUB_STATE:
+ {
+ u32 req_sub_state = data->edata.uval;
+ u32 allow_mask = msm_vidc_core_sub_state_mask(core->state, MSM_VIDC_ALLOW);
+
+ req_sub_state = data->edata.uval;
+
+ /* none of the requested substate supported */
+ if (!(req_sub_state & allow_mask)) {
+ d_vpr_e("%s: invalid substate update request %#x\n",
+ __func__, req_sub_state);
+ return -EINVAL;
+ }
+
+ /* update core substate */
+ core->sub_state |= req_sub_state & allow_mask;
+ return rc;
+ }
+ default: {
+ d_vpr_e("%s: unexpected core event type %u\n",
+ __func__, type);
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+struct msm_vidc_core_state_handle {
+ enum msm_vidc_core_state state;
+ int (*handle)(struct msm_vidc_core *core,
+ enum msm_vidc_core_event_type type,
+ struct msm_vidc_event_data *data);
+};
+
+static struct msm_vidc_core_state_handle
+ *msm_vidc_get_core_state_handle(enum msm_vidc_core_state req_state)
+{
+ int cnt;
+ struct msm_vidc_core_state_handle *core_state_handle = NULL;
+ static struct msm_vidc_core_state_handle state_handle[] = {
+ {MSM_VIDC_CORE_DEINIT, msm_vidc_core_deinit_state },
+ {MSM_VIDC_CORE_INIT_WAIT, msm_vidc_core_init_wait_state },
+ {MSM_VIDC_CORE_INIT, msm_vidc_core_init_state },
+ {MSM_VIDC_CORE_ERROR, msm_vidc_core_error_state },
+ };
+
+ for (cnt = 0; cnt < ARRAY_SIZE(state_handle); cnt++) {
+ if (state_handle[cnt].state == req_state) {
+ core_state_handle = &state_handle[cnt];
+ break;
+ }
+ }
+
+ /* if req_state does not exist in the table */
+ if (cnt == ARRAY_SIZE(state_handle)) {
+ d_vpr_e("%s: invalid core state \"%s\" requested\n",
+ __func__, core_state_name(req_state));
+ return core_state_handle;
+ }
+
+ return core_state_handle;
+}
+
+int msm_vidc_update_core_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_state request_state, const char *func)
+{
+ struct msm_vidc_core_state_handle *state_handle = NULL;
+ int rc = 0;
+
+ /* get core state handler for requested state */
+ state_handle = msm_vidc_get_core_state_handle(request_state);
+ if (!state_handle)
+ return -EINVAL;
+
+ d_vpr_h("%s: core state changed to %s from %s\n", func,
+ core_state_name(state_handle->state), core_state_name(core->state));
+
+ /* finally update core state and handler */
+ core->state = state_handle->state;
+ core->state_handle = state_handle->handle;
+
+ return rc;
+}
+
+struct msm_vidc_core_state_allow {
+ enum msm_vidc_core_state from;
+ enum msm_vidc_core_state to;
+ enum msm_vidc_allow allow;
+};
+
+enum msm_vidc_allow msm_vidc_allow_core_state_change(struct msm_vidc_core *core,
+ enum msm_vidc_core_state req_state)
+{
+ int cnt;
+ enum msm_vidc_allow allow = MSM_VIDC_DISALLOW;
+ static struct msm_vidc_core_state_allow state[] = {
+ /* from, to, allow */
+ {MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_DEINIT, MSM_VIDC_IGNORE },
+ {MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_ALLOW },
+ {MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_INIT, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_CORE_DEINIT, MSM_VIDC_CORE_ERROR, MSM_VIDC_IGNORE },
+ {MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_DEINIT, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_IGNORE },
+ {MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_INIT, MSM_VIDC_ALLOW },
+ {MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_CORE_ERROR, MSM_VIDC_ALLOW },
+ {MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_DEINIT, MSM_VIDC_ALLOW },
+ {MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_INIT, MSM_VIDC_IGNORE },
+ {MSM_VIDC_CORE_INIT, MSM_VIDC_CORE_ERROR, MSM_VIDC_ALLOW },
+ {MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_DEINIT, MSM_VIDC_ALLOW },
+ {MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_INIT_WAIT, MSM_VIDC_IGNORE },
+ {MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_INIT, MSM_VIDC_IGNORE },
+ {MSM_VIDC_CORE_ERROR, MSM_VIDC_CORE_ERROR, MSM_VIDC_IGNORE },
+ };
+
+ for (cnt = 0; cnt < ARRAY_SIZE(state); cnt++) {
+ if (state[cnt].from == core->state && state[cnt].to == req_state) {
+ allow = state[cnt].allow;
+ break;
+ }
+ }
+
+ return allow;
+}
+
+int msm_vidc_change_core_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_state request_state, const char *func)
+{
+ enum msm_vidc_allow allow;
+ int rc = 0;
+
+ /* core must be locked */
+ rc = __strict_check(core, func);
+ if (rc) {
+ d_vpr_e("%s(): core was not locked\n", func);
+ return rc;
+ }
+
+ /* current and requested state is same */
+ if (core->state == request_state)
+ return 0;
+
+ /* check if requested state movement is allowed */
+ allow = msm_vidc_allow_core_state_change(core, request_state);
+ if (allow == MSM_VIDC_IGNORE) {
+ d_vpr_h("%s: %s core state change %s -> %s\n", func,
+ allow_name(allow), core_state_name(core->state),
+ core_state_name(request_state));
+ return 0;
+ } else if (allow == MSM_VIDC_DISALLOW) {
+ d_vpr_e("%s: %s core state change %s -> %s\n", func,
+ allow_name(allow), core_state_name(core->state),
+ core_state_name(request_state));
+ return -EINVAL;
+ }
+
+ /* go ahead and update core state */
+ return msm_vidc_update_core_state(core, request_state, func);
+}
+
+bool is_core_sub_state(struct msm_vidc_core *core, enum msm_vidc_core_sub_state sub_state)
+{
+ return !!(core->sub_state & sub_state);
+}
+
+const char *core_sub_state_name(enum msm_vidc_core_sub_state sub_state)
+{
+ switch (sub_state) {
+ case CORE_SUBSTATE_NONE: return "NONE ";
+ case CORE_SUBSTATE_GDSC_HANDOFF: return "GDSC_HANDOFF ";
+ case CORE_SUBSTATE_PM_SUSPEND: return "PM_SUSPEND ";
+ case CORE_SUBSTATE_FW_PWR_CTRL: return "FW_PWR_CTRL ";
+ case CORE_SUBSTATE_POWER_ENABLE: return "POWER_ENABLE ";
+ case CORE_SUBSTATE_PAGE_FAULT: return "PAGE_FAULT ";
+ case CORE_SUBSTATE_CPU_WATCHDOG: return "CPU_WATCHDOG ";
+ case CORE_SUBSTATE_VIDEO_UNRESPONSIVE: return "VIDEO_UNRESPONSIVE ";
+ case CORE_SUBSTATE_MAX: return "MAX ";
+ }
+
+ return "UNKNOWN ";
+}
+
+static int prepare_core_sub_state_name(enum msm_vidc_core_sub_state sub_state,
+ char *buf, u32 size)
+{
+ int i = 0;
+
+ if (!buf || !size)
+ return -EINVAL;
+
+ strscpy(buf, "\0", size);
+ if (sub_state == CORE_SUBSTATE_NONE) {
+ strscpy(buf, "CORE_SUBSTATE_NONE", size);
+ return 0;
+ }
+
+ for (i = 0; BIT(i) < CORE_SUBSTATE_MAX; i++) {
+ if (sub_state & BIT(i))
+ strlcat(buf, core_sub_state_name(BIT(i)), size);
+ }
+
+ return 0;
+}
+
+static int msm_vidc_update_core_sub_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_sub_state sub_state,
+ const char *func)
+{
+ struct msm_vidc_event_data data;
+ char sub_state_name[MAX_NAME_LENGTH];
+ int ret = 0, rc = 0;
+
+ /* no substate update */
+ if (!sub_state)
+ return 0;
+
+ /* invoke update core substate event */
+ memset(&data, 0, sizeof(struct msm_vidc_event_data));
+ data.edata.uval = sub_state;
+ rc = core->state_handle(core, CORE_EVENT_UPDATE_SUB_STATE, &data);
+ if (rc) {
+ ret = prepare_core_sub_state_name(sub_state, sub_state_name,
+ sizeof(sub_state_name) - 1);
+ if (!ret)
+ d_vpr_e("%s: state %s, requested invalid core substate %s\n",
+ func, core_state_name(core->state), sub_state_name);
+ return rc;
+ }
+
+ return rc;
+}
+
+int msm_vidc_change_core_sub_state(struct msm_vidc_core *core,
+ enum msm_vidc_core_sub_state clear_sub_state,
+ enum msm_vidc_core_sub_state set_sub_state,
+ const char *func)
+{
+ int rc = 0;
+ enum msm_vidc_core_sub_state prev_sub_state;
+
+ /* core must be locked */
+ rc = __strict_check(core, func);
+ if (rc) {
+ d_vpr_e("%s(): core was not locked\n", func);
+ return rc;
+ }
+
+ /* sanitize core state handler */
+ if (!core->state_handle) {
+ d_vpr_e("%s: invalid core state handle\n", __func__);
+ return -EINVAL;
+ }
+
+ /* final value will not change */
+ if (clear_sub_state == set_sub_state)
+ return 0;
+
+ /* sanitize clear & set value */
+ if (set_sub_state > CORE_SUBSTATE_MAX ||
+ clear_sub_state > CORE_SUBSTATE_MAX) {
+ d_vpr_e("%s: invalid sub states. clear %#x or set %#x\n",
+ func, clear_sub_state, set_sub_state);
+ return -EINVAL;
+ }
+
+ prev_sub_state = core->sub_state;
+
+ /* set sub state */
+ rc = msm_vidc_update_core_sub_state(core, set_sub_state, func);
+ if (rc)
+ return rc;
+
+ /* check if all core substates updated */
+ if ((core->sub_state & set_sub_state) != set_sub_state)
+ d_vpr_e("%s: all substates not updated %#x, expected %#x\n",
+ func, core->sub_state & set_sub_state, set_sub_state);
+
+ /* clear sub state */
+ core->sub_state &= ~clear_sub_state;
+
+ /* print substates only when there is a change */
+ if (core->sub_state != prev_sub_state) {
+ rc = prepare_core_sub_state_name(core->sub_state, core->sub_state_name,
+ sizeof(core->sub_state_name) - 1);
+ if (!rc)
+ d_vpr_h("%s: core sub state changed to %s\n", func, core->sub_state_name);
+ }
+
+ return 0;
+}
+
+/* do not modify the state names as it is used in test scripts */
+static const char * const state_name_arr[] = {
+ [MSM_VIDC_OPEN] = "OPEN",
+ [MSM_VIDC_INPUT_STREAMING] = "INPUT_STREAMING",
+ [MSM_VIDC_OUTPUT_STREAMING] = "OUTPUT_STREAMING",
+ [MSM_VIDC_STREAMING] = "STREAMING",
+ [MSM_VIDC_CLOSE] = "CLOSE",
+ [MSM_VIDC_ERROR] = "ERROR",
+};
+
+const char *state_name(enum msm_vidc_state state)
+{
+ const char *name = "UNKNOWN STATE";
+
+ if (state >= ARRAY_SIZE(state_name_arr))
+ goto exit;
+
+ name = state_name_arr[state];
+
+exit:
+ return name;
+}
+
+bool is_state(struct msm_vidc_inst *inst, enum msm_vidc_state state)
+{
+ return inst->state == state;
+}
+
+bool is_sub_state(struct msm_vidc_inst *inst, enum msm_vidc_sub_state sub_state)
+{
+ return (inst->sub_state & sub_state);
+}
+
+const char *sub_state_name(enum msm_vidc_sub_state sub_state)
+{
+ switch (sub_state) {
+ case MSM_VIDC_DRAIN: return "DRAIN ";
+ case MSM_VIDC_DRC: return "DRC ";
+ case MSM_VIDC_DRAIN_LAST_BUFFER: return "DRAIN_LAST_BUFFER ";
+ case MSM_VIDC_DRC_LAST_BUFFER: return "DRC_LAST_BUFFER ";
+ case MSM_VIDC_INPUT_PAUSE: return "INPUT_PAUSE ";
+ case MSM_VIDC_OUTPUT_PAUSE: return "OUTPUT_PAUSE ";
+ }
+
+ return "SUB_STATE_NONE";
+}
+
+static int prepare_sub_state_name(enum msm_vidc_sub_state sub_state,
+ char *buf, u32 size)
+{
+ int i = 0;
+
+ if (!buf || !size)
+ return -EINVAL;
+
+ strscpy(buf, "\0", size);
+ if (sub_state == MSM_VIDC_SUB_STATE_NONE) {
+ strscpy(buf, "SUB_STATE_NONE", size);
+ return 0;
+ }
+
+ for (i = 0; i < MSM_VIDC_MAX_SUB_STATES; i++) {
+ if (sub_state & BIT(i))
+ strlcat(buf, sub_state_name(BIT(i)), size);
+ }
+
+ return 0;
+}
+
+struct msm_vidc_state_allow {
+ enum msm_vidc_state from;
+ enum msm_vidc_state to;
+ enum msm_vidc_allow allow;
+};
+
+static enum msm_vidc_allow msm_vidc_allow_state_change(struct msm_vidc_inst *inst,
+ enum msm_vidc_state req_state)
+{
+ int cnt;
+ enum msm_vidc_allow allow = MSM_VIDC_DISALLOW;
+ static struct msm_vidc_state_allow state[] = {
+ /* from, to, allow */
+ {MSM_VIDC_OPEN, MSM_VIDC_OPEN, MSM_VIDC_IGNORE },
+ {MSM_VIDC_OPEN, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_ALLOW },
+ {MSM_VIDC_OPEN, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_ALLOW },
+ {MSM_VIDC_OPEN, MSM_VIDC_STREAMING, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_OPEN, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
+ {MSM_VIDC_OPEN, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
+
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_OPEN, MSM_VIDC_ALLOW },
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_IGNORE },
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_STREAMING, MSM_VIDC_ALLOW },
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
+
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_OPEN, MSM_VIDC_ALLOW },
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_IGNORE },
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_STREAMING, MSM_VIDC_ALLOW },
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
+
+ {MSM_VIDC_STREAMING, MSM_VIDC_OPEN, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_STREAMING, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_ALLOW },
+ {MSM_VIDC_STREAMING, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_ALLOW },
+ {MSM_VIDC_STREAMING, MSM_VIDC_STREAMING, MSM_VIDC_IGNORE },
+ {MSM_VIDC_STREAMING, MSM_VIDC_CLOSE, MSM_VIDC_ALLOW },
+ {MSM_VIDC_STREAMING, MSM_VIDC_ERROR, MSM_VIDC_ALLOW },
+
+ {MSM_VIDC_CLOSE, MSM_VIDC_OPEN, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_CLOSE, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_CLOSE, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_CLOSE, MSM_VIDC_STREAMING, MSM_VIDC_DISALLOW },
+ {MSM_VIDC_CLOSE, MSM_VIDC_CLOSE, MSM_VIDC_IGNORE },
+ {MSM_VIDC_CLOSE, MSM_VIDC_ERROR, MSM_VIDC_IGNORE },
+
+ {MSM_VIDC_ERROR, MSM_VIDC_OPEN, MSM_VIDC_IGNORE },
+ {MSM_VIDC_ERROR, MSM_VIDC_INPUT_STREAMING, MSM_VIDC_IGNORE },
+ {MSM_VIDC_ERROR, MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_IGNORE },
+ {MSM_VIDC_ERROR, MSM_VIDC_STREAMING, MSM_VIDC_IGNORE },
+ {MSM_VIDC_ERROR, MSM_VIDC_CLOSE, MSM_VIDC_IGNORE },
+ {MSM_VIDC_ERROR, MSM_VIDC_ERROR, MSM_VIDC_IGNORE },
+ };
+
+ for (cnt = 0; cnt < ARRAY_SIZE(state); cnt++) {
+ if (state[cnt].from == inst->state && state[cnt].to == req_state) {
+ allow = state[cnt].allow;
+ break;
+ }
+ }
+
+ return allow;
+}
+
+static int msm_vidc_open_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_event event, void *data)
+{
+ int rc = 0;
+
+ /* inst must be locked */
+ rc = __strict_inst_check(inst, __func__);
+ if (rc) {
+ i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case MSM_VIDC_TRY_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)data;
+
+ /* allow try_fmt request in open state */
+ rc = msm_vidc_try_fmt(inst, f);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_S_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)data;
+
+ /* allow s_fmt request in open state */
+ rc = msm_vidc_s_fmt(inst, f);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_S_CTRL:
+ {
+ struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
+
+ /* allow set_control request in open state */
+ rc = msm_vidc_s_ctrl(inst, ctrl);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *b = (struct v4l2_requestbuffers *)data;
+
+ /* allow reqbufs request in open state */
+ rc = msm_vidc_reqbufs(inst, b);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_STREAMON:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ /* allow streamon request in open state */
+ rc = msm_vidc_start_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_STREAMOFF:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ /* ignore streamoff request in open state */
+ i_vpr_h(inst, "%s: streamoff of (%s) ignored in state (%s)\n",
+ __func__, v4l2_type_name(q->type), state_name(inst->state));
+ break;
+ }
+ case MSM_VIDC_CMD_START:
+ {
+ /* disallow start cmd request in open state */
+ i_vpr_e(inst, "%s: (%s) not allowed, sub_state (%s)\n",
+ __func__, event_name(event), inst->sub_state_name);
+
+ return -EBUSY;
+ }
+ case MSM_VIDC_CMD_STOP:
+ {
+ /* ignore stop cmd request in open state */
+ i_vpr_h(inst, "%s: (%s) ignored, sub_state (%s)\n",
+ __func__, event_name(event), inst->sub_state_name);
+ break;
+ }
+ case MSM_VIDC_BUF_QUEUE:
+ {
+ struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
+
+ /* defer qbuf request in open state */
+ print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
+ break;
+ }
+ default:
+ {
+ i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_input_streaming_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_event event, void *data)
+{
+ int rc = 0;
+
+ /* inst must be locked */
+ rc = __strict_inst_check(inst, __func__);
+ if (rc) {
+ i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case MSM_VIDC_BUF_QUEUE:
+ {
+ struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
+
+ /* disallow */
+ if (!is_input_buffer(buf->type) && !is_output_buffer(buf->type)) {
+ i_vpr_e(inst, "%s: invalid buf type %u\n", __func__, buf->type);
+ return -EINVAL;
+ }
+
+ /* defer output port */
+ if (is_output_buffer(buf->type)) {
+ print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
+ return 0;
+ }
+
+ rc = msm_vidc_buf_queue(inst, buf);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_TRY_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)data;
+
+ /* disallow */
+ if (f->type == INPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) port\n",
+ __func__, event_name(event), v4l2_type_name(f->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_try_fmt(inst, f);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_S_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)data;
+
+ /* disallow */
+ if (f->type == INPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) port\n",
+ __func__, event_name(event), v4l2_type_name(f->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_s_fmt(inst, f);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_S_CTRL:
+ {
+ struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
+ u32 cap_id = msm_vidc_get_cap_id(inst, ctrl->id);
+
+ if (cap_id == INST_CAP_NONE) {
+ i_vpr_e(inst, "%s: invalid cap_id %u\n", __func__, cap_id);
+ return -EINVAL;
+ }
+
+ /* disallow */
+ if (is_decode_session(inst)) {
+ /* check dynamic allowed if master port is streaming */
+ if (!(inst->capabilities[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)) {
+ i_vpr_e(inst, "%s: cap_id %#x not allowed in state %s\n",
+ __func__, cap_id, state_name(inst->state));
+ return -EINVAL;
+ }
+ }
+
+ rc = msm_vidc_s_ctrl(inst, ctrl);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *b = (struct v4l2_requestbuffers *)data;
+
+ /* disallow */
+ if (b->type == INPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) port\n",
+ __func__, event_name(event), v4l2_type_name(b->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_reqbufs(inst, b);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_STREAMON:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ /* disallow */
+ if (q->type == INPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) type\n",
+ __func__, event_name(event), v4l2_type_name(q->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_start_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_STREAMOFF:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ /* ignore */
+ if (q->type == OUTPUT_MPLANE) {
+ i_vpr_h(inst, "%s: streamoff of (%s) ignored in state (%s)\n",
+ __func__, v4l2_type_name(q->type), state_name(inst->state));
+ return 0;
+ }
+
+ /* sanitize type field */
+ if (q->type != INPUT_MPLANE) {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
+ return -EINVAL;
+ }
+
+ rc = msm_vidc_stop_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_CMD_START:
+ {
+ /* disallow if START called for non DRC/drain cases */
+ if (!is_drc_pending(inst) && !is_drain_pending(inst)) {
+ i_vpr_e(inst, "%s: (%s) not allowed, sub_state (%s)\n",
+ __func__, event_name(event), inst->sub_state_name);
+ return -EBUSY;
+ }
+
+ /* client would call start(resume) to complete DRC/drain sequence */
+ rc = msm_vidc_start_cmd(inst);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_CMD_STOP:
+ {
+ /* back to back drain not allowed */
+ if (is_sub_state(inst, MSM_VIDC_DRAIN)) {
+ i_vpr_e(inst, "%s: drain (%s) not allowed, sub_state (%s)\n\n",
+ __func__, event_name(event), inst->sub_state_name);
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_stop_cmd(inst);
+ if (rc)
+ return rc;
+ break;
+ }
+ default:
+ {
+ i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_output_streaming_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_event event, void *data)
+{
+ int rc = 0;
+
+ /* inst must be locked */
+ rc = __strict_inst_check(inst, __func__);
+ if (rc) {
+ i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case MSM_VIDC_BUF_QUEUE:
+ {
+ struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
+
+ /* disallow */
+ if (!is_input_buffer(buf->type) && !is_output_buffer(buf->type)) {
+ i_vpr_e(inst, "%s: invalid buf type %u\n", __func__, buf->type);
+ return -EINVAL;
+ }
+
+ /* defer input port */
+ if (is_input_buffer(buf->type)) {
+ print_vidc_buffer(VIDC_LOW, "low ", "qbuf deferred", inst, buf);
+ return 0;
+ }
+
+ rc = msm_vidc_buf_queue(inst, buf);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_TRY_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)data;
+
+ /* disallow */
+ if (f->type == OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) port\n",
+ __func__, event_name(event), v4l2_type_name(f->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_try_fmt(inst, f);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_S_FMT:
+ {
+ struct v4l2_format *f = (struct v4l2_format *)data;
+
+ /* disallow */
+ if (f->type == OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) port\n",
+ __func__, event_name(event), v4l2_type_name(f->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_s_fmt(inst, f);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_S_CTRL:
+ {
+ struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
+ u32 cap_id = msm_vidc_get_cap_id(inst, ctrl->id);
+
+ if (cap_id == INST_CAP_NONE) {
+ i_vpr_e(inst, "%s: invalid cap_id %u\n", __func__, cap_id);
+ return -EINVAL;
+ }
+
+ /* disallow */
+ if (is_encode_session(inst)) {
+ /* check dynamic allowed if master port is streaming */
+ if (!(inst->capabilities[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)) {
+ i_vpr_e(inst, "%s: cap_id %#x not allowed in state %s\n",
+ __func__, cap_id, state_name(inst->state));
+ return -EINVAL;
+ }
+ }
+
+ rc = msm_vidc_s_ctrl(inst, ctrl);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *b = (struct v4l2_requestbuffers *)data;
+
+ /* disallow */
+ if (b->type == OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) port\n",
+ __func__, event_name(event), v4l2_type_name(b->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_reqbufs(inst, b);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_STREAMON:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ /* disallow */
+ if (q->type == OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: (%s) not allowed for (%s) type\n",
+ __func__, event_name(event), v4l2_type_name(q->type));
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_start_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_STREAMOFF:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ /* ignore */
+ if (q->type == INPUT_MPLANE) {
+ i_vpr_h(inst, "%s: streamoff of (%s) ignored in state (%s)\n",
+ __func__, v4l2_type_name(q->type), state_name(inst->state));
+ return 0;
+ }
+
+ /* sanitize type field */
+ if (q->type != OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
+ return -EINVAL;
+ }
+
+ rc = msm_vidc_stop_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_CMD_START:
+ {
+ /* disallow if START called for non DRC/drain cases */
+ if (!is_drc_pending(inst) && !is_drain_pending(inst)) {
+ i_vpr_e(inst, "%s: (%s) not allowed, sub_state (%s)\n",
+ __func__, event_name(event), inst->sub_state_name);
+ return -EBUSY;
+ }
+
+ /* client would call start(resume) to complete DRC/drain sequence */
+ rc = msm_vidc_start_cmd(inst);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_CMD_STOP:
+ {
+ /* drain not allowed as input is not streaming */
+ i_vpr_e(inst, "%s: drain (%s) not allowed, sub state %s\n",
+ __func__, event_name(event), inst->sub_state_name);
+ return -EBUSY;
+ }
+ default: {
+ i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_streaming_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_event event, void *data)
+{
+ int rc = 0;
+
+ /* inst must be locked */
+ rc = __strict_inst_check(inst, __func__);
+ if (rc) {
+ i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case MSM_VIDC_BUF_QUEUE:
+ {
+ struct msm_vidc_buffer *buf = (struct msm_vidc_buffer *)data;
+
+ /* disallow */
+ if (!is_input_buffer(buf->type) && !is_output_buffer(buf->type)) {
+ i_vpr_e(inst, "%s: invalid buf type %u\n", __func__, buf->type);
+ return -EINVAL;
+ }
+
+ rc = msm_vidc_buf_queue(inst, buf);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_S_CTRL:
+ {
+ struct v4l2_ctrl *ctrl = (struct v4l2_ctrl *)data;
+ u32 cap_id = msm_vidc_get_cap_id(inst, ctrl->id);
+
+ if (cap_id == INST_CAP_NONE) {
+ i_vpr_e(inst, "%s: invalid cap_id %u\n", __func__, cap_id);
+ return -EINVAL;
+ }
+
+ /* disallow */
+ if (!(inst->capabilities[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)) {
+ i_vpr_e(inst, "%s: cap_id %#x not allowed in state %s\n",
+ __func__, cap_id, state_name(inst->state));
+ return -EINVAL;
+ }
+
+ rc = msm_vidc_s_ctrl(inst, ctrl);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_STREAMOFF:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ /* sanitize type field */
+ if (q->type != INPUT_MPLANE && q->type != OUTPUT_MPLANE) {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, q->type);
+ return -EINVAL;
+ }
+
+ rc = msm_vidc_stop_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_CMD_START:
+ {
+ /* disallow if START called for non DRC/drain cases */
+ if (!is_drc_pending(inst) && !is_drain_pending(inst)) {
+ i_vpr_e(inst, "%s: (%s) not allowed, sub_state (%s)\n",
+ __func__, event_name(event), inst->sub_state_name);
+ return -EBUSY;
+ }
+
+ /* client would call start(resume) to complete DRC/drain sequence */
+ rc = msm_vidc_start_cmd(inst);
+ if (rc)
+ return rc;
+ break;
+ }
+ case MSM_VIDC_CMD_STOP:
+ {
+ /* back to back drain not allowed */
+ if (is_sub_state(inst, MSM_VIDC_DRAIN)) {
+ i_vpr_e(inst, "%s: drain (%s) not allowed, sub_state (%s)\n\n",
+ __func__, event_name(event), inst->sub_state_name);
+ return -EBUSY;
+ }
+
+ rc = msm_vidc_stop_cmd(inst);
+ if (rc)
+ return rc;
+ break;
+ }
+ default: {
+ i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_close_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_event event, void *data)
+{
+ int rc = 0;
+
+ /* inst must be locked */
+ rc = __strict_inst_check(inst, __func__);
+ if (rc) {
+ i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case MSM_VIDC_STREAMOFF:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ rc = msm_vidc_stop_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ default: {
+ i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+static int msm_vidc_error_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_event event, void *data)
+{
+ int rc = 0;
+
+ /* inst must be locked */
+ rc = __strict_inst_check(inst, __func__);
+ if (rc) {
+ i_vpr_e(inst, "%s(): inst was not locked\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case MSM_VIDC_STREAMOFF:
+ {
+ struct vb2_queue *q = (struct vb2_queue *)data;
+
+ rc = msm_vidc_stop_streaming(inst, q);
+ if (rc)
+ return rc;
+ break;
+ }
+ default: {
+ i_vpr_e(inst, "%s: unexpected event %s\n", __func__, event_name(event));
+ return -EINVAL;
+ }
+ }
+
+ return rc;
+}
+
+struct msm_vidc_state_handle {
+ enum msm_vidc_state state;
+ int (*handle)(struct msm_vidc_inst *inst,
+ enum msm_vidc_event event, void *data);
+};
+
+static struct msm_vidc_state_handle *msm_vidc_get_state_handle(struct msm_vidc_inst *inst,
+ enum msm_vidc_state req_state)
+{
+ int cnt;
+ struct msm_vidc_state_handle *inst_state_handle = NULL;
+ static struct msm_vidc_state_handle state_handle[] = {
+ {MSM_VIDC_OPEN, msm_vidc_open_state },
+ {MSM_VIDC_INPUT_STREAMING, msm_vidc_input_streaming_state },
+ {MSM_VIDC_OUTPUT_STREAMING, msm_vidc_output_streaming_state },
+ {MSM_VIDC_STREAMING, msm_vidc_streaming_state },
+ {MSM_VIDC_CLOSE, msm_vidc_close_state },
+ {MSM_VIDC_ERROR, msm_vidc_error_state },
+ };
+
+ for (cnt = 0; cnt < ARRAY_SIZE(state_handle); cnt++) {
+ if (state_handle[cnt].state == req_state) {
+ inst_state_handle = &state_handle[cnt];
+ break;
+ }
+ }
+
+ /* check if req_state does not exist in the table */
+ if (cnt == ARRAY_SIZE(state_handle)) {
+ i_vpr_e(inst, "%s: invalid state %s\n", __func__, state_name(req_state));
+ return inst_state_handle;
+ }
+
+ return inst_state_handle;
+}
+
+int msm_vidc_update_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_state request_state, const char *func)
+{
+ struct msm_vidc_state_handle *state_handle = NULL;
+ int rc = 0;
+
+ /* get inst state handler for requested state */
+ state_handle = msm_vidc_get_state_handle(inst, request_state);
+ if (!state_handle)
+ return -EINVAL;
+
+ if (request_state == MSM_VIDC_ERROR)
+ i_vpr_e(inst, FMT_STRING_STATE_CHANGE,
+ func, state_name(request_state), state_name(inst->state));
+ else
+ i_vpr_h(inst, FMT_STRING_STATE_CHANGE,
+ func, state_name(request_state), state_name(inst->state));
+
+ /* finally update inst state and handler */
+ inst->state = state_handle->state;
+ inst->event_handle = state_handle->handle;
+
+ return rc;
+}
+
+int msm_vidc_change_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_state request_state, const char *func)
+{
+ enum msm_vidc_allow allow;
+ int rc;
+
+ if (is_session_error(inst)) {
+ i_vpr_h(inst,
+ "%s: inst is in bad state, can not change state to %s\n",
+ func, state_name(request_state));
+ return 0;
+ }
+
+ /* current and requested state is same */
+ if (inst->state == request_state)
+ return 0;
+
+ /* check if requested state movement is allowed */
+ allow = msm_vidc_allow_state_change(inst, request_state);
+ if (allow != MSM_VIDC_ALLOW) {
+ i_vpr_e(inst, "%s: %s state change %s -> %s\n", func,
+ allow_name(allow), state_name(inst->state),
+ state_name(request_state));
+ return (allow == MSM_VIDC_DISALLOW ? -EINVAL : 0);
+ }
+
+ /* go ahead and update inst state */
+ rc = msm_vidc_update_state(inst, request_state, func);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+struct msm_vidc_sub_state_allow {
+ enum msm_vidc_state state;
+ enum msm_vidc_allow allow;
+ u32 sub_state_mask;
+};
+
+static int msm_vidc_set_sub_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_sub_state sub_state, const char *func)
+{
+ char sub_state_name[MAX_NAME_LENGTH];
+ int cnt, rc = 0;
+ static struct msm_vidc_sub_state_allow sub_state_allow[] = {
+ /* state, allow, sub_state */
+ {MSM_VIDC_OPEN, MSM_VIDC_DISALLOW, MSM_VIDC_DRC |
+ MSM_VIDC_DRAIN |
+ MSM_VIDC_DRC_LAST_BUFFER |
+ MSM_VIDC_DRAIN_LAST_BUFFER |
+ MSM_VIDC_INPUT_PAUSE |
+ MSM_VIDC_OUTPUT_PAUSE },
+
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_DISALLOW, MSM_VIDC_DRC_LAST_BUFFER |
+ MSM_VIDC_DRAIN_LAST_BUFFER |
+ MSM_VIDC_OUTPUT_PAUSE },
+ {MSM_VIDC_INPUT_STREAMING, MSM_VIDC_ALLOW, MSM_VIDC_DRC |
+ MSM_VIDC_DRAIN |
+ MSM_VIDC_INPUT_PAUSE },
+
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_DISALLOW, MSM_VIDC_DRC |
+ MSM_VIDC_DRAIN |
+ MSM_VIDC_INPUT_PAUSE },
+ {MSM_VIDC_OUTPUT_STREAMING, MSM_VIDC_ALLOW, MSM_VIDC_DRC_LAST_BUFFER |
+ MSM_VIDC_DRAIN_LAST_BUFFER |
+ MSM_VIDC_OUTPUT_PAUSE },
+
+ {MSM_VIDC_STREAMING, MSM_VIDC_ALLOW, MSM_VIDC_DRC |
+ MSM_VIDC_DRAIN |
+ MSM_VIDC_DRC_LAST_BUFFER |
+ MSM_VIDC_DRAIN_LAST_BUFFER |
+ MSM_VIDC_INPUT_PAUSE |
+ MSM_VIDC_OUTPUT_PAUSE },
+
+ {MSM_VIDC_CLOSE, MSM_VIDC_ALLOW, MSM_VIDC_DRC |
+ MSM_VIDC_DRAIN |
+ MSM_VIDC_DRC_LAST_BUFFER |
+ MSM_VIDC_DRAIN_LAST_BUFFER |
+ MSM_VIDC_INPUT_PAUSE |
+ MSM_VIDC_OUTPUT_PAUSE },
+
+ {MSM_VIDC_ERROR, MSM_VIDC_ALLOW, MSM_VIDC_DRC |
+ MSM_VIDC_DRAIN |
+ MSM_VIDC_DRC_LAST_BUFFER |
+ MSM_VIDC_DRAIN_LAST_BUFFER |
+ MSM_VIDC_INPUT_PAUSE |
+ MSM_VIDC_OUTPUT_PAUSE },
+ };
+
+ /* no substate to update */
+ if (!sub_state)
+ return 0;
+
+ /* check if any substate is disallowed */
+ for (cnt = 0; cnt < ARRAY_SIZE(sub_state_allow); cnt++) {
+ /* skip other states */
+ if (sub_state_allow[cnt].state != inst->state)
+ continue;
+
+ /* continue if not disallowed */
+ if (sub_state_allow[cnt].allow != MSM_VIDC_DISALLOW)
+ continue;
+
+ if (sub_state_allow[cnt].sub_state_mask & sub_state) {
+ prepare_sub_state_name(sub_state, sub_state_name, sizeof(sub_state_name));
+ i_vpr_e(inst, "%s: state (%s), disallow substate (%s)\n",
+ func, state_name(inst->state), sub_state_name);
+ return -EINVAL;
+ }
+ }
+
+ /* remove ignorable substates from a given substate */
+ for (cnt = 0; cnt < ARRAY_SIZE(sub_state_allow); cnt++) {
+ /* skip other states */
+ if (sub_state_allow[cnt].state != inst->state)
+ continue;
+
+ /* continue if not ignored */
+ if (sub_state_allow[cnt].allow != MSM_VIDC_IGNORE)
+ continue;
+
+ if (sub_state_allow[cnt].sub_state_mask & sub_state) {
+ prepare_sub_state_name(sub_state, sub_state_name, sizeof(sub_state_name));
+ i_vpr_h(inst, "%s: state (%s), ignore substate (%s)\n",
+ func, state_name(inst->state), sub_state_name);
+
+ /* remove ignorable substate bits from actual */
+ sub_state &= ~(sub_state_allow[cnt].sub_state_mask & sub_state);
+ break;
+ }
+ }
+
+ /* check if all substate bits are allowed */
+ for (cnt = 0; cnt < ARRAY_SIZE(sub_state_allow); cnt++) {
+ /* skip other states */
+ if (sub_state_allow[cnt].state != inst->state)
+ continue;
+
+ /* continue if not allowed */
+ if (sub_state_allow[cnt].allow != MSM_VIDC_ALLOW)
+ continue;
+
+ if ((sub_state_allow[cnt].sub_state_mask & sub_state) != sub_state) {
+ prepare_sub_state_name(sub_state, sub_state_name, sizeof(sub_state_name));
+ i_vpr_e(inst, "%s: state (%s), not all substates allowed (%s)\n",
+ func, state_name(inst->state), sub_state_name);
+ return -EINVAL;
+ }
+ }
+
+ /* update substate */
+ inst->sub_state |= sub_state;
+
+ return rc;
+}
+
+int msm_vidc_change_sub_state(struct msm_vidc_inst *inst,
+ enum msm_vidc_sub_state clear_sub_state,
+ enum msm_vidc_sub_state set_sub_state, const char *func)
+{
+ enum msm_vidc_sub_state prev_sub_state;
+ int rc = 0;
+
+ if (is_session_error(inst)) {
+ i_vpr_h(inst,
+ "%s: inst is in bad state, can not change sub state\n", func);
+ return 0;
+ }
+
+ /* final value will not change */
+ if (!clear_sub_state && !set_sub_state)
+ return 0;
+
+ /* sanitize clear & set value */
+ if ((clear_sub_state & set_sub_state) ||
+ set_sub_state > MSM_VIDC_MAX_SUB_STATE_VALUE ||
+ clear_sub_state > MSM_VIDC_MAX_SUB_STATE_VALUE) {
+ i_vpr_e(inst, "%s: invalid sub states to clear %#x or set %#x\n",
+ func, clear_sub_state, set_sub_state);
+ return -EINVAL;
+ }
+
+ prev_sub_state = inst->sub_state;
+
+ /* set sub state */
+ rc = msm_vidc_set_sub_state(inst, set_sub_state, __func__);
+ if (rc)
+ return rc;
+
+ /* clear sub state */
+ inst->sub_state &= ~clear_sub_state;
+
+ /* print substates only when there is a change */
+ if (inst->sub_state != prev_sub_state) {
+ rc = prepare_sub_state_name(inst->sub_state, inst->sub_state_name,
+ sizeof(inst->sub_state_name));
+ if (!rc)
+ i_vpr_h(inst, "%s: state %s and sub state changed to %s\n",
+ func, state_name(inst->state), inst->sub_state_name);
+ }
+
+ return 0;
+}
--
2.7.4


2023-07-28 15:59:44

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH 00/33] Qualcomm video decoder/encoder driver

On 28/07/2023 16:23, Vikash Garodia wrote:
> This patch series introduces support for Qualcomm new video acceleration
> hardware architecture, used for video stream decoding/encoding. This driver
> is based on new communication protocol between video hardware and application
> processor.
>
> This driver comes with below capabilities:
> - V4L2 complaint video driver with M2M and STREAMING capability.
> - Supports H264, H265, VP9 decoders.
> - Supports H264, H265 encoders.
>
> This driver comes with below features:
> - Centralized resource and memory management.
> - Centralized management of core and instance states.
> - Defines platform specific capabilities and features. As a results, it provides
> a single point of control to enable/disable a given feature depending on
> specific platform capabilities.
> - Handles hardware interdependent configurations. For a given configuration from
> client, the driver checks for hardware dependent configuration/s and updates
> the same.
> - Handles multiple complex video scenarios involving state transitions - DRC,
> Drain, Seek, back to back DRC, DRC during Drain sequence, DRC during Seek
> sequence.
> - Introduces a flexible way for driver to subscribe for any property with
> hardware. Hardware would inform driver with those subscribed property with any
> change in value.
> - Introduces performance (clock and bus) model based on new hardware
> architecture.
> - Introduces multi thread safe design to handle communication between client and
> hardware.
> - Adapts encoder quality improvements available in new hardware architecture.
> - Implements asynchronous communication with hardware to achieve better
> experience in low latency usecases.
> - Supports multi stage hardware architecture for encode/decode.
> - Output and capture planes are controlled independently. Thereby providing a
> way to reconfigure individual plane.
> - Hardware packetization layer supports synchronization between configuration
> packet and data packet.
> - Introduces a flexibility to receive a hardware response for a given command
> packet.
> - Native hardware support of LAST flag which is mandatory to align with port
> reconfiguration and DRAIN sequence as per V4L guidelines.
> - Native hardware support for drain sequence.
>
> I think that the driver is in good shape for mainline kernel, and I hope the
> review comments will help to improve it, so please do review, and make comments.

No bindings, no driver. Please post start the series from the bindings.

>
> Dikshita Agarwal (17):
> iris: vidc: add core functions
> iris: add vidc wrapper file
> iris: vidc: add vb2 ops
> iris: vidc: add helpers for memory management
> iris: vidc: add helper functions for resource management
> iris: vidc: add helper functions for power management
> iris: add helpers for media format
> iris: vidc: add PIL functionality for video firmware
> iris: platform: add platform files
> iris: platform: sm8550: add capability file for sm8550
> iris: variant: add helper functions for register handling
> iris: variant: iris3: add iris3 specific ops
> iris: variant: iris3: add helpers for buffer size calculations
> iris: variant: iris3: add helper for bus and clock calculation
> iris: variant: iris: implement the logic to compute bus bandwidth
> iris: variant: iris3: implement logic to compute clock frequency
> iris: enable building of iris video driver
>
> Vikash Garodia (16):
> MAINTAINERS: Add Qualcomm Iris video accelerator driver
> iris: vidc: add v4l2 wrapper file
> iris: vidc: define video core and instance context
> iris: iris: add video encoder files
> iris: vidc: add video decoder files
> iris: vidc: add control files
> iris: vidc: add helper functions
> iris: vidc: add helpers for state management
> iris: add vidc buffer files
> iris: vidc: define various structures and enum
> iris: vidc: hfi: add Host Firmware Interface (HFI)
> iris: vidc: hfi: add Host Firmware Interface (HFI) response handling
> iris: vidc: hfi: add helpers for handling shared queues
> iris: vidc: hfi: Add packetization layer
> iris: vidc: hfi: defines HFI properties and enums
> iris: vidc: add debug files
>
> MAINTAINERS | 10 +
> drivers/media/platform/qcom/Kconfig | 1 +
> drivers/media/platform/qcom/Makefile | 1 +
> drivers/media/platform/qcom/iris/Kconfig | 15 +
> drivers/media/platform/qcom/iris/Makefile | 46 +
> .../iris/platform/common/inc/msm_vidc_platform.h | 305 ++
> .../iris/platform/common/src/msm_vidc_platform.c | 2499 ++++++++++++
> .../iris/platform/sm8550/inc/msm_vidc_sm8550.h | 14 +
> .../iris/platform/sm8550/src/msm_vidc_sm8550.c | 1727 ++++++++
> .../iris/variant/common/inc/msm_vidc_variant.h | 22 +
> .../iris/variant/common/src/msm_vidc_variant.c | 163 +
> .../qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h | 1481 +++++++
> .../iris/variant/iris3/inc/msm_vidc_buffer_iris3.h | 19 +
> .../qcom/iris/variant/iris3/inc/msm_vidc_iris3.h | 15 +
> .../iris/variant/iris3/inc/msm_vidc_power_iris3.h | 17 +
> .../iris/variant/iris3/inc/perf_static_model.h | 229 ++
> .../iris/variant/iris3/src/msm_vidc_buffer_iris3.c | 595 +++
> .../iris/variant/iris3/src/msm_vidc_bus_iris3.c | 884 ++++
> .../iris/variant/iris3/src/msm_vidc_clock_iris3.c | 627 +++
> .../qcom/iris/variant/iris3/src/msm_vidc_iris3.c | 954 +++++
> .../iris/variant/iris3/src/msm_vidc_power_iris3.c | 345 ++
> .../media/platform/qcom/iris/vidc/inc/firmware.h | 18 +
> .../platform/qcom/iris/vidc/inc/hfi_command.h | 190 +
> .../media/platform/qcom/iris/vidc/inc/hfi_packet.h | 52 +
> .../platform/qcom/iris/vidc/inc/hfi_property.h | 666 +++
> .../platform/qcom/iris/vidc/inc/msm_media_info.h | 599 +++
> .../media/platform/qcom/iris/vidc/inc/msm_vdec.h | 40 +
> .../media/platform/qcom/iris/vidc/inc/msm_venc.h | 34 +
> .../media/platform/qcom/iris/vidc/inc/msm_vidc.h | 60 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_buffer.h | 32 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_control.h | 26 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_core.h | 165 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_debug.h | 186 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_driver.h | 352 ++
> .../platform/qcom/iris/vidc/inc/msm_vidc_inst.h | 207 +
> .../qcom/iris/vidc/inc/msm_vidc_internal.h | 787 ++++
> .../platform/qcom/iris/vidc/inc/msm_vidc_memory.h | 83 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_power.h | 94 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_state.h | 102 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h | 77 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_vb2.h | 39 +
> .../media/platform/qcom/iris/vidc/inc/resources.h | 259 ++
> .../media/platform/qcom/iris/vidc/inc/venus_hfi.h | 66 +
> .../platform/qcom/iris/vidc/inc/venus_hfi_queue.h | 89 +
> .../qcom/iris/vidc/inc/venus_hfi_response.h | 26 +
> .../media/platform/qcom/iris/vidc/src/firmware.c | 294 ++
> .../media/platform/qcom/iris/vidc/src/hfi_packet.c | 657 +++
> .../media/platform/qcom/iris/vidc/src/msm_vdec.c | 2091 ++++++++++
> .../media/platform/qcom/iris/vidc/src/msm_venc.c | 1484 +++++++
> .../media/platform/qcom/iris/vidc/src/msm_vidc.c | 841 ++++
> .../platform/qcom/iris/vidc/src/msm_vidc_buffer.c | 290 ++
> .../platform/qcom/iris/vidc/src/msm_vidc_control.c | 824 ++++
> .../platform/qcom/iris/vidc/src/msm_vidc_debug.c | 581 +++
> .../platform/qcom/iris/vidc/src/msm_vidc_driver.c | 4276 ++++++++++++++++++++
> .../platform/qcom/iris/vidc/src/msm_vidc_memory.c | 448 ++
> .../platform/qcom/iris/vidc/src/msm_vidc_power.c | 560 +++
> .../platform/qcom/iris/vidc/src/msm_vidc_probe.c | 660 +++
> .../platform/qcom/iris/vidc/src/msm_vidc_state.c | 1607 ++++++++
> .../platform/qcom/iris/vidc/src/msm_vidc_v4l2.c | 953 +++++
> .../platform/qcom/iris/vidc/src/msm_vidc_vb2.c | 605 +++
> .../media/platform/qcom/iris/vidc/src/resources.c | 1321 ++++++
> .../media/platform/qcom/iris/vidc/src/venus_hfi.c | 1503 +++++++
> .../platform/qcom/iris/vidc/src/venus_hfi_queue.c | 537 +++
> .../qcom/iris/vidc/src/venus_hfi_response.c | 1607 ++++++++
> 64 files changed, 35357 insertions(+)
> create mode 100644 drivers/media/platform/qcom/iris/Kconfig
> create mode 100644 drivers/media/platform/qcom/iris/Makefile
> create mode 100644 drivers/media/platform/qcom/iris/platform/common/inc/msm_vidc_platform.h
> create mode 100644 drivers/media/platform/qcom/iris/platform/common/src/msm_vidc_platform.c
> create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/inc/msm_vidc_sm8550.h
> create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/src/msm_vidc_sm8550.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_buffer_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/perf_static_model.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_buffer_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_bus_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_clock_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/firmware.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_command.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_packet.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_property.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_media_info.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vdec.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_buffer.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_control.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_core.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_driver.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_inst.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_memory.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_power.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/resources.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_queue.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/firmware.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/hfi_packet.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vdec.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_venc.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_buffer.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_control.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_driver.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_memory.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_power.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_probe.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_v4l2.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/resources.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_queue.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c
>

--
With best wishes
Dmitry


2023-07-28 16:00:59

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 24/33] iris: vidc: add debug files

this implements the debugging framework.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../platform/qcom/iris/vidc/inc/msm_vidc_debug.h | 186 +++++++
.../platform/qcom/iris/vidc/src/msm_vidc_debug.c | 581 +++++++++++++++++++++
2 files changed, 767 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c

diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
new file mode 100644
index 0000000..ffced01
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __MSM_VIDC_DEBUG__
+#define __MSM_VIDC_DEBUG__
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+
+struct msm_vidc_core;
+struct msm_vidc_inst;
+
+#ifndef VIDC_DBG_LABEL
+#define VIDC_DBG_LABEL "msm_vidc"
+#endif
+
+/* Allow only 6 prints/sec */
+#define VIDC_DBG_SESSION_RATELIMIT_INTERVAL (1 * HZ)
+#define VIDC_DBG_SESSION_RATELIMIT_BURST 6
+
+#define VIDC_DBG_TAG_INST VIDC_DBG_LABEL ": %4s: %s: "
+#define VIDC_DBG_TAG_CORE VIDC_DBG_LABEL ": %4s: %08x: %s: "
+#define FW_DBG_TAG VIDC_DBG_LABEL ": %6s: "
+#define DEFAULT_SID ((u32)-1)
+
+#ifndef MSM_VIDC_EMPTY_BRACE
+#define MSM_VIDC_EMPTY_BRACE {},
+#endif
+
+extern unsigned int msm_vidc_debug;
+extern unsigned int msm_fw_debug;
+extern bool msm_vidc_fw_dump;
+
+/* do not modify the log message as it is used in test scripts */
+#define FMT_STRING_SET_CTRL \
+ "%s: state %s, name %s, id 0x%x value %d\n"
+#define FMT_STRING_STATE_CHANGE \
+ "%s: state changed to %s from %s\n"
+#define FMT_STRING_MSG_SFR \
+ "SFR Message from FW: %s\n"
+#define FMT_STRING_FAULT_HANDLER \
+ "%s: faulting address: %lx\n"
+#define FMT_STRING_SET_CAP \
+ "set cap: name: %24s, cap value: %#10x, hfi: %#10llx\n"
+
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set msm_vidc_debug = 0x101F
+ */
+
+enum vidc_msg_prio_drv {
+ VIDC_ERR = 0x00000001,
+ VIDC_HIGH = 0x00000002,
+ VIDC_LOW = 0x00000004,
+ VIDC_PERF = 0x00000008,
+ VIDC_PKT = 0x00000010,
+ VIDC_BUS = 0x00000020,
+ VIDC_STAT = 0x00000040,
+ VIDC_ENCODER = 0x00000100,
+ VIDC_DECODER = 0x00000200,
+ VIDC_PRINTK = 0x10000000,
+ VIDC_FTRACE = 0x20000000,
+};
+
+enum vidc_msg_prio_fw {
+ FW_LOW = 0x00000001,
+ FW_MED = 0x00000002,
+ FW_HIGH = 0x00000004,
+ FW_ERROR = 0x00000008,
+ FW_FATAL = 0x00000010,
+ FW_PERF = 0x00000020,
+ FW_CACHE_LOW = 0x00000100,
+ FW_CACHE_MED = 0x00000200,
+ FW_CACHE_HIGH = 0x00000400,
+ FW_CACHE_ERROR = 0x00000800,
+ FW_CACHE_FATAL = 0x00001000,
+ FW_CACHE_PERF = 0x00002000,
+ FW_PRINTK = 0x10000000,
+ FW_FTRACE = 0x20000000,
+};
+
+#define DRV_LOG (VIDC_ERR | VIDC_PRINTK)
+#define DRV_LOGSHIFT (0)
+#define DRV_LOGMASK (0x0FFFFFFF)
+
+#define FW_LOG (FW_ERROR | FW_FATAL | FW_PRINTK)
+#define FW_LOGSHIFT (0)
+#define FW_LOGMASK (0x0FFFFFFF)
+
+#define dprintk_inst(__level, __level_str, inst, __fmt, ...) \
+ do { \
+ if (inst && (msm_vidc_debug & (__level))) { \
+ pr_info(VIDC_DBG_TAG_INST __fmt, \
+ __level_str, \
+ inst->debug_str, \
+ ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define i_vpr_e(inst, __fmt, ...) dprintk_inst(VIDC_ERR, "err ", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_i(inst, __fmt, ...) dprintk_inst(VIDC_HIGH, "high", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_h(inst, __fmt, ...) dprintk_inst(VIDC_HIGH, "high", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_l(inst, __fmt, ...) dprintk_inst(VIDC_LOW, "low ", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_p(inst, __fmt, ...) dprintk_inst(VIDC_PERF, "perf", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_t(inst, __fmt, ...) dprintk_inst(VIDC_PKT, "pkt ", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_b(inst, __fmt, ...) dprintk_inst(VIDC_BUS, "bus ", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_s(inst, __fmt, ...) dprintk_inst(VIDC_STAT, "stat", inst, __fmt, ##__VA_ARGS__)
+
+#define i_vpr_hp(inst, __fmt, ...) \
+ dprintk_inst(VIDC_HIGH | VIDC_PERF, "high", inst, __fmt, ##__VA_ARGS__)
+#define i_vpr_hs(inst, __fmt, ...) \
+ dprintk_inst(VIDC_HIGH | VIDC_STAT, "stat", inst, __fmt, ##__VA_ARGS__)
+
+#define dprintk_core(__level, __level_str, __fmt, ...) \
+ do { \
+ if (msm_vidc_debug & (__level)) { \
+ pr_info(VIDC_DBG_TAG_CORE __fmt, \
+ __level_str, \
+ DEFAULT_SID, \
+ "codec", \
+ ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define d_vpr_e(__fmt, ...) dprintk_core(VIDC_ERR, "err ", __fmt, ##__VA_ARGS__)
+#define d_vpr_h(__fmt, ...) dprintk_core(VIDC_HIGH, "high", __fmt, ##__VA_ARGS__)
+#define d_vpr_l(__fmt, ...) dprintk_core(VIDC_LOW, "low ", __fmt, ##__VA_ARGS__)
+#define d_vpr_p(__fmt, ...) dprintk_core(VIDC_PERF, "perf", __fmt, ##__VA_ARGS__)
+#define d_vpr_t(__fmt, ...) dprintk_core(VIDC_PKT, "pkt ", __fmt, ##__VA_ARGS__)
+#define d_vpr_b(__fmt, ...) dprintk_core(VIDC_BUS, "bus ", __fmt, ##__VA_ARGS__)
+#define d_vpr_s(__fmt, ...) dprintk_core(VIDC_STAT, "stat", __fmt, ##__VA_ARGS__)
+#define d_vpr_hs(__fmt, ...) \
+ dprintk_core(VIDC_HIGH | VIDC_STAT, "high", __fmt, ##__VA_ARGS__)
+
+#define dprintk_ratelimit(__level, __level_str, __fmt, ...) \
+ do { \
+ if (msm_vidc_check_ratelimit()) { \
+ dprintk_core(__level, __level_str, __fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define dprintk_firmware(__level, __fmt, ...) \
+ do { \
+ if ((msm_fw_debug & (__level)) & FW_PRINTK) { \
+ pr_info(FW_DBG_TAG __fmt, \
+ "fw", \
+ ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+enum msm_vidc_debugfs_event {
+ MSM_VIDC_DEBUGFS_EVENT_ETB,
+ MSM_VIDC_DEBUGFS_EVENT_EBD,
+ MSM_VIDC_DEBUGFS_EVENT_FTB,
+ MSM_VIDC_DEBUGFS_EVENT_FBD,
+};
+
+enum msm_vidc_bug_on_error {
+ MSM_VIDC_BUG_ON_FATAL = BIT(0),
+ MSM_VIDC_BUG_ON_NOC = BIT(1),
+ MSM_VIDC_BUG_ON_WD_TIMEOUT = BIT(2),
+};
+
+struct dentry *msm_vidc_debugfs_init_drv(void);
+struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core);
+struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
+ struct dentry *parent);
+void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst);
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+ enum msm_vidc_debugfs_event e);
+int msm_vidc_check_ratelimit(void);
+
+static inline bool is_stats_enabled(void)
+{
+ return !!(msm_vidc_debug & VIDC_STAT);
+}
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c
new file mode 100644
index 0000000..489e8dc
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "msm_vidc.h"
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_inst.h"
+#include "msm_vidc_internal.h"
+
+#define MAX_DEBUG_LEVEL_STRING_LEN 15
+#define MSM_VIDC_MIN_STATS_DELAY_MS 200
+#define MSM_VIDC_MAX_STATS_DELAY_MS 10000
+
+unsigned int msm_vidc_debug = DRV_LOG;
+unsigned int msm_fw_debug = FW_LOG;
+
+static int debug_level_set_drv(const char *val,
+ const struct kernel_param *kp)
+{
+ struct msm_vidc_core *core = NULL;
+ unsigned int dvalue;
+ int ret;
+
+ if (!kp || !kp->arg || !val) {
+ d_vpr_e("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(val, 0, &dvalue);
+ if (ret)
+ return ret;
+
+ msm_vidc_debug = dvalue;
+
+ core = *(struct msm_vidc_core **)kp->arg;
+
+ if (!core) {
+ d_vpr_e("%s: Invalid core/capabilities\n", __func__);
+ return 0;
+ }
+
+ /* check if driver is more than default level */
+ if ((dvalue & DRV_LOGMASK) & ~(DRV_LOG)) {
+ core->capabilities[HW_RESPONSE_TIMEOUT].value = 4 * HW_RESPONSE_TIMEOUT_VALUE;
+ core->capabilities[SW_PC_DELAY].value = 4 * SW_PC_DELAY_VALUE;
+ core->capabilities[FW_UNLOAD_DELAY].value = 4 * FW_UNLOAD_DELAY_VALUE;
+ } else {
+ /* reset timeout values, if user reduces the logging */
+ core->capabilities[HW_RESPONSE_TIMEOUT].value = HW_RESPONSE_TIMEOUT_VALUE;
+ core->capabilities[SW_PC_DELAY].value = SW_PC_DELAY_VALUE;
+ core->capabilities[FW_UNLOAD_DELAY].value = FW_UNLOAD_DELAY_VALUE;
+ }
+
+ d_vpr_h("timeout for driver: hw_response %u, sw_pc %u, fw_unload %u, debug_level %#x\n",
+ core->capabilities[HW_RESPONSE_TIMEOUT].value,
+ core->capabilities[SW_PC_DELAY].value,
+ core->capabilities[FW_UNLOAD_DELAY].value,
+ msm_vidc_debug);
+
+ return 0;
+}
+
+static int debug_level_set_fw(const char *val,
+ const struct kernel_param *kp)
+{
+ struct msm_vidc_core *core = NULL;
+ unsigned int dvalue;
+ int ret;
+
+ if (!kp || !kp->arg || !val) {
+ d_vpr_e("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(val, 0, &dvalue);
+ if (ret)
+ return ret;
+
+ msm_fw_debug = dvalue;
+
+ core = *(struct msm_vidc_core **)kp->arg;
+
+ if (!core) {
+ d_vpr_e("%s: Invalid core/capabilities\n", __func__);
+ return 0;
+ }
+
+ /* check if firmware is more than default level */
+ if ((dvalue & FW_LOGMASK) & ~(FW_LOG)) {
+ core->capabilities[HW_RESPONSE_TIMEOUT].value = 4 * HW_RESPONSE_TIMEOUT_VALUE;
+ core->capabilities[SW_PC_DELAY].value = 4 * SW_PC_DELAY_VALUE;
+ core->capabilities[FW_UNLOAD_DELAY].value = 4 * FW_UNLOAD_DELAY_VALUE;
+ } else {
+ /* reset timeout values, if user reduces the logging */
+ core->capabilities[HW_RESPONSE_TIMEOUT].value = HW_RESPONSE_TIMEOUT_VALUE;
+ core->capabilities[SW_PC_DELAY].value = SW_PC_DELAY_VALUE;
+ core->capabilities[FW_UNLOAD_DELAY].value = FW_UNLOAD_DELAY_VALUE;
+ }
+
+ d_vpr_h("timeout for firmware: hw_response %u, sw_pc %u, fw_unload %u, debug_level %#x\n",
+ core->capabilities[HW_RESPONSE_TIMEOUT].value,
+ core->capabilities[SW_PC_DELAY].value,
+ core->capabilities[FW_UNLOAD_DELAY].value,
+ msm_fw_debug);
+
+ return 0;
+}
+
+static int debug_level_get_drv(char *buffer, const struct kernel_param *kp)
+{
+ return scnprintf(buffer, PAGE_SIZE, "%#x", msm_vidc_debug);
+}
+
+static int debug_level_get_fw(char *buffer, const struct kernel_param *kp)
+{
+ return scnprintf(buffer, PAGE_SIZE, "%#x", msm_fw_debug);
+}
+
+static const struct kernel_param_ops msm_vidc_debug_fops = {
+ .set = debug_level_set_drv,
+ .get = debug_level_get_drv,
+};
+
+static const struct kernel_param_ops msm_fw_debug_fops = {
+ .set = debug_level_set_fw,
+ .get = debug_level_get_fw,
+};
+
+static int fw_dump_set(const char *val, const struct kernel_param *kp)
+{
+ unsigned int dvalue;
+ int ret;
+
+ if (!kp || !kp->arg || !val) {
+ d_vpr_e("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = kstrtouint(val, 0, &dvalue);
+ if (ret)
+ return ret;
+
+ msm_vidc_fw_dump = dvalue;
+
+ d_vpr_h("fw dump %s\n", msm_vidc_fw_dump ? "Enabled" : "Disabled");
+
+ return 0;
+}
+
+static int fw_dump_get(char *buffer, const struct kernel_param *kp)
+{
+ return scnprintf(buffer, PAGE_SIZE, "%#x", msm_vidc_fw_dump);
+}
+
+static const struct kernel_param_ops msm_vidc_fw_dump_fops = {
+ .set = fw_dump_set,
+ .get = fw_dump_get,
+};
+
+module_param_cb(msm_vidc_debug, &msm_vidc_debug_fops, &g_core, 0644);
+module_param_cb(msm_fw_debug, &msm_fw_debug_fops, &g_core, 0644);
+module_param_cb(msm_vidc_fw_dump, &msm_vidc_fw_dump_fops, &g_core, 0644);
+
+bool msm_vidc_fw_dump = !true;
+EXPORT_SYMBOL(msm_vidc_fw_dump);
+
+#define MAX_DBG_BUF_SIZE 4096
+
+struct core_inst_pair {
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst;
+};
+
+/* debug fs support */
+
+static u32 write_str(char *buffer, size_t size, const char *fmt, ...)
+{
+ va_list args;
+ u32 len;
+
+ va_start(args, fmt);
+ len = vscnprintf(buffer, size, fmt, args);
+ va_end(args);
+ return len;
+}
+
+static ssize_t core_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_vidc_core *core = file->private_data;
+ char *cur, *end, *dbuf = NULL;
+ ssize_t len = 0;
+
+ if (!core) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, core);
+ return 0;
+ }
+
+ dbuf = vzalloc(MAX_DBG_BUF_SIZE);
+ if (!dbuf) {
+ d_vpr_e("%s: allocation failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ cur = dbuf;
+ end = cur + MAX_DBG_BUF_SIZE;
+
+ cur += write_str(cur, end - cur, "Core state: %d\n", core->state);
+
+ cur += write_str(cur, end - cur,
+ "FW version : %s\n", core->fw_version);
+ cur += write_str(cur, end - cur,
+ "register_base: 0x%x\n", core->resource->register_base_addr);
+ cur += write_str(cur, end - cur, "irq: %u\n", core->resource->irq);
+
+ len = simple_read_from_buffer(buf, count, ppos, dbuf, cur - dbuf);
+
+ vfree(dbuf);
+ return len;
+}
+
+static const struct file_operations core_info_fops = {
+ .open = simple_open,
+ .read = core_info_read,
+};
+
+static ssize_t stats_delay_write_ms(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int rc = 0;
+ struct msm_vidc_core *core = filp->private_data;
+ char kbuf[MAX_DEBUG_LEVEL_STRING_LEN] = {0};
+ u32 delay_ms = 0;
+
+ if (!core) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, core);
+ return 0;
+ }
+
+ /* filter partial writes and invalid commands */
+ if (*ppos != 0 || count >= sizeof(kbuf) || count == 0) {
+ d_vpr_e("returning error - pos %lld, count %lu\n", *ppos, count);
+ rc = -EINVAL;
+ }
+
+ rc = simple_write_to_buffer(kbuf, sizeof(kbuf) - 1, ppos, buf, count);
+ if (rc < 0) {
+ d_vpr_e("%s: User memory fault\n", __func__);
+ rc = -EFAULT;
+ goto exit;
+ }
+
+ rc = kstrtoint(kbuf, 0, &delay_ms);
+ if (rc) {
+ d_vpr_e("returning error err %d\n", rc);
+ rc = -EINVAL;
+ goto exit;
+ }
+ delay_ms = clamp_t(u32, delay_ms, MSM_VIDC_MIN_STATS_DELAY_MS, MSM_VIDC_MAX_STATS_DELAY_MS);
+ core->capabilities[STATS_TIMEOUT_MS].value = delay_ms;
+ d_vpr_h("Stats delay is updated to - %d ms\n", delay_ms);
+
+exit:
+ return rc;
+}
+
+static ssize_t stats_delay_read_ms(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ size_t len;
+ char kbuf[MAX_DEBUG_LEVEL_STRING_LEN];
+ struct msm_vidc_core *core = file->private_data;
+
+ if (!core) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, core);
+ return 0;
+ }
+
+ len = scnprintf(kbuf, sizeof(kbuf), "%u\n", core->capabilities[STATS_TIMEOUT_MS].value);
+ return simple_read_from_buffer(buf, count, ppos, kbuf, len);
+}
+
+static const struct file_operations stats_delay_fops = {
+ .open = simple_open,
+ .write = stats_delay_write_ms,
+ .read = stats_delay_read_ms,
+};
+
+struct dentry *msm_vidc_debugfs_init_drv(void)
+{
+ struct dentry *dir = NULL;
+
+ dir = debugfs_create_dir("msm_vidc", NULL);
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ goto failed_create_dir;
+ }
+
+ return dir;
+
+failed_create_dir:
+ debugfs_remove_recursive(dir);
+
+ return NULL;
+}
+
+struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core)
+{
+ struct dentry *dir = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+ struct dentry *parent;
+
+ if (!core->debugfs_parent) {
+ d_vpr_e("%s: invalid params\n", __func__);
+ goto failed_create_dir;
+ }
+ parent = core->debugfs_parent;
+
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core");
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ d_vpr_e("Failed to create debugfs for msm_vidc\n");
+ goto failed_create_dir;
+ }
+ if (!debugfs_create_file("info", 0444, dir, core, &core_info_fops)) {
+ d_vpr_e("debugfs_create_file: fail\n");
+ goto failed_create_dir;
+ }
+
+ if (!debugfs_create_file("stats_delay_ms", 0644, dir, core, &stats_delay_fops)) {
+ d_vpr_e("debugfs_create_file: fail\n");
+ goto failed_create_dir;
+ }
+failed_create_dir:
+ return dir;
+}
+
+static int inst_info_open(struct inode *inode, struct file *file)
+{
+ d_vpr_l("Open inode ptr: %pK\n", inode->i_private);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t inst_info_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct core_inst_pair *idata = file->private_data;
+ struct msm_vidc_core *core;
+ struct msm_vidc_inst *inst;
+ char *cur, *end, *dbuf = NULL;
+ int i, j;
+ ssize_t len = 0;
+ struct v4l2_format *f;
+
+ if (!idata || !idata->core || !idata->inst) {
+ d_vpr_e("%s: invalid params %pK\n", __func__, idata);
+ return 0;
+ }
+
+ core = idata->core;
+ inst = idata->inst;
+
+ inst = get_inst(core, inst->session_id);
+ if (!inst) {
+ d_vpr_h("%s: instance has become obsolete", __func__);
+ return 0;
+ }
+
+ dbuf = vzalloc(MAX_DBG_BUF_SIZE);
+ if (!dbuf) {
+ d_vpr_e("%s: allocation failed\n", __func__);
+ len = -ENOMEM;
+ goto failed_alloc;
+ }
+ cur = dbuf;
+ end = cur + MAX_DBG_BUF_SIZE;
+
+ f = &inst->fmts[OUTPUT_PORT];
+ cur += write_str(cur, end - cur, "==============================\n");
+ cur += write_str(cur, end - cur, "INSTANCE: %pK (%s)\n", inst,
+ inst->domain == MSM_VIDC_ENCODER ? "Encoder" : "Decoder");
+ cur += write_str(cur, end - cur, "==============================\n");
+ cur += write_str(cur, end - cur, "core: %pK\n", inst->core);
+ cur += write_str(cur, end - cur, "height: %d\n", f->fmt.pix_mp.height);
+ cur += write_str(cur, end - cur, "width: %d\n", f->fmt.pix_mp.width);
+ cur += write_str(cur, end - cur, "fps: %d\n",
+ inst->capabilities[FRAME_RATE].value >> 16);
+ cur += write_str(cur, end - cur, "state: %d\n", inst->state);
+ cur += write_str(cur, end - cur, "-----------Formats-------------\n");
+ for (i = 0; i < MAX_PORT; i++) {
+ if (i != INPUT_PORT && i != OUTPUT_PORT)
+ continue;
+ f = &inst->fmts[i];
+ cur += write_str(cur, end - cur, "capability: %s\n",
+ i == INPUT_PORT ? "Output" : "Capture");
+ cur += write_str(cur, end - cur, "planes : %d\n",
+ f->fmt.pix_mp.num_planes);
+ cur += write_str(cur, end - cur,
+ "type: %s\n", i == INPUT_PORT ?
+ "Output" : "Capture");
+ cur += write_str(cur, end - cur, "count: %u\n",
+ inst->bufq[i].vb2q->num_buffers);
+
+ for (j = 0; j < f->fmt.pix_mp.num_planes; j++)
+ cur += write_str(cur, end - cur,
+ "size for plane %d: %u\n",
+ j, f->fmt.pix_mp.plane_fmt[j].sizeimage);
+
+ cur += write_str(cur, end - cur, "\n");
+ }
+ cur += write_str(cur, end - cur, "-------------------------------\n");
+ cur += write_str(cur, end - cur, "ETB Count: %d\n",
+ inst->debug_count.etb);
+ cur += write_str(cur, end - cur, "EBD Count: %d\n",
+ inst->debug_count.ebd);
+ cur += write_str(cur, end - cur, "FTB Count: %d\n",
+ inst->debug_count.ftb);
+ cur += write_str(cur, end - cur, "FBD Count: %d\n",
+ inst->debug_count.fbd);
+
+ len = simple_read_from_buffer(buf, count, ppos,
+ dbuf, cur - dbuf);
+
+ vfree(dbuf);
+failed_alloc:
+ put_inst(inst);
+ return len;
+}
+
+static int inst_info_release(struct inode *inode, struct file *file)
+{
+ d_vpr_l("Release inode ptr: %pK\n", inode->i_private);
+ file->private_data = NULL;
+ return 0;
+}
+
+static const struct file_operations inst_info_fops = {
+ .open = inst_info_open,
+ .read = inst_info_read,
+ .release = inst_info_release,
+};
+
+struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, struct dentry *parent)
+{
+ struct dentry *dir = NULL, *info = NULL;
+ char debugfs_name[MAX_DEBUGFS_NAME];
+ struct core_inst_pair *idata = NULL;
+
+ snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%d", inst->session_id);
+
+ idata = vzalloc(sizeof(*idata));
+ if (!idata) {
+ i_vpr_e(inst, "%s: allocation failed\n", __func__);
+ goto exit;
+ }
+
+ idata->core = inst->core;
+ idata->inst = inst;
+
+ dir = debugfs_create_dir(debugfs_name, parent);
+ if (IS_ERR_OR_NULL(dir)) {
+ dir = NULL;
+ i_vpr_e(inst,
+ "%s: Failed to create debugfs for msm_vidc\n",
+ __func__);
+ goto failed_create_dir;
+ }
+
+ info = debugfs_create_file("info", 0444, dir,
+ idata, &inst_info_fops);
+ if (IS_ERR_OR_NULL(info)) {
+ i_vpr_e(inst, "%s: debugfs_create_file: fail\n",
+ __func__);
+ goto failed_create_file;
+ }
+
+ dir->d_inode->i_private = info->d_inode->i_private;
+ return dir;
+
+failed_create_file:
+ debugfs_remove_recursive(dir);
+ dir = NULL;
+failed_create_dir:
+ vfree(idata);
+exit:
+ return dir;
+}
+
+void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst)
+{
+ struct dentry *dentry = NULL;
+
+ if (!inst->debugfs_root)
+ return;
+
+ dentry = inst->debugfs_root;
+ if (dentry->d_inode) {
+ i_vpr_l(inst, "%s: Destroy %pK\n",
+ __func__, dentry->d_inode->i_private);
+ vfree(dentry->d_inode->i_private);
+ dentry->d_inode->i_private = NULL;
+ }
+ debugfs_remove_recursive(dentry);
+ inst->debugfs_root = NULL;
+}
+
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+ enum msm_vidc_debugfs_event e)
+{
+ switch (e) {
+ case MSM_VIDC_DEBUGFS_EVENT_ETB:
+ inst->debug_count.etb++;
+ if (inst->debug_count.ebd &&
+ inst->debug_count.ftb > inst->debug_count.fbd) {
+ }
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_EBD:
+ inst->debug_count.ebd++;
+ /*
+ * Host needs to ensure FW at least have 2 buffers available always
+ * one for HW processing and another for fw processing in parallel
+ * to avoid FW starving for buffers
+ */
+ if (inst->debug_count.etb < (inst->debug_count.ebd + 2)) {
+ i_vpr_p(inst,
+ "EBD: FW needs input buffers. Processed etb %llu ebd %llu ftb %llu fbd %llu\n",
+ inst->debug_count.etb, inst->debug_count.ebd,
+ inst->debug_count.ftb, inst->debug_count.fbd);
+ }
+ if (inst->debug_count.fbd &&
+ inst->debug_count.ftb < (inst->debug_count.fbd + 2))
+ i_vpr_p(inst,
+ "EBD: FW needs output buffers. Processed etb %llu ebd %llu ftb %llu fbd %llu\n",
+ inst->debug_count.etb, inst->debug_count.ebd,
+ inst->debug_count.ftb, inst->debug_count.fbd);
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_FTB:
+ inst->debug_count.ftb++;
+ if (inst->debug_count.ebd &&
+ inst->debug_count.etb > inst->debug_count.ebd) {
+ }
+ break;
+ case MSM_VIDC_DEBUGFS_EVENT_FBD:
+ inst->debug_count.fbd++;
+ /*
+ * Host needs to ensure FW at least have 2 buffers available always
+ * one for HW processing and another for fw processing in parallel
+ * to avoid FW starving for buffers
+ */
+ if (inst->debug_count.ftb < (inst->debug_count.fbd + 2)) {
+ i_vpr_p(inst,
+ "FBD: FW needs output buffers. Processed etb %llu ebd %llu ftb %llu fbd %llu\n",
+ inst->debug_count.etb, inst->debug_count.ebd,
+ inst->debug_count.ftb, inst->debug_count.fbd);
+ }
+ if (inst->debug_count.ebd &&
+ inst->debug_count.etb < (inst->debug_count.ebd + 2))
+ i_vpr_p(inst,
+ "FBD: FW needs input buffers. Processed etb %llu ebd %llu ftb %llu fbd %llu\n",
+ inst->debug_count.etb, inst->debug_count.ebd,
+ inst->debug_count.ftb, inst->debug_count.fbd);
+ break;
+ default:
+ i_vpr_e(inst, "invalid event in debugfs: %d\n", e);
+ break;
+ }
+}
+
+int msm_vidc_check_ratelimit(void)
+{
+ static DEFINE_RATELIMIT_STATE(_rs,
+ VIDC_DBG_SESSION_RATELIMIT_INTERVAL,
+ VIDC_DBG_SESSION_RATELIMIT_BURST);
+ return __ratelimit(&_rs);
+}
--
2.7.4


2023-07-28 16:01:22

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 07/33] iris: iris: add video encoder files

This implements video encoder functionalities of the driver.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../media/platform/qcom/iris/vidc/inc/msm_venc.h | 34 +
.../media/platform/qcom/iris/vidc/src/msm_venc.c | 1484 ++++++++++++++++++++
2 files changed, 1518 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h
create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_venc.c

diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h
new file mode 100644
index 0000000..bb0a395
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _MSM_VENC_H_
+#define _MSM_VENC_H_
+
+#include "msm_vidc_core.h"
+#include "msm_vidc_inst.h"
+
+int msm_venc_streamoff_input(struct msm_vidc_inst *inst);
+int msm_venc_streamon_input(struct msm_vidc_inst *inst);
+int msm_venc_streamoff_output(struct msm_vidc_inst *inst);
+int msm_venc_streamon_output(struct msm_vidc_inst *inst);
+int msm_venc_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb2);
+int msm_venc_stop_cmd(struct msm_vidc_inst *inst);
+int msm_venc_start_cmd(struct msm_vidc_inst *inst);
+int msm_venc_try_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f);
+int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f);
+int msm_venc_s_fmt_output(struct msm_vidc_inst *inst, struct v4l2_format *f);
+int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f);
+int msm_venc_s_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s);
+int msm_venc_g_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s);
+int msm_venc_s_param(struct msm_vidc_inst *inst, struct v4l2_streamparm *s_parm);
+int msm_venc_g_param(struct msm_vidc_inst *inst, struct v4l2_streamparm *s_parm);
+int msm_venc_subscribe_event(struct msm_vidc_inst *inst,
+ const struct v4l2_event_subscription *sub);
+int msm_venc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f);
+int msm_venc_inst_init(struct msm_vidc_inst *inst);
+int msm_venc_inst_deinit(struct msm_vidc_inst *inst);
+
+#endif // _MSM_VENC_H_
diff --git a/drivers/media/platform/qcom/iris/vidc/src/msm_venc.c b/drivers/media/platform/qcom/iris/vidc/src/msm_venc.c
new file mode 100644
index 0000000..4962716
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/src/msm_venc.c
@@ -0,0 +1,1484 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "hfi_packet.h"
+#include "msm_media_info.h"
+#include "msm_venc.h"
+#include "msm_vidc_control.h"
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_inst.h"
+#include "msm_vidc_internal.h"
+#include "msm_vidc_platform.h"
+#include "msm_vidc_power.h"
+#include "venus_hfi.h"
+
+static const u32 msm_venc_input_set_prop[] = {
+ HFI_PROP_COLOR_FORMAT,
+ HFI_PROP_RAW_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LINEAR_STRIDE_SCANLINE,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+};
+
+static const u32 msm_venc_output_set_prop[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+};
+
+static const u32 msm_venc_input_subscribe_for_properties[] = {
+ HFI_PROP_NO_OUTPUT,
+};
+
+static const u32 msm_venc_output_subscribe_for_properties[] = {
+ HFI_PROP_PICTURE_TYPE,
+ HFI_PROP_BUFFER_MARK,
+ HFI_PROP_WORST_COMPRESSION_RATIO,
+};
+
+static const u32 msm_venc_output_internal_buffer_type[] = {
+ MSM_VIDC_BUF_BIN,
+ MSM_VIDC_BUF_COMV,
+ MSM_VIDC_BUF_NON_COMV,
+ MSM_VIDC_BUF_LINE,
+ MSM_VIDC_BUF_DPB,
+};
+
+static const u32 msm_venc_input_internal_buffer_type[] = {
+ MSM_VIDC_BUF_VPSS,
+};
+
+struct msm_venc_prop_type_handle {
+ u32 type;
+ int (*handle)(struct msm_vidc_inst *inst, enum msm_vidc_port_type port);
+};
+
+static int msm_venc_codec_change(struct msm_vidc_inst *inst, u32 v4l2_codec)
+{
+ int rc = 0;
+ bool session_init = false;
+
+ if (!inst->codec)
+ session_init = true;
+
+ if (inst->codec && inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat == v4l2_codec)
+ return 0;
+
+ i_vpr_h(inst, "%s: codec changed from %s to %s\n",
+ __func__, v4l2_pixelfmt_name(inst, inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat),
+ v4l2_pixelfmt_name(inst, v4l2_codec));
+
+ inst->codec = v4l2_codec_to_driver(inst, v4l2_codec, __func__);
+ if (!inst->codec) {
+ i_vpr_e(inst, "%s: invalid codec %#x\n", __func__, v4l2_codec);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat = v4l2_codec;
+ rc = msm_vidc_update_debug_str(inst);
+ if (rc)
+ goto exit;
+
+ rc = msm_vidc_get_inst_capability(inst);
+ if (rc)
+ goto exit;
+
+ rc = msm_vidc_ctrl_handler_init(inst, session_init);
+ if (rc)
+ goto exit;
+
+ rc = msm_vidc_update_buffer_count(inst, INPUT_PORT);
+ if (rc)
+ goto exit;
+
+ rc = msm_vidc_update_buffer_count(inst, OUTPUT_PORT);
+ if (rc)
+ goto exit;
+
+exit:
+ return rc;
+}
+
+/* todo: add logs for each property once finalised */
+static int msm_venc_set_colorformat(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ u32 pixelformat;
+ enum msm_vidc_colorformat_type colorformat;
+ u32 hfi_colorformat;
+
+ if (port != INPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ return -EINVAL;
+ }
+
+ pixelformat = inst->fmts[INPUT_PORT].fmt.pix_mp.pixelformat;
+ colorformat = v4l2_colorformat_to_driver(inst, pixelformat, __func__);
+ if (!(colorformat & inst->capabilities[PIX_FMTS].step_or_mask)) {
+ i_vpr_e(inst, "%s: invalid pixelformat %s\n",
+ __func__, v4l2_pixelfmt_name(inst, pixelformat));
+ return -EINVAL;
+ }
+
+ hfi_colorformat = get_hfi_colorformat(inst, colorformat);
+ i_vpr_h(inst, "%s: hfi colorformat: %#x", __func__,
+ hfi_colorformat);
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_COLOR_FORMAT,
+ HFI_HOST_FLAGS_NONE,
+ get_hfi_port(inst, port),
+ HFI_PAYLOAD_U32_ENUM,
+ &hfi_colorformat,
+ sizeof(u32));
+ if (rc)
+ return rc;
+ return 0;
+}
+
+static int msm_venc_set_stride_scanline(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ u32 color_format, stride_y, scanline_y;
+ u32 stride_uv = 0, scanline_uv = 0;
+ u32 payload[2];
+
+ if (port != INPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ return -EINVAL;
+ }
+
+ color_format = inst->capabilities[PIX_FMTS].value;
+ if (!is_linear_colorformat(color_format)) {
+ i_vpr_h(inst,
+ "%s: not a linear color fmt, property is not set\n",
+ __func__);
+ return 0;
+ }
+
+ if (is_rgba_colorformat(color_format)) {
+ stride_y = video_rgb_stride_pix(color_format,
+ inst->fmts[INPUT_PORT].fmt.pix_mp.width);
+ scanline_y = video_rgb_scanlines(color_format,
+ inst->fmts[INPUT_PORT].fmt.pix_mp.height);
+ } else {
+ stride_y = video_y_stride_pix(color_format,
+ inst->fmts[INPUT_PORT].fmt.pix_mp.width);
+ scanline_y = video_y_scanlines(color_format,
+ inst->fmts[INPUT_PORT].fmt.pix_mp.height);
+ }
+ if (color_format == MSM_VIDC_FMT_NV12 ||
+ color_format == MSM_VIDC_FMT_P010 ||
+ color_format == MSM_VIDC_FMT_NV21) {
+ stride_uv = stride_y;
+ scanline_uv = scanline_y / 2;
+ }
+
+ payload[0] = stride_y << 16 | scanline_y;
+ payload[1] = stride_uv << 16 | scanline_uv;
+ i_vpr_h(inst,
+ "%s: y: stride %d scanline, %d uv: stride %d scanline_uv %d",
+ __func__,
+ stride_y, scanline_y, stride_uv, scanline_uv);
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_LINEAR_STRIDE_SCANLINE,
+ HFI_HOST_FLAGS_NONE,
+ get_hfi_port(inst, port),
+ HFI_PAYLOAD_64_PACKED,
+ &payload,
+ sizeof(u64));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int msm_venc_set_raw_resolution(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ u32 resolution;
+
+ if (port != INPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ return -EINVAL;
+ }
+
+ resolution = (inst->fmts[port].fmt.pix_mp.width << 16) |
+ inst->fmts[port].fmt.pix_mp.height;
+ i_vpr_h(inst, "%s: width: %d height: %d\n", __func__,
+ inst->fmts[port].fmt.pix_mp.width, inst->fmts[port].fmt.pix_mp.height);
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_RAW_RESOLUTION,
+ HFI_HOST_FLAGS_NONE,
+ get_hfi_port(inst, port),
+ HFI_PAYLOAD_32_PACKED,
+ &resolution,
+ sizeof(u32));
+ if (rc)
+ return rc;
+ return 0;
+}
+
+static int msm_venc_set_bitstream_resolution(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ u32 resolution;
+
+ if (port != OUTPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ return -EINVAL;
+ }
+
+ resolution = (inst->fmts[port].fmt.pix_mp.width << 16) |
+ inst->fmts[port].fmt.pix_mp.height;
+ i_vpr_h(inst, "%s: width: %d height: %d\n", __func__,
+ inst->fmts[port].fmt.pix_mp.width,
+ inst->fmts[port].fmt.pix_mp.height);
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_HOST_FLAGS_NONE,
+ get_hfi_port(inst, port),
+ HFI_PAYLOAD_32_PACKED,
+ &resolution,
+ sizeof(u32));
+ if (rc)
+ return rc;
+ return 0;
+}
+
+static int msm_venc_set_crop_offsets(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ u32 left_offset, top_offset, right_offset, bottom_offset;
+ u32 crop[2] = {0};
+ u32 width, height;
+
+ if (port != OUTPUT_PORT && port != INPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ return -EINVAL;
+ }
+
+ if (port == INPUT_PORT) {
+ left_offset = inst->crop.left;
+ top_offset = inst->crop.top;
+ width = inst->crop.width;
+ height = inst->crop.height;
+ } else {
+ left_offset = inst->compose.left;
+ top_offset = inst->compose.top;
+ width = inst->compose.width;
+ height = inst->compose.height;
+ if (is_rotation_90_or_270(inst)) {
+ width = inst->compose.height;
+ height = inst->compose.width;
+ }
+ }
+
+ right_offset = (inst->fmts[port].fmt.pix_mp.width - width);
+ bottom_offset = (inst->fmts[port].fmt.pix_mp.height - height);
+
+ crop[0] = left_offset << 16 | top_offset;
+ crop[1] = right_offset << 16 | bottom_offset;
+ i_vpr_h(inst,
+ "%s: l_off: %d t_off: %d r_off: %d b_off: %d",
+ __func__,
+ left_offset, top_offset, right_offset, bottom_offset);
+
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_HOST_FLAGS_NONE,
+ get_hfi_port(inst, port),
+ HFI_PAYLOAD_64_PACKED,
+ &crop,
+ sizeof(u64));
+ if (rc)
+ return rc;
+ return 0;
+}
+
+static int msm_venc_set_colorspace(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ u32 primaries = MSM_VIDC_PRIMARIES_RESERVED;
+ u32 matrix_coeff = MSM_VIDC_MATRIX_COEFF_RESERVED;
+ u32 transfer_char = MSM_VIDC_TRANSFER_RESERVED;
+ u32 full_range = 0;
+ u32 colour_description_present_flag = 0;
+ u32 video_signal_type_present_flag = 0, payload = 0;
+ /* Unspecified video format */
+ u32 video_format = 5;
+ struct v4l2_format *input_fmt;
+ u32 pix_fmt;
+ struct v4l2_pix_format_mplane *pixmp = NULL;
+
+ if (port != INPUT_PORT) {
+ i_vpr_e(inst, "%s: invalid port %d\n", __func__, port);
+ return -EINVAL;
+ }
+
+ if (inst->capabilities[SIGNAL_COLOR_INFO].flags & CAP_FLAG_CLIENT_SET) {
+ i_vpr_h(inst, "%s: client configured colorspace via control\n", __func__);
+ return 0;
+ }
+
+ input_fmt = &inst->fmts[INPUT_PORT];
+ pixmp = &inst->fmts[port].fmt.pix_mp;
+ pix_fmt = v4l2_colorformat_to_driver(inst,
+ input_fmt->fmt.pix_mp.pixelformat, __func__);
+ if (pixmp->colorspace != V4L2_COLORSPACE_DEFAULT ||
+ pixmp->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT ||
+ pixmp->xfer_func != V4L2_XFER_FUNC_DEFAULT) {
+ colour_description_present_flag = 1;
+ video_signal_type_present_flag = 1;
+ primaries = v4l2_color_primaries_to_driver(inst,
+ pixmp->colorspace, __func__);
+ matrix_coeff = v4l2_matrix_coeff_to_driver(inst,
+ pixmp->ycbcr_enc, __func__);
+ transfer_char = v4l2_transfer_char_to_driver(inst,
+ pixmp->xfer_func, __func__);
+ } else if (is_rgba_colorformat(pix_fmt)) {
+ colour_description_present_flag = 1;
+ video_signal_type_present_flag = 1;
+ primaries = MSM_VIDC_PRIMARIES_BT709;
+ matrix_coeff = MSM_VIDC_MATRIX_COEFF_BT709;
+ transfer_char = MSM_VIDC_TRANSFER_BT709;
+ full_range = 0;
+ }
+
+ if (pixmp->quantization !=
+ V4L2_QUANTIZATION_DEFAULT) {
+ video_signal_type_present_flag = 1;
+ full_range = pixmp->quantization ==
+ V4L2_QUANTIZATION_FULL_RANGE ? 1 : 0;
+ }
+
+ payload = (matrix_coeff & 0xFF) |
+ ((transfer_char << 8) & 0xFF00) |
+ ((primaries << 16) & 0xFF0000) |
+ ((colour_description_present_flag << 24) & 0x1000000) |
+ ((full_range << 25) & 0x2000000) |
+ ((video_format << 26) & 0x1C000000) |
+ ((video_signal_type_present_flag << 29) & 0x20000000);
+ i_vpr_h(inst, "%s: color info: %#x\n", __func__, payload);
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+ HFI_HOST_FLAGS_NONE,
+ get_hfi_port(inst, port),
+ HFI_PAYLOAD_32_PACKED,
+ &payload,
+ sizeof(u32));
+ if (rc)
+ return rc;
+ return 0;
+}
+
+static int msm_venc_set_quality_mode(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+ struct msm_vidc_core *core = inst->core;
+ u32 mode;
+
+ rc = call_session_op(core, decide_quality_mode, inst);
+ if (rc) {
+ i_vpr_e(inst, "%s: decide_work_route failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ mode = inst->capabilities[QUALITY_MODE].value;
+ i_vpr_h(inst, "%s: quality_mode: %u\n", __func__, mode);
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_QUALITY_MODE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_U32_ENUM,
+ &mode,
+ sizeof(u32));
+ if (rc)
+ return rc;
+ return 0;
+}
+
+static int msm_venc_set_ring_buffer_count(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+ struct msm_vidc_inst_cap *cap;
+
+ cap = &inst->capabilities[ENC_RING_BUFFER_COUNT];
+
+ if (!cap->set)
+ return 0;
+
+ rc = cap->set(inst, ENC_RING_BUFFER_COUNT);
+ if (rc) {
+ i_vpr_e(inst, "%s: set cap failed\n", __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int msm_venc_set_input_properties(struct msm_vidc_inst *inst)
+{
+ int i, j, rc = 0;
+ static const struct msm_venc_prop_type_handle prop_type_handle_arr[] = {
+ {HFI_PROP_COLOR_FORMAT, msm_venc_set_colorformat },
+ {HFI_PROP_RAW_RESOLUTION, msm_venc_set_raw_resolution },
+ {HFI_PROP_CROP_OFFSETS, msm_venc_set_crop_offsets },
+ {HFI_PROP_LINEAR_STRIDE_SCANLINE, msm_venc_set_stride_scanline },
+ {HFI_PROP_SIGNAL_COLOR_INFO, msm_venc_set_colorspace },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_input_set_prop); i++) {
+ /* set session input properties */
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_arr); j++) {
+ if (prop_type_handle_arr[j].type == msm_venc_input_set_prop[i]) {
+ rc = prop_type_handle_arr[j].handle(inst, INPUT_PORT);
+ if (rc)
+ goto exit;
+ break;
+ }
+ }
+
+ /* is property type unknown ? */
+ if (j == ARRAY_SIZE(prop_type_handle_arr))
+ i_vpr_e(inst, "%s: unknown property %#x\n", __func__,
+ msm_venc_input_set_prop[i]);
+ }
+
+exit:
+ return rc;
+}
+
+static int msm_venc_set_output_properties(struct msm_vidc_inst *inst)
+{
+ int i, j, rc = 0;
+ static const struct msm_venc_prop_type_handle prop_type_handle_arr[] = {
+ {HFI_PROP_BITSTREAM_RESOLUTION, msm_venc_set_bitstream_resolution },
+ {HFI_PROP_CROP_OFFSETS, msm_venc_set_crop_offsets },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_output_set_prop); i++) {
+ /* set session output properties */
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_arr); j++) {
+ if (prop_type_handle_arr[j].type == msm_venc_output_set_prop[i]) {
+ rc = prop_type_handle_arr[j].handle(inst, OUTPUT_PORT);
+ if (rc)
+ goto exit;
+ break;
+ }
+ }
+
+ /* is property type unknown ? */
+ if (j == ARRAY_SIZE(prop_type_handle_arr))
+ i_vpr_e(inst, "%s: unknown property %#x\n", __func__,
+ msm_venc_output_set_prop[i]);
+ }
+
+exit:
+ return rc;
+}
+
+static int msm_venc_set_internal_properties(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+
+ rc = msm_venc_set_quality_mode(inst);
+ if (rc)
+ return rc;
+
+ rc = msm_venc_set_ring_buffer_count(inst);
+
+ return rc;
+}
+
+static int msm_venc_get_input_internal_buffers(struct msm_vidc_inst *inst)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_input_internal_buffer_type); i++) {
+ rc = msm_vidc_get_internal_buffers(inst, msm_venc_input_internal_buffer_type[i]);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+static int msm_venc_destroy_internal_buffers(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ int rc = 0;
+ struct msm_vidc_buffers *buffers;
+ struct msm_vidc_buffer *buf, *dummy;
+ const u32 *internal_buf_type;
+ u32 i, len;
+
+ if (port == INPUT_PORT) {
+ internal_buf_type = msm_venc_input_internal_buffer_type;
+ len = ARRAY_SIZE(msm_venc_input_internal_buffer_type);
+ } else {
+ internal_buf_type = msm_venc_output_internal_buffer_type;
+ len = ARRAY_SIZE(msm_venc_output_internal_buffer_type);
+ }
+
+ for (i = 0; i < len; i++) {
+ buffers = msm_vidc_get_buffers(inst, internal_buf_type[i], __func__);
+ if (!buffers)
+ return -EINVAL;
+
+ if (buffers->reuse) {
+ i_vpr_l(inst, "%s: reuse enabled for %s\n", __func__,
+ buf_name(internal_buf_type[i]));
+ continue;
+ }
+
+ list_for_each_entry_safe(buf, dummy, &buffers->list, list) {
+ i_vpr_h(inst,
+ "%s: destroying internal buffer: type %d idx %d fd %d addr %#llx size %d\n",
+ __func__, buf->type, buf->index, buf->fd,
+ buf->device_addr, buf->buffer_size);
+
+ rc = msm_vidc_destroy_internal_buffer(inst, buf);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int msm_venc_create_input_internal_buffers(struct msm_vidc_inst *inst)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_input_internal_buffer_type); i++) {
+ rc = msm_vidc_create_internal_buffers(inst, msm_venc_input_internal_buffer_type[i]);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+static int msm_venc_queue_input_internal_buffers(struct msm_vidc_inst *inst)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_input_internal_buffer_type); i++) {
+ rc = msm_vidc_queue_internal_buffers(inst, msm_venc_input_internal_buffer_type[i]);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+static int msm_venc_get_output_internal_buffers(struct msm_vidc_inst *inst)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_output_internal_buffer_type); i++) {
+ rc = msm_vidc_get_internal_buffers(inst, msm_venc_output_internal_buffer_type[i]);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+static int msm_venc_create_output_internal_buffers(struct msm_vidc_inst *inst)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_output_internal_buffer_type); i++) {
+ rc = msm_vidc_create_internal_buffers(inst,
+ msm_venc_output_internal_buffer_type[i]);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int msm_venc_queue_output_internal_buffers(struct msm_vidc_inst *inst)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < ARRAY_SIZE(msm_venc_output_internal_buffer_type); i++) {
+ rc = msm_vidc_queue_internal_buffers(inst, msm_venc_output_internal_buffer_type[i]);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int msm_venc_property_subscription(struct msm_vidc_inst *inst,
+ enum msm_vidc_port_type port)
+{
+ u32 payload[32] = {0};
+ u32 i;
+ u32 payload_size = 0;
+
+ payload[0] = HFI_MODE_PROPERTY;
+ if (port == INPUT_PORT) {
+ for (i = 0; i < ARRAY_SIZE(msm_venc_input_subscribe_for_properties); i++)
+ payload[i + 1] = msm_venc_input_subscribe_for_properties[i];
+ payload_size = (ARRAY_SIZE(msm_venc_input_subscribe_for_properties) + 1) *
+ sizeof(u32);
+ } else if (port == OUTPUT_PORT) {
+ for (i = 0; i < ARRAY_SIZE(msm_venc_output_subscribe_for_properties); i++)
+ payload[i + 1] = msm_venc_output_subscribe_for_properties[i];
+ payload_size = (ARRAY_SIZE(msm_venc_output_subscribe_for_properties) + 1) *
+ sizeof(u32);
+ } else {
+ i_vpr_e(inst, "%s: invalid port: %d\n", __func__, port);
+ return -EINVAL;
+ }
+
+ return venus_hfi_session_command(inst,
+ HFI_CMD_SUBSCRIBE_MODE,
+ port,
+ HFI_PAYLOAD_U32_ARRAY,
+ &payload[0],
+ payload_size);
+}
+
+int msm_venc_streamoff_input(struct msm_vidc_inst *inst)
+{
+ return msm_vidc_session_streamoff(inst, INPUT_PORT);
+}
+
+int msm_venc_streamon_input(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+
+ rc = msm_vidc_check_session_supported(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_vidc_check_scaling_supported(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_set_input_properties(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_get_input_internal_buffers(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_destroy_internal_buffers(inst, INPUT_PORT);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_create_input_internal_buffers(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_queue_input_internal_buffers(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_property_subscription(inst, INPUT_PORT);
+ if (rc)
+ goto error;
+
+ rc = msm_vidc_process_streamon_input(inst);
+ if (rc)
+ goto error;
+
+ return 0;
+
+error:
+ i_vpr_e(inst, "%s: failed\n", __func__);
+ return rc;
+}
+
+int msm_venc_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb2)
+{
+ return msm_vidc_queue_buffer_single(inst, vb2);
+}
+
+int msm_venc_stop_cmd(struct msm_vidc_inst *inst)
+{
+ i_vpr_h(inst, "received cmd: drain\n");
+ return msm_vidc_process_drain(inst);
+}
+
+int msm_venc_start_cmd(struct msm_vidc_inst *inst)
+{
+ i_vpr_h(inst, "received cmd: resume\n");
+
+ vb2_clear_last_buffer_dequeued(inst->bufq[OUTPUT_PORT].vb2q);
+
+ /* tune power features */
+ msm_vidc_allow_dcvs(inst);
+ msm_vidc_power_data_reset(inst);
+
+ /* print final buffer counts & size details */
+ msm_vidc_print_buffer_info(inst);
+
+ /* print internal buffer memory usage stats */
+ msm_vidc_print_memory_stats(inst);
+
+ return msm_vidc_process_resume(inst);
+}
+
+int msm_venc_streamoff_output(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+
+ core = inst->core;
+
+ /* restore LAYER_COUNT max allowed value */
+ inst->capabilities[ENH_LAYER_COUNT].max =
+ core->capabilities[MAX_ENH_LAYER_COUNT].value;
+
+ rc = msm_vidc_session_streamoff(inst, OUTPUT_PORT);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+int msm_venc_streamon_output(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+
+ rc = msm_venc_set_output_properties(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_vidc_set_v4l2_properties(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_set_internal_properties(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_get_output_internal_buffers(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_destroy_internal_buffers(inst, OUTPUT_PORT);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_create_output_internal_buffers(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_queue_output_internal_buffers(inst);
+ if (rc)
+ goto error;
+
+ rc = msm_venc_property_subscription(inst, OUTPUT_PORT);
+ if (rc)
+ goto error;
+
+ rc = msm_vidc_process_streamon_output(inst);
+ if (rc)
+ goto error;
+
+ return 0;
+
+error:
+ i_vpr_e(inst, "%s: failed\n", __func__);
+ msm_venc_streamoff_output(inst);
+ return rc;
+}
+
+int msm_venc_try_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+ int rc = 0;
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ u32 pix_fmt;
+
+ memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
+
+ if (f->type == INPUT_MPLANE) {
+ pix_fmt = v4l2_colorformat_to_driver(inst, f->fmt.pix_mp.pixelformat, __func__);
+ if (!pix_fmt) {
+ i_vpr_e(inst, "%s: unsupported format, set current params\n", __func__);
+ f->fmt.pix_mp.pixelformat = inst->fmts[INPUT_PORT].fmt.pix_mp.pixelformat;
+ f->fmt.pix_mp.width = inst->fmts[INPUT_PORT].fmt.pix_mp.width;
+ f->fmt.pix_mp.height = inst->fmts[INPUT_PORT].fmt.pix_mp.height;
+ pix_fmt = v4l2_colorformat_to_driver(inst, f->fmt.pix_mp.pixelformat,
+ __func__);
+ }
+ } else if (f->type == OUTPUT_MPLANE) {
+ pix_fmt = v4l2_codec_to_driver(inst, f->fmt.pix_mp.pixelformat, __func__);
+ if (!pix_fmt) {
+ i_vpr_e(inst, "%s: unsupported codec, set current params\n", __func__);
+ f->fmt.pix_mp.width = inst->fmts[OUTPUT_PORT].fmt.pix_mp.width;
+ f->fmt.pix_mp.height = inst->fmts[OUTPUT_PORT].fmt.pix_mp.height;
+ f->fmt.pix_mp.pixelformat = inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat;
+ }
+ } else {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, f->type);
+ return -EINVAL;
+ }
+
+ if (pixmp->field == V4L2_FIELD_ANY)
+ pixmp->field = V4L2_FIELD_NONE;
+ pixmp->num_planes = 1;
+
+ return rc;
+}
+
+int msm_venc_s_fmt_output(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+ int rc = 0;
+ struct v4l2_format *fmt;
+ struct msm_vidc_core *core;
+ u32 codec_align;
+ u32 width, height;
+ enum msm_vidc_codec_type codec;
+
+ core = inst->core;
+ msm_venc_try_fmt(inst, f);
+
+ fmt = &inst->fmts[OUTPUT_PORT];
+ if (fmt->fmt.pix_mp.pixelformat != f->fmt.pix_mp.pixelformat) {
+ rc = msm_venc_codec_change(inst, f->fmt.pix_mp.pixelformat);
+ if (rc)
+ return rc;
+ }
+ fmt->type = OUTPUT_MPLANE;
+
+ codec = v4l2_codec_to_driver(inst, f->fmt.pix_mp.pixelformat, __func__);
+
+ codec_align = (codec == MSM_VIDC_HEVC) ? 32 : 16;
+ /* use rotated width height if rotation is enabled */
+ width = inst->compose.width;
+ height = inst->compose.height;
+ if (is_rotation_90_or_270(inst)) {
+ width = inst->compose.height;
+ height = inst->compose.width;
+ }
+ /* width, height is readonly for client */
+ fmt->fmt.pix_mp.width = ALIGN(width, codec_align);
+ fmt->fmt.pix_mp.height = ALIGN(height, codec_align);
+ fmt->fmt.pix_mp.num_planes = 1;
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage =
+ call_session_op(core, buffer_size, inst, MSM_VIDC_BUF_OUTPUT);
+ /* video hw supports conversion to V4L2_COLORSPACE_REC709 only */
+ if (f->fmt.pix_mp.colorspace != V4L2_COLORSPACE_DEFAULT &&
+ f->fmt.pix_mp.colorspace != V4L2_COLORSPACE_REC709)
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
+ fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+ inst->buffers.output.min_count =
+ call_session_op(core, min_count, inst, MSM_VIDC_BUF_OUTPUT);
+ inst->buffers.output.extra_count =
+ call_session_op(core, extra_count, inst, MSM_VIDC_BUF_OUTPUT);
+ if (inst->buffers.output.actual_count <
+ inst->buffers.output.min_count +
+ inst->buffers.output.extra_count) {
+ inst->buffers.output.actual_count =
+ inst->buffers.output.min_count +
+ inst->buffers.output.extra_count;
+ }
+ inst->buffers.output.size =
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ i_vpr_h(inst,
+ "%s: type: OUTPUT, codec %s width %d height %d size %u min_count %d extra_count %d\n",
+ __func__, v4l2_pixelfmt_name(inst, fmt->fmt.pix_mp.pixelformat),
+ fmt->fmt.pix_mp.width, fmt->fmt.pix_mp.height,
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage,
+ inst->buffers.output.min_count,
+ inst->buffers.output.extra_count);
+
+ /* finally update client format */
+ memcpy(f, fmt, sizeof(struct v4l2_format));
+ return rc;
+}
+
+static int msm_venc_s_fmt_input(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+ int rc = 0;
+ struct v4l2_format *fmt, *output_fmt;
+ struct msm_vidc_core *core;
+ u32 pix_fmt, width, height, size, bytesperline;
+
+ core = inst->core;
+ msm_venc_try_fmt(inst, f);
+
+ pix_fmt = v4l2_colorformat_to_driver(inst, f->fmt.pix_mp.pixelformat, __func__);
+ msm_vidc_update_cap_value(inst, PIX_FMTS, pix_fmt, __func__);
+
+ width = f->fmt.pix_mp.width;
+ height = f->fmt.pix_mp.height;
+
+ if (is_rgba_colorformat(pix_fmt))
+ bytesperline = video_rgb_stride_bytes(pix_fmt, f->fmt.pix_mp.width);
+ else
+ bytesperline = video_y_stride_bytes(pix_fmt, f->fmt.pix_mp.width);
+
+ fmt = &inst->fmts[INPUT_PORT];
+ fmt->type = INPUT_MPLANE;
+ fmt->fmt.pix_mp.width = width;
+ fmt->fmt.pix_mp.height = height;
+ fmt->fmt.pix_mp.num_planes = 1;
+ fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = bytesperline;
+ size = call_session_op(core, buffer_size, inst, MSM_VIDC_BUF_INPUT);
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage = size;
+ /* update input port colorspace info */
+ fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+ /*
+ * Update output port colorspace info.
+ */
+ output_fmt = &inst->fmts[OUTPUT_PORT];
+ output_fmt->fmt.pix_mp.colorspace = fmt->fmt.pix_mp.colorspace;
+ output_fmt->fmt.pix_mp.xfer_func = fmt->fmt.pix_mp.xfer_func;
+ output_fmt->fmt.pix_mp.ycbcr_enc = fmt->fmt.pix_mp.ycbcr_enc;
+ output_fmt->fmt.pix_mp.quantization = fmt->fmt.pix_mp.quantization;
+
+ inst->buffers.input.min_count =
+ call_session_op(core, min_count, inst, MSM_VIDC_BUF_INPUT);
+ inst->buffers.input.extra_count =
+ call_session_op(core, extra_count, inst, MSM_VIDC_BUF_INPUT);
+ if (inst->buffers.input.actual_count <
+ inst->buffers.input.min_count +
+ inst->buffers.input.extra_count) {
+ inst->buffers.input.actual_count =
+ inst->buffers.input.min_count +
+ inst->buffers.input.extra_count;
+ }
+ inst->buffers.input.size = size;
+
+ if (f->fmt.pix_mp.width != inst->crop.width ||
+ f->fmt.pix_mp.height != inst->crop.height) {
+ /* reset crop dimensions with updated resolution */
+ inst->crop.top = 0;
+ inst->crop.left = 0;
+ inst->crop.width = f->fmt.pix_mp.width;
+ inst->crop.height = f->fmt.pix_mp.height;
+
+ /* reset compose dimensions with updated resolution */
+ inst->compose.top = 0;
+ inst->compose.left = 0;
+ inst->compose.width = f->fmt.pix_mp.width;
+ inst->compose.height = f->fmt.pix_mp.height;
+
+ /* update output format */
+ rc = msm_venc_s_fmt_output(inst, output_fmt);
+ if (rc)
+ return rc;
+ }
+
+ i_vpr_h(inst,
+ "%s: type: INPUT, format %s width %d height %d size %u min_count %d extra_count %d\n",
+ __func__, v4l2_pixelfmt_name(inst, fmt->fmt.pix_mp.pixelformat),
+ fmt->fmt.pix_mp.width, fmt->fmt.pix_mp.height,
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage,
+ inst->buffers.input.min_count,
+ inst->buffers.input.extra_count);
+
+ /* finally update client format */
+ memcpy(f, fmt, sizeof(struct v4l2_format));
+
+ return rc;
+}
+
+int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+ int rc = 0;
+
+ if (f->type == INPUT_MPLANE) {
+ rc = msm_venc_s_fmt_input(inst, f);
+ if (rc)
+ goto exit;
+ } else if (f->type == OUTPUT_MPLANE) {
+ rc = msm_venc_s_fmt_output(inst, f);
+ if (rc)
+ goto exit;
+ } else {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, f->type);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+exit:
+ if (rc)
+ i_vpr_e(inst, "%s: failed\n", __func__);
+
+ return rc;
+}
+
+int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+ int rc = 0;
+ int port;
+
+ port = v4l2_type_to_driver_port(inst, f->type, __func__);
+ if (port < 0)
+ return -EINVAL;
+
+ memcpy(f, &inst->fmts[port], sizeof(struct v4l2_format));
+
+ return rc;
+}
+
+int msm_venc_s_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s)
+{
+ int rc = 0;
+ struct v4l2_format *output_fmt;
+
+ if (s->type != INPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, s->type);
+ return -EINVAL;
+ }
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ if (s->r.left || s->r.top) {
+ i_vpr_h(inst, "%s: unsupported top %d or left %d\n",
+ __func__, s->r.left, s->r.top);
+ s->r.left = 0;
+ s->r.top = 0;
+ }
+ if (s->r.width > inst->fmts[INPUT_PORT].fmt.pix_mp.width) {
+ i_vpr_h(inst, "%s: unsupported width %d, fmt width %d\n",
+ __func__, s->r.width,
+ inst->fmts[INPUT_PORT].fmt.pix_mp.width);
+ s->r.width = inst->fmts[INPUT_PORT].fmt.pix_mp.width;
+ }
+ if (s->r.height > inst->fmts[INPUT_PORT].fmt.pix_mp.height) {
+ i_vpr_h(inst, "%s: unsupported height %d, fmt height %d\n",
+ __func__, s->r.height,
+ inst->fmts[INPUT_PORT].fmt.pix_mp.height);
+ s->r.height = inst->fmts[INPUT_PORT].fmt.pix_mp.height;
+ }
+
+ inst->crop.left = s->r.left;
+ inst->crop.top = s->r.top;
+ inst->crop.width = s->r.width;
+ inst->crop.height = s->r.height;
+ /* adjust compose such that it is within crop */
+ inst->compose.left = inst->crop.left;
+ inst->compose.top = inst->crop.top;
+ inst->compose.width = inst->crop.width;
+ inst->compose.height = inst->crop.height;
+ /* update output format based on new crop dimensions */
+ output_fmt = &inst->fmts[OUTPUT_PORT];
+ rc = msm_venc_s_fmt_output(inst, output_fmt);
+ if (rc)
+ return rc;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ if (s->r.left < inst->crop.left) {
+ i_vpr_e(inst,
+ "%s: compose left (%d) less than crop left (%d)\n",
+ __func__, s->r.left, inst->crop.left);
+ s->r.left = inst->crop.left;
+ }
+ if (s->r.top < inst->crop.top) {
+ i_vpr_e(inst,
+ "%s: compose top (%d) less than crop top (%d)\n",
+ __func__, s->r.top, inst->crop.top);
+ s->r.top = inst->crop.top;
+ }
+ if (s->r.width > inst->crop.width) {
+ i_vpr_e(inst,
+ "%s: compose width (%d) greate than crop width (%d)\n",
+ __func__, s->r.width, inst->crop.width);
+ s->r.width = inst->crop.width;
+ }
+ if (s->r.height > inst->crop.height) {
+ i_vpr_e(inst,
+ "%s: compose height (%d) greate than crop height (%d)\n",
+ __func__, s->r.height, inst->crop.height);
+ s->r.height = inst->crop.height;
+ }
+ inst->compose.left = s->r.left;
+ inst->compose.top = s->r.top;
+ inst->compose.width = s->r.width;
+ inst->compose.height = s->r.height;
+
+ if (is_scaling_enabled(inst)) {
+ i_vpr_h(inst,
+ "%s: scaling enabled, crop: l %d t %d w %d h %d compose: l %d t %d w %d h %d\n",
+ __func__, inst->crop.left, inst->crop.top,
+ inst->crop.width, inst->crop.height,
+ inst->compose.left, inst->compose.top,
+ inst->compose.width, inst->compose.height);
+ }
+
+ /* update output format based on new compose dimensions */
+ output_fmt = &inst->fmts[OUTPUT_PORT];
+ rc = msm_venc_s_fmt_output(inst, output_fmt);
+ if (rc)
+ return rc;
+ break;
+ default:
+ i_vpr_e(inst, "%s: invalid target %d\n",
+ __func__, s->target);
+ rc = -EINVAL;
+ break;
+ }
+ if (!rc)
+ i_vpr_h(inst, "%s: target %d, r [%d, %d, %d, %d]\n",
+ __func__, s->target, s->r.top, s->r.left,
+ s->r.width, s->r.height);
+ return rc;
+}
+
+int msm_venc_g_selection(struct msm_vidc_inst *inst, struct v4l2_selection *s)
+{
+ int rc = 0;
+
+ if (s->type != INPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ i_vpr_e(inst, "%s: invalid type %d\n", __func__, s->type);
+ return -EINVAL;
+ }
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = inst->crop.left;
+ s->r.top = inst->crop.top;
+ s->r.width = inst->crop.width;
+ s->r.height = inst->crop.height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.left = inst->compose.left;
+ s->r.top = inst->compose.top;
+ s->r.width = inst->compose.width;
+ s->r.height = inst->compose.height;
+ break;
+ default:
+ i_vpr_e(inst, "%s: invalid target %d\n",
+ __func__, s->target);
+ rc = -EINVAL;
+ break;
+ }
+ if (!rc)
+ i_vpr_h(inst, "%s: target %d, r [%d, %d, %d, %d]\n",
+ __func__, s->target, s->r.top, s->r.left,
+ s->r.width, s->r.height);
+ return rc;
+}
+
+int msm_venc_s_param(struct msm_vidc_inst *inst,
+ struct v4l2_streamparm *s_parm)
+{
+ int rc = 0;
+ struct v4l2_fract *timeperframe = NULL;
+ u32 q16_rate, max_rate, default_rate;
+ u64 us_per_frame = 0, input_rate = 0;
+ bool is_frame_rate = false;
+
+ if (s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ timeperframe = &s_parm->parm.output.timeperframe;
+ max_rate = inst->capabilities[OPERATING_RATE].max >> 16;
+ default_rate = inst->capabilities[OPERATING_RATE].value >> 16;
+ s_parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ } else {
+ timeperframe = &s_parm->parm.capture.timeperframe;
+ is_frame_rate = true;
+ max_rate = inst->capabilities[FRAME_RATE].max >> 16;
+ default_rate = inst->capabilities[FRAME_RATE].value >> 16;
+ s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ }
+
+ if (!timeperframe->denominator || !timeperframe->numerator) {
+ i_vpr_e(inst, "%s: type %s, invalid rate, update with default\n",
+ __func__, v4l2_type_name(s_parm->type));
+ if (!timeperframe->numerator)
+ timeperframe->numerator = 1;
+ if (!timeperframe->denominator)
+ timeperframe->denominator = default_rate;
+ }
+
+ us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
+ do_div(us_per_frame, timeperframe->denominator);
+
+ if (!us_per_frame) {
+ i_vpr_e(inst, "%s: us_per_frame is zero\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ input_rate = (u64)USEC_PER_SEC;
+ do_div(input_rate, us_per_frame);
+
+ i_vpr_h(inst, "%s: type %s, %s value %llu\n",
+ __func__, v4l2_type_name(s_parm->type),
+ is_frame_rate ? "frame rate" : "operating rate", input_rate);
+
+ q16_rate = (u32)input_rate << 16;
+ msm_vidc_update_cap_value(inst, is_frame_rate ? FRAME_RATE : OPERATING_RATE,
+ q16_rate, __func__);
+ if ((s_parm->type == INPUT_MPLANE && inst->bufq[INPUT_PORT].vb2q->streaming) ||
+ (s_parm->type == OUTPUT_MPLANE && inst->bufq[OUTPUT_PORT].vb2q->streaming)) {
+ rc = msm_vidc_check_core_mbps(inst);
+ if (rc) {
+ i_vpr_e(inst, "%s: unsupported load\n", __func__);
+ goto reset_rate;
+ }
+ rc = input_rate > max_rate;
+ if (rc) {
+ i_vpr_e(inst, "%s: unsupported rate %llu, max %u\n", __func__,
+ input_rate, max_rate);
+ rc = -ENOMEM;
+ goto reset_rate;
+ }
+ }
+
+ if (is_frame_rate)
+ inst->capabilities[FRAME_RATE].flags |= CAP_FLAG_CLIENT_SET;
+ else
+ inst->capabilities[OPERATING_RATE].flags |= CAP_FLAG_CLIENT_SET;
+ /*
+ * In static case, frame rate is set via
+ * inst database set function mentioned in
+ * FRAME_RATE cap id.
+ * In dynamic case, frame rate is set like below.
+ */
+ if (inst->bufq[OUTPUT_PORT].vb2q->streaming) {
+ rc = venus_hfi_session_property(inst,
+ HFI_PROP_FRAME_RATE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_Q16,
+ &q16_rate,
+ sizeof(u32));
+ if (rc) {
+ i_vpr_e(inst,
+ "%s: failed to set frame rate to fw\n", __func__);
+ goto exit;
+ }
+ }
+
+ return 0;
+
+reset_rate:
+ if (rc) {
+ i_vpr_e(inst, "%s: setting rate %llu failed, reset to %u\n", __func__,
+ input_rate, default_rate);
+ msm_vidc_update_cap_value(inst, is_frame_rate ? FRAME_RATE : OPERATING_RATE,
+ default_rate << 16, __func__);
+ }
+exit:
+ return rc;
+}
+
+int msm_venc_g_param(struct msm_vidc_inst *inst,
+ struct v4l2_streamparm *s_parm)
+{
+ struct v4l2_fract *timeperframe = NULL;
+
+ if (s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ timeperframe = &s_parm->parm.output.timeperframe;
+ timeperframe->numerator = 1;
+ timeperframe->denominator =
+ inst->capabilities[OPERATING_RATE].value >> 16;
+ s_parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ } else {
+ timeperframe = &s_parm->parm.capture.timeperframe;
+ timeperframe->numerator = 1;
+ timeperframe->denominator =
+ inst->capabilities[FRAME_RATE].value >> 16;
+ s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ }
+
+ i_vpr_h(inst, "%s: type %s, num %u denom %u\n",
+ __func__, v4l2_type_name(s_parm->type), timeperframe->numerator,
+ timeperframe->denominator);
+ return 0;
+}
+
+int msm_venc_subscribe_event(struct msm_vidc_inst *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int rc = 0;
+
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ rc = v4l2_event_subscribe(&inst->fh, sub, MAX_EVENTS, NULL);
+ break;
+ case V4L2_EVENT_CTRL:
+ rc = v4l2_ctrl_subscribe_event(&inst->fh, sub);
+ break;
+ default:
+ i_vpr_e(inst, "%s: invalid type %d id %d\n", __func__, sub->type, sub->id);
+ return -EINVAL;
+ }
+
+ if (rc)
+ i_vpr_e(inst, "%s: failed, type %d id %d\n",
+ __func__, sub->type, sub->id);
+ return rc;
+}
+
+int msm_venc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+ u32 array[32] = {0};
+ u32 i = 0;
+
+ core = inst->core;
+
+ if (f->type == OUTPUT_MPLANE) {
+ u32 codecs = core->capabilities[ENC_CODECS].value;
+ u32 idx = 0;
+
+ for (i = 0; i <= 31; i++) {
+ if (codecs & BIT(i)) {
+ if (idx >= ARRAY_SIZE(array))
+ break;
+ array[idx] = codecs & BIT(i);
+ idx++;
+ }
+ }
+ if (!array[f->index])
+ return -EINVAL;
+ f->pixelformat = v4l2_codec_from_driver(inst, array[f->index],
+ __func__);
+ if (!f->pixelformat)
+ return -EINVAL;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strscpy(f->description, "codec", sizeof(f->description));
+ } else if (f->type == INPUT_MPLANE) {
+ u32 formats = inst->capabilities[PIX_FMTS].step_or_mask;
+ u32 idx = 0;
+
+ for (i = 0; i <= 31; i++) {
+ if (formats & BIT(i)) {
+ if (idx >= ARRAY_SIZE(array))
+ break;
+ array[idx] = formats & BIT(i);
+ idx++;
+ }
+ }
+ if (!array[f->index])
+ return -EINVAL;
+ f->pixelformat = v4l2_colorformat_from_driver(inst, array[f->index],
+ __func__);
+ if (!f->pixelformat)
+ return -EINVAL;
+ strscpy(f->description, "colorformat", sizeof(f->description));
+ }
+
+ memset(f->reserved, 0, sizeof(f->reserved));
+
+ i_vpr_h(inst, "%s: index %d, %s: %s, flags %#x\n",
+ __func__, f->index, f->description,
+ v4l2_pixelfmt_name(inst, f->pixelformat), f->flags);
+ return rc;
+}
+
+int msm_venc_inst_init(struct msm_vidc_inst *inst)
+{
+ int rc = 0;
+ struct msm_vidc_core *core;
+ struct v4l2_format *f;
+ enum msm_vidc_colorformat_type colorformat;
+
+ core = inst->core;
+
+ if (core->capabilities[DCVS].value)
+ inst->power.dcvs_mode = true;
+
+ f = &inst->fmts[OUTPUT_PORT];
+ f->type = OUTPUT_MPLANE;
+ f->fmt.pix_mp.width = DEFAULT_WIDTH;
+ f->fmt.pix_mp.height = DEFAULT_HEIGHT;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ call_session_op(core, buffer_size, inst, MSM_VIDC_BUF_OUTPUT);
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
+ f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+ inst->buffers.output.min_count =
+ call_session_op(core, min_count, inst, MSM_VIDC_BUF_OUTPUT);
+ inst->buffers.output.extra_count =
+ call_session_op(core, extra_count, inst, MSM_VIDC_BUF_OUTPUT);
+ inst->buffers.output.actual_count =
+ inst->buffers.output.min_count +
+ inst->buffers.output.extra_count;
+ inst->buffers.output.size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ inst->crop.left = 0;
+ inst->crop.top = 0;
+ inst->crop.width = f->fmt.pix_mp.width;
+ inst->crop.height = f->fmt.pix_mp.height;
+
+ inst->compose.left = 0;
+ inst->compose.top = 0;
+ inst->compose.width = f->fmt.pix_mp.width;
+ inst->compose.height = f->fmt.pix_mp.height;
+
+ f = &inst->fmts[INPUT_PORT];
+ f->type = INPUT_MPLANE;
+ f->fmt.pix_mp.pixelformat =
+ v4l2_colorformat_from_driver(inst, MSM_VIDC_FMT_NV12C, __func__);
+ f->fmt.pix_mp.width = DEFAULT_WIDTH;
+ f->fmt.pix_mp.height = DEFAULT_HEIGHT;
+ f->fmt.pix_mp.num_planes = 1;
+ colorformat = v4l2_colorformat_to_driver(inst, f->fmt.pix_mp.pixelformat,
+ __func__);
+ f->fmt.pix_mp.plane_fmt[0].bytesperline =
+ video_y_stride_bytes(colorformat, DEFAULT_WIDTH);
+ f->fmt.pix_mp.plane_fmt[0].sizeimage =
+ call_session_op(core, buffer_size, inst, MSM_VIDC_BUF_INPUT);
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
+ f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+ inst->buffers.input.min_count =
+ call_session_op(core, min_count, inst, MSM_VIDC_BUF_INPUT);
+ inst->buffers.input.extra_count =
+ call_session_op(core, extra_count, inst, MSM_VIDC_BUF_INPUT);
+ inst->buffers.input.actual_count =
+ inst->buffers.input.min_count +
+ inst->buffers.input.extra_count;
+ inst->buffers.input.size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ inst->hfi_rc_type = HFI_RC_VBR_CFR;
+ inst->hfi_layer_type = HFI_HIER_P_SLIDING_WINDOW;
+
+ rc = msm_venc_codec_change(inst,
+ inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat);
+
+ return rc;
+}
+
+int msm_venc_inst_deinit(struct msm_vidc_inst *inst)
+{
+ return msm_vidc_ctrl_handler_deinit(inst);
+}
--
2.7.4


2023-07-28 16:17:56

by Bryan O'Donoghue

[permalink] [raw]
Subject: Re: [PATCH 18/33] iris: vidc: hfi: add Host Firmware Interface (HFI)

On 28/07/2023 14:23, Vikash Garodia wrote:
> + rc = hfi_packet_sys_intraframe_powercollapse(core, core->packet,
> + core->packet_size, enable);
> + if (rc)
> + return rc;

I'm 99.9999999999 % sure this is misnamed.

"Inter" means in-between two things.
"Intra" means inside of one thing.

So "intraframe" means inside of one frame "interframe" would mean power
collapsing in-between two frames, which is what I think this does.

And I'd still rather be adding inter-frame power-collapse to as many
different versions of the existing silicon and new silicon as opposed to
segregating it off in a new driver.

I'm assuming that more than sm8550 supports it since @ the end of the
day this is a firmware feature to power-collapse during an active
session when we aren't busy.

---
bod

2023-07-28 16:19:29

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH 00/33] Qualcomm video decoder/encoder driver

On 28/07/2023 16:23, Vikash Garodia wrote:
> This patch series introduces support for Qualcomm new video acceleration
> hardware architecture, used for video stream decoding/encoding. This driver
> is based on new communication protocol between video hardware and application
> processor.
>
> This driver comes with below capabilities:
> - V4L2 complaint video driver with M2M and STREAMING capability.
> - Supports H264, H265, VP9 decoders.
> - Supports H264, H265 encoders.

Please describe, why is it impossible to support this hardware in the
venus driver. We do not usually add new drivers for the new generations
of the hardware, unless it is fully incompatible with the previous
generations. Let me point you to camss or drm/msm drivers. They have
successfully solved the issue of supporting multiple generations of the
hardware in the same driver.

Unless the "iris3" is completely different from all the previous
generations, I strongly suggest spending time on restructuring existing
venus driver and then adding support for the new hardware there instead
of dumping out something completely new.

>
> This driver comes with below features:
> - Centralized resource and memory management.
> - Centralized management of core and instance states.
> - Defines platform specific capabilities and features. As a results, it provides
> a single point of control to enable/disable a given feature depending on
> specific platform capabilities.
> - Handles hardware interdependent configurations. For a given configuration from
> client, the driver checks for hardware dependent configuration/s and updates
> the same.
> - Handles multiple complex video scenarios involving state transitions - DRC,
> Drain, Seek, back to back DRC, DRC during Drain sequence, DRC during Seek
> sequence.
> - Introduces a flexible way for driver to subscribe for any property with
> hardware. Hardware would inform driver with those subscribed property with any
> change in value.
> - Introduces performance (clock and bus) model based on new hardware
> architecture.
> - Introduces multi thread safe design to handle communication between client and
> hardware.
> - Adapts encoder quality improvements available in new hardware architecture.
> - Implements asynchronous communication with hardware to achieve better
> experience in low latency usecases.
> - Supports multi stage hardware architecture for encode/decode.
> - Output and capture planes are controlled independently. Thereby providing a
> way to reconfigure individual plane.
> - Hardware packetization layer supports synchronization between configuration
> packet and data packet.
> - Introduces a flexibility to receive a hardware response for a given command
> packet.
> - Native hardware support of LAST flag which is mandatory to align with port
> reconfiguration and DRAIN sequence as per V4L guidelines.
> - Native hardware support for drain sequence.
>
> I think that the driver is in good shape for mainline kernel, and I hope the
> review comments will help to improve it, so please do review, and make comments.
>
> Dikshita Agarwal (17):
> iris: vidc: add core functions
> iris: add vidc wrapper file
> iris: vidc: add vb2 ops
> iris: vidc: add helpers for memory management
> iris: vidc: add helper functions for resource management
> iris: vidc: add helper functions for power management
> iris: add helpers for media format
> iris: vidc: add PIL functionality for video firmware
> iris: platform: add platform files
> iris: platform: sm8550: add capability file for sm8550
> iris: variant: add helper functions for register handling
> iris: variant: iris3: add iris3 specific ops
> iris: variant: iris3: add helpers for buffer size calculations
> iris: variant: iris3: add helper for bus and clock calculation
> iris: variant: iris: implement the logic to compute bus bandwidth
> iris: variant: iris3: implement logic to compute clock frequency
> iris: enable building of iris video driver
>
> Vikash Garodia (16):
> MAINTAINERS: Add Qualcomm Iris video accelerator driver
> iris: vidc: add v4l2 wrapper file
> iris: vidc: define video core and instance context
> iris: iris: add video encoder files
> iris: vidc: add video decoder files
> iris: vidc: add control files
> iris: vidc: add helper functions
> iris: vidc: add helpers for state management
> iris: add vidc buffer files
> iris: vidc: define various structures and enum
> iris: vidc: hfi: add Host Firmware Interface (HFI)
> iris: vidc: hfi: add Host Firmware Interface (HFI) response handling
> iris: vidc: hfi: add helpers for handling shared queues
> iris: vidc: hfi: Add packetization layer
> iris: vidc: hfi: defines HFI properties and enums
> iris: vidc: add debug files
>
> MAINTAINERS | 10 +
> drivers/media/platform/qcom/Kconfig | 1 +
> drivers/media/platform/qcom/Makefile | 1 +
> drivers/media/platform/qcom/iris/Kconfig | 15 +
> drivers/media/platform/qcom/iris/Makefile | 46 +
> .../iris/platform/common/inc/msm_vidc_platform.h | 305 ++
> .../iris/platform/common/src/msm_vidc_platform.c | 2499 ++++++++++++
> .../iris/platform/sm8550/inc/msm_vidc_sm8550.h | 14 +
> .../iris/platform/sm8550/src/msm_vidc_sm8550.c | 1727 ++++++++
> .../iris/variant/common/inc/msm_vidc_variant.h | 22 +
> .../iris/variant/common/src/msm_vidc_variant.c | 163 +
> .../qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h | 1481 +++++++
> .../iris/variant/iris3/inc/msm_vidc_buffer_iris3.h | 19 +
> .../qcom/iris/variant/iris3/inc/msm_vidc_iris3.h | 15 +
> .../iris/variant/iris3/inc/msm_vidc_power_iris3.h | 17 +
> .../iris/variant/iris3/inc/perf_static_model.h | 229 ++
> .../iris/variant/iris3/src/msm_vidc_buffer_iris3.c | 595 +++
> .../iris/variant/iris3/src/msm_vidc_bus_iris3.c | 884 ++++
> .../iris/variant/iris3/src/msm_vidc_clock_iris3.c | 627 +++
> .../qcom/iris/variant/iris3/src/msm_vidc_iris3.c | 954 +++++
> .../iris/variant/iris3/src/msm_vidc_power_iris3.c | 345 ++
> .../media/platform/qcom/iris/vidc/inc/firmware.h | 18 +
> .../platform/qcom/iris/vidc/inc/hfi_command.h | 190 +
> .../media/platform/qcom/iris/vidc/inc/hfi_packet.h | 52 +
> .../platform/qcom/iris/vidc/inc/hfi_property.h | 666 +++
> .../platform/qcom/iris/vidc/inc/msm_media_info.h | 599 +++
> .../media/platform/qcom/iris/vidc/inc/msm_vdec.h | 40 +
> .../media/platform/qcom/iris/vidc/inc/msm_venc.h | 34 +
> .../media/platform/qcom/iris/vidc/inc/msm_vidc.h | 60 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_buffer.h | 32 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_control.h | 26 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_core.h | 165 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_debug.h | 186 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_driver.h | 352 ++
> .../platform/qcom/iris/vidc/inc/msm_vidc_inst.h | 207 +
> .../qcom/iris/vidc/inc/msm_vidc_internal.h | 787 ++++
> .../platform/qcom/iris/vidc/inc/msm_vidc_memory.h | 83 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_power.h | 94 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_state.h | 102 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h | 77 +
> .../platform/qcom/iris/vidc/inc/msm_vidc_vb2.h | 39 +
> .../media/platform/qcom/iris/vidc/inc/resources.h | 259 ++
> .../media/platform/qcom/iris/vidc/inc/venus_hfi.h | 66 +
> .../platform/qcom/iris/vidc/inc/venus_hfi_queue.h | 89 +
> .../qcom/iris/vidc/inc/venus_hfi_response.h | 26 +
> .../media/platform/qcom/iris/vidc/src/firmware.c | 294 ++
> .../media/platform/qcom/iris/vidc/src/hfi_packet.c | 657 +++
> .../media/platform/qcom/iris/vidc/src/msm_vdec.c | 2091 ++++++++++
> .../media/platform/qcom/iris/vidc/src/msm_venc.c | 1484 +++++++
> .../media/platform/qcom/iris/vidc/src/msm_vidc.c | 841 ++++
> .../platform/qcom/iris/vidc/src/msm_vidc_buffer.c | 290 ++
> .../platform/qcom/iris/vidc/src/msm_vidc_control.c | 824 ++++
> .../platform/qcom/iris/vidc/src/msm_vidc_debug.c | 581 +++
> .../platform/qcom/iris/vidc/src/msm_vidc_driver.c | 4276 ++++++++++++++++++++
> .../platform/qcom/iris/vidc/src/msm_vidc_memory.c | 448 ++
> .../platform/qcom/iris/vidc/src/msm_vidc_power.c | 560 +++
> .../platform/qcom/iris/vidc/src/msm_vidc_probe.c | 660 +++
> .../platform/qcom/iris/vidc/src/msm_vidc_state.c | 1607 ++++++++
> .../platform/qcom/iris/vidc/src/msm_vidc_v4l2.c | 953 +++++
> .../platform/qcom/iris/vidc/src/msm_vidc_vb2.c | 605 +++
> .../media/platform/qcom/iris/vidc/src/resources.c | 1321 ++++++
> .../media/platform/qcom/iris/vidc/src/venus_hfi.c | 1503 +++++++
> .../platform/qcom/iris/vidc/src/venus_hfi_queue.c | 537 +++
> .../qcom/iris/vidc/src/venus_hfi_response.c | 1607 ++++++++
> 64 files changed, 35357 insertions(+)
> create mode 100644 drivers/media/platform/qcom/iris/Kconfig
> create mode 100644 drivers/media/platform/qcom/iris/Makefile
> create mode 100644 drivers/media/platform/qcom/iris/platform/common/inc/msm_vidc_platform.h
> create mode 100644 drivers/media/platform/qcom/iris/platform/common/src/msm_vidc_platform.c
> create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/inc/msm_vidc_sm8550.h
> create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/src/msm_vidc_sm8550.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_buffer_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/perf_static_model.h
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_buffer_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_bus_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_clock_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/firmware.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_command.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_packet.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_property.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_media_info.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vdec.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_buffer.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_control.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_core.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_driver.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_inst.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_memory.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_power.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/resources.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_queue.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/firmware.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/hfi_packet.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vdec.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_venc.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_buffer.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_control.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_driver.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_memory.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_power.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_probe.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_v4l2.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/resources.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_queue.c
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c
>

--
With best wishes
Dmitry


2023-07-28 16:39:52

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 28/33] iris: variant: iris3: add iris3 specific ops

From: Dikshita Agarwal <[email protected]>

This implements iris3 specific ops for power on, power off,
boot firmware, power collapse etc.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../qcom/iris/variant/iris3/inc/msm_vidc_iris3.h | 15 +
.../qcom/iris/variant/iris3/src/msm_vidc_iris3.c | 954 +++++++++++++++++++++
2 files changed, 969 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c

diff --git a/drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h b/drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h
new file mode 100644
index 0000000..704367e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _MSM_VIDC_IRIS3_H_
+#define _MSM_VIDC_IRIS3_H_
+
+#include "msm_vidc_core.h"
+
+int msm_vidc_init_iris3(struct msm_vidc_core *core);
+int msm_vidc_adjust_bitrate_boost_iris3(void *instance, struct v4l2_ctrl *ctrl);
+
+#endif // _MSM_VIDC_IRIS3_H_
diff --git a/drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c b/drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c
new file mode 100644
index 0000000..95dff62
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c
@@ -0,0 +1,954 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "msm_vidc_buffer.h"
+#include "msm_vidc_buffer_iris3.h"
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_inst.h"
+#include "msm_vidc_internal.h"
+#include "msm_vidc_iris3.h"
+#include "msm_vidc_platform.h"
+#include "msm_vidc_power_iris3.h"
+#include "msm_vidc_state.h"
+#include "msm_vidc_variant.h"
+#include "venus_hfi.h"
+
+#define VIDEO_ARCH_LX 1
+
+#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_VCICMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x20)
+#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_VCICMDARG2_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x2C)
+#define CPU_CS_VCICMDARG3_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x30)
+#define CPU_CS_VMIMSG_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x34)
+#define CPU_CS_VMIMSGAG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x38)
+#define CPU_CS_VMIMSGAG1_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x3C)
+#define CPU_CS_SCIACMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x48)
+#define CPU_CS_H2XSOFTINTEN_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x148)
+
+/* HFI_CTRL_STATUS */
+#define CPU_CS_SCIACMDARG0_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x4C)
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK_IRIS3 0xfe
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY_IRIS3 0x100
+#define CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK_IRIS3 0x40000000
+
+/* HFI_QTBL_INFO */
+#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)
+
+/* HFI_VERSION_INFO */
+#define CPU_CS_SCIACMDARG3_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x58)
+
+/* SFR_ADDR */
+#define CPU_CS_SCIBCMD_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x5C)
+
+/* MMAP_ADDR */
+#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_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS_IRIS3 + 0x160)
+#define CPU_CS_AHB_BRIDGE_SYNC_RESET_STATUS (CPU_CS_BASE_OFFS_IRIS3 + 0x164)
+
+/* FAL10 Feature Control */
+#define CPU_CS_X2RPMH_IRIS3 (CPU_CS_BASE_OFFS_IRIS3 + 0x168)
+#define CPU_CS_X2RPMH_MASK0_BMSK_IRIS3 0x1
+#define CPU_CS_X2RPMH_MASK0_SHFT_IRIS3 0x0
+#define CPU_CS_X2RPMH_MASK1_BMSK_IRIS3 0x2
+#define CPU_CS_X2RPMH_MASK1_SHFT_IRIS3 0x1
+#define CPU_CS_X2RPMH_SWOVERRIDE_BMSK_IRIS3 0x4
+#define CPU_CS_X2RPMH_SWOVERRIDE_SHFT_IRIS3 0x3
+
+#define CPU_IC_SOFTINT_IRIS3 (CPU_IC_BASE_OFFS_IRIS3 + 0x150)
+#define CPU_IC_SOFTINT_H2A_SHFT_IRIS3 0x0
+
+/*
+ * --------------------------------------------------------------------------
+ * MODULE: AON_MVP_NOC_RESET_REGISTERS
+ * --------------------------------------------------------------------------
+ */
+#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
+#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004)
+
+/*
+ * --------------------------------------------------------------------------
+ * MODULE: wrapper
+ * --------------------------------------------------------------------------
+ */
+#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_CPU_CLOCK_CONFIG_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x2000)
+#define WRAPPER_CPU_CGC_DIS_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x2010)
+#define WRAPPER_CPU_STATUS_IRIS3 (WRAPPER_BASE_OFFS_IRIS3 + 0x2014)
+
+#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)
+
+/*
+ * --------------------------------------------------------------------------
+ * MODULE: tz_wrapper
+ * --------------------------------------------------------------------------
+ */
+#define WRAPPER_TZ_BASE_OFFS 0x000C0000
+#define WRAPPER_TZ_CPU_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS)
+#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 VERSION_INFO_IRIS3 CPU_CS_SCIACMDARG3_IRIS3
+
+#define SFR_ADDR_IRIS3 CPU_CS_SCIBCMD_IRIS3
+#define MMAP_ADDR_IRIS3 CPU_CS_SCIBCMDARG0_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)
+
+/*
+ * --------------------------------------------------------------------------
+ * MODULE: VCODEC_SS registers
+ * --------------------------------------------------------------------------
+ */
+#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS_IRIS3 + 0x70)
+
+/*
+ * --------------------------------------------------------------------------
+ * MODULE: vcodec noc error log registers (iris3)
+ * --------------------------------------------------------------------------
+ */
+#define VCODEC_NOC_VIDEO_A_NOC_BASE_OFFS 0x00010000
+#define VCODEC_NOC_ERL_MAIN_SWID_LOW 0x00011200
+#define VCODEC_NOC_ERL_MAIN_SWID_HIGH 0x00011204
+#define VCODEC_NOC_ERL_MAIN_MAINCTL_LOW 0x00011208
+#define VCODEC_NOC_ERL_MAIN_ERRVLD_LOW 0x00011210
+#define VCODEC_NOC_ERL_MAIN_ERRCLR_LOW 0x00011218
+#define VCODEC_NOC_ERL_MAIN_ERRLOG0_LOW 0x00011220
+#define VCODEC_NOC_ERL_MAIN_ERRLOG0_HIGH 0x00011224
+#define VCODEC_NOC_ERL_MAIN_ERRLOG1_LOW 0x00011228
+#define VCODEC_NOC_ERL_MAIN_ERRLOG1_HIGH 0x0001122C
+#define VCODEC_NOC_ERL_MAIN_ERRLOG2_LOW 0x00011230
+#define VCODEC_NOC_ERL_MAIN_ERRLOG2_HIGH 0x00011234
+#define VCODEC_NOC_ERL_MAIN_ERRLOG3_LOW 0x00011238
+#define VCODEC_NOC_ERL_MAIN_ERRLOG3_HIGH 0x0001123C
+
+static int __interrupt_init_iris3(struct msm_vidc_core *core)
+{
+ u32 mask_val = 0;
+ int rc = 0;
+
+ /* All interrupts should be disabled initially 0x1F6 : Reset value */
+ rc = __read_register(core, WRAPPER_INTR_MASK_IRIS3, &mask_val);
+ if (rc)
+ return rc;
+
+ /* Write 0 to unmask CPU and WD interrupts */
+ mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK_IRIS3 |
+ WRAPPER_INTR_MASK_A2HCPU_BMSK_IRIS3);
+ rc = __write_register(core, WRAPPER_INTR_MASK_IRIS3, mask_val);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int __setup_ucregion_memory_map_iris3(struct msm_vidc_core *core)
+{
+ u32 value;
+ int rc = 0;
+
+ value = (u32)core->iface_q_table.align_device_addr;
+ rc = __write_register(core, UC_REGION_ADDR_IRIS3, value);
+ if (rc)
+ return rc;
+
+ value = SHARED_QSIZE;
+ rc = __write_register(core, UC_REGION_SIZE_IRIS3, value);
+ if (rc)
+ return rc;
+
+ value = (u32)core->iface_q_table.align_device_addr;
+ rc = __write_register(core, QTBL_ADDR_IRIS3, value);
+ if (rc)
+ return rc;
+
+ rc = __write_register(core, QTBL_INFO_IRIS3, 0x01);
+ if (rc)
+ return rc;
+
+ /* update queues vaddr for debug purpose */
+ value = (u32)((u64)core->iface_q_table.align_virtual_addr);
+ rc = __write_register(core, CPU_CS_VCICMDARG0_IRIS3, value);
+ if (rc)
+ return rc;
+
+ value = (u32)((u64)core->iface_q_table.align_virtual_addr >> 32);
+ rc = __write_register(core, CPU_CS_VCICMDARG1_IRIS3, value);
+ if (rc)
+ return rc;
+
+ if (core->sfr.align_device_addr) {
+ value = (u32)core->sfr.align_device_addr + VIDEO_ARCH_LX;
+ rc = __write_register(core, SFR_ADDR_IRIS3, value);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static bool is_iris3_hw_power_collapsed(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ u32 value = 0, pwr_status = 0;
+
+ rc = __read_register(core, WRAPPER_CORE_POWER_STATUS, &value);
+ if (rc)
+ return false;
+
+ /* if BIT(1) is 1 then video hw power is on else off */
+ pwr_status = value & BIT(1);
+ return pwr_status ? false : true;
+}
+
+static int __power_off_iris3_hardware(struct msm_vidc_core *core)
+{
+ int rc = 0, i;
+ u32 value = 0;
+ bool pwr_collapsed = false;
+
+ /*
+ * Incase hw power control is enabled, for both CPU WD, video
+ * hw unresponsive cases, check for power status to decide on
+ * executing NOC reset sequence before disabling power. If there
+ * is no CPU WD and hw power control is enabled, fw is expected
+ * to power collapse video hw always.
+ */
+ if (is_core_sub_state(core, CORE_SUBSTATE_FW_PWR_CTRL)) {
+ pwr_collapsed = is_iris3_hw_power_collapsed(core);
+ if (is_core_sub_state(core, CORE_SUBSTATE_CPU_WATCHDOG) ||
+ is_core_sub_state(core, CORE_SUBSTATE_VIDEO_UNRESPONSIVE)) {
+ if (pwr_collapsed) {
+ d_vpr_e("%s: video hw power collapsed %s\n",
+ __func__, core->sub_state_name);
+ goto disable_power;
+ } else {
+ d_vpr_e("%s: video hw is power ON %s\n",
+ __func__, core->sub_state_name);
+ }
+ } else {
+ if (!pwr_collapsed)
+ d_vpr_e("%s: video hw is not power collapsed\n", __func__);
+
+ d_vpr_h("%s: disabling hw power\n", __func__);
+ goto disable_power;
+ }
+ }
+
+ /*
+ * check to make sure core clock branch enabled else
+ * we cannot read vcodec top idle register
+ */
+ rc = __read_register(core, WRAPPER_CORE_CLOCK_CONFIG_IRIS3, &value);
+ if (rc)
+ return rc;
+
+ if (value) {
+ d_vpr_h("%s: core clock config not enabled, enabling it to read vcodec registers\n",
+ __func__);
+ rc = __write_register(core, WRAPPER_CORE_CLOCK_CONFIG_IRIS3, 0);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * add MNoC idle check before collapsing MVS0 per HPG update
+ * poll for NoC DMA idle -> HPG 6.1.1
+ */
+ for (i = 0; i < core->capabilities[NUM_VPP_PIPE].value; i++) {
+ rc = __read_register_with_poll_timeout(core, VCODEC_SS_IDLE_STATUSN + 4 * i,
+ 0x400000, 0x400000, 2000, 20000);
+ if (rc)
+ d_vpr_h("%s: VCODEC_SS_IDLE_STATUSN (%d) is not idle (%#x)\n",
+ __func__, i, value);
+ }
+
+ /* Apply partial reset on MSF interface and wait for ACK */
+ rc = __write_register(core, AON_WRAPPER_MVP_NOC_RESET_REQ, 0x3);
+ if (rc)
+ return rc;
+
+ rc = __read_register_with_poll_timeout(core, AON_WRAPPER_MVP_NOC_RESET_ACK,
+ 0x3, 0x3, 200, 2000);
+ if (rc)
+ d_vpr_h("%s: AON_WRAPPER_MVP_NOC_RESET assert failed\n", __func__);
+
+ /* De-assert partial reset on MSF interface and wait for ACK */
+ rc = __write_register(core, AON_WRAPPER_MVP_NOC_RESET_REQ, 0x0);
+ if (rc)
+ return rc;
+
+ rc = __read_register_with_poll_timeout(core, AON_WRAPPER_MVP_NOC_RESET_ACK,
+ 0x3, 0x0, 200, 2000);
+ if (rc)
+ d_vpr_h("%s: AON_WRAPPER_MVP_NOC_RESET de-assert failed\n", __func__);
+
+ /*
+ * Reset both sides of 2 ahb2ahb_bridges (TZ and non-TZ)
+ * do we need to check status register here?
+ */
+ rc = __write_register(core, CPU_CS_AHB_BRIDGE_SYNC_RESET, 0x3);
+ if (rc)
+ return rc;
+ rc = __write_register(core, CPU_CS_AHB_BRIDGE_SYNC_RESET, 0x2);
+ if (rc)
+ return rc;
+ rc = __write_register(core, CPU_CS_AHB_BRIDGE_SYNC_RESET, 0x0);
+ if (rc)
+ return rc;
+
+disable_power:
+ /* power down process */
+ rc = call_res_op(core, gdsc_off, core, "vcodec");
+ if (rc) {
+ d_vpr_e("%s: disable regulator vcodec failed\n", __func__);
+ rc = 0;
+ }
+
+ rc = call_res_op(core, clk_disable, core, "vcodec_clk");
+ if (rc) {
+ d_vpr_e("%s: disable unprepare vcodec_clk failed\n", __func__);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int __power_off_iris3_controller(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ /*
+ * mask fal10_veto QLPAC error since fal10_veto can go 1
+ * when pwwait == 0 and clamped to 0 -> HPG 6.1.2
+ */
+ rc = __write_register(core, CPU_CS_X2RPMH_IRIS3, 0x3);
+ if (rc)
+ return rc;
+
+ /* set MNoC to low power, set PD_NOC_QREQ (bit 0) */
+ rc = __write_register_masked(core, AON_WRAPPER_MVP_NOC_LPI_CONTROL,
+ 0x1, BIT(0));
+ if (rc)
+ return rc;
+
+ rc = __read_register_with_poll_timeout(core, AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ 0x1, 0x1, 200, 2000);
+ if (rc)
+ d_vpr_h("%s: AON_WRAPPER_MVP_NOC_LPI_CONTROL failed\n", __func__);
+
+ /* Set Iris CPU NoC to Low power */
+ rc = __write_register_masked(core, WRAPPER_IRIS_CPU_NOC_LPI_CONTROL,
+ 0x1, BIT(0));
+ if (rc)
+ return rc;
+
+ rc = __read_register_with_poll_timeout(core, WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ 0x1, 0x1, 200, 2000);
+ if (rc)
+ d_vpr_h("%s: WRAPPER_IRIS_CPU_NOC_LPI_CONTROL failed\n", __func__);
+
+ /* Debug bridge LPI release */
+ rc = __write_register(core, WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_IRIS3, 0x0);
+ if (rc)
+ return rc;
+
+ rc = __read_register_with_poll_timeout(core, WRAPPER_DEBUG_BRIDGE_LPI_STATUS_IRIS3,
+ 0xffffffff, 0x0, 200, 2000);
+ if (rc)
+ d_vpr_h("%s: debug bridge release failed\n", __func__);
+
+ /* Reset MVP QNS4PDXFIFO */
+ rc = __write_register(core, WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG, 0x3);
+ if (rc)
+ return rc;
+
+ rc = __write_register(core, WRAPPER_TZ_QNS4PDXFIFO_RESET, 0x1);
+ if (rc)
+ return rc;
+
+ rc = __write_register(core, WRAPPER_TZ_QNS4PDXFIFO_RESET, 0x0);
+ if (rc)
+ return rc;
+
+ rc = __write_register(core, WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG, 0x0);
+ if (rc)
+ return rc;
+
+ /* Turn off MVP MVS0C core clock */
+ rc = call_res_op(core, clk_disable, core, "core_clk");
+ if (rc) {
+ d_vpr_e("%s: disable unprepare core_clk failed\n", __func__);
+ rc = 0;
+ }
+
+ /* power down process */
+ rc = call_res_op(core, gdsc_off, core, "iris-ctl");
+ if (rc) {
+ d_vpr_e("%s: disable regulator iris-ctl failed\n", __func__);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int __power_off_iris3(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE))
+ return 0;
+
+ rc = call_res_op(core, set_clks, core, 0);
+ if (rc)
+ d_vpr_e("%s: resetting clocks failed\n", __func__);
+
+ if (__power_off_iris3_hardware(core))
+ d_vpr_e("%s: failed to power off hardware\n", __func__);
+
+ if (__power_off_iris3_controller(core))
+ d_vpr_e("%s: failed to power off controller\n", __func__);
+
+ rc = call_res_op(core, set_bw, core, 0, 0);
+ if (rc)
+ d_vpr_e("%s: failed to unvote buses\n", __func__);
+
+ if (!call_iris_op(core, watchdog, core, core->intr_status))
+ disable_irq_nosync(core->resource->irq);
+
+ msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE, 0, __func__);
+
+ return rc;
+}
+
+static int __power_on_iris3_controller(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = call_res_op(core, gdsc_on, core, "iris-ctl");
+ if (rc)
+ goto fail_regulator;
+
+ rc = call_res_op(core, reset_bridge, core);
+ if (rc)
+ goto fail_reset_ahb2axi;
+
+ rc = call_res_op(core, clk_enable, core, "gcc_video_axi0");
+ if (rc)
+ goto fail_clk_axi;
+
+ rc = call_res_op(core, clk_enable, core, "core_clk");
+ if (rc)
+ goto fail_clk_controller;
+
+ return 0;
+
+fail_clk_controller:
+ call_res_op(core, clk_disable, core, "gcc_video_axi0");
+fail_clk_axi:
+fail_reset_ahb2axi:
+ call_res_op(core, gdsc_off, core, "iris-ctl");
+fail_regulator:
+ return rc;
+}
+
+static int __power_on_iris3_hardware(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = call_res_op(core, gdsc_on, core, "vcodec");
+ if (rc)
+ goto fail_regulator;
+
+ rc = call_res_op(core, clk_enable, core, "vcodec_clk");
+ if (rc)
+ goto fail_clk_controller;
+
+ return 0;
+
+fail_clk_controller:
+ call_res_op(core, gdsc_off, core, "vcodec");
+fail_regulator:
+ return rc;
+}
+
+static int __power_on_iris3(struct msm_vidc_core *core)
+{
+ struct frequency_table *freq_tbl;
+ u32 freq = 0;
+ int rc = 0;
+
+ if (is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE))
+ return 0;
+
+ if (!core_in_valid_state(core)) {
+ d_vpr_e("%s: invalid core state %s\n",
+ __func__, core_state_name(core->state));
+ return -EINVAL;
+ }
+
+ /* Vote for all hardware resources */
+ rc = call_res_op(core, set_bw, core, INT_MAX, INT_MAX);
+ if (rc) {
+ d_vpr_e("%s: failed to vote buses, rc %d\n", __func__, rc);
+ goto fail_vote_buses;
+ }
+
+ rc = __power_on_iris3_controller(core);
+ if (rc) {
+ d_vpr_e("%s: failed to power on iris3 controller\n", __func__);
+ goto fail_power_on_controller;
+ }
+
+ rc = __power_on_iris3_hardware(core);
+ if (rc) {
+ d_vpr_e("%s: failed to power on iris3 hardware\n", __func__);
+ goto fail_power_on_hardware;
+ }
+ /* video controller and hardware powered on successfully */
+ rc = msm_vidc_change_core_sub_state(core, 0, CORE_SUBSTATE_POWER_ENABLE, __func__);
+ if (rc)
+ goto fail_power_on_substate;
+
+ freq_tbl = core->resource->freq_set.freq_tbl;
+ freq = core->power.clk_freq ? core->power.clk_freq :
+ freq_tbl[0].freq;
+
+ rc = call_res_op(core, set_clks, core, freq);
+ if (rc) {
+ d_vpr_e("%s: failed to scale clocks\n", __func__);
+ rc = 0;
+ }
+ /*
+ * Re-program all of the registers that get reset as a result of
+ * regulator_disable() and _enable()
+ */
+ __set_registers(core);
+
+ __interrupt_init_iris3(core);
+ core->intr_status = 0;
+ enable_irq(core->resource->irq);
+
+ return rc;
+
+fail_power_on_substate:
+ __power_off_iris3_hardware(core);
+fail_power_on_hardware:
+ __power_off_iris3_controller(core);
+fail_power_on_controller:
+ call_res_op(core, set_bw, core, 0, 0);
+fail_vote_buses:
+ msm_vidc_change_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE, 0, __func__);
+ return rc;
+}
+
+static int __prepare_pc_iris3(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ u32 wfi_status = 0, idle_status = 0, pc_ready = 0;
+ u32 ctrl_status = 0;
+
+ rc = __read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
+ if (rc)
+ return rc;
+
+ pc_ready = ctrl_status & CTRL_STATUS_PC_READY_IRIS3;
+ idle_status = ctrl_status & BIT(30);
+
+ if (pc_ready) {
+ d_vpr_h("Already in pc_ready state\n");
+ return 0;
+ }
+ rc = __read_register(core, WRAPPER_TZ_CPU_STATUS, &wfi_status);
+ if (rc)
+ return rc;
+
+ wfi_status &= BIT(0);
+ if (!wfi_status || !idle_status) {
+ d_vpr_e("Skipping PC, wfi status not set\n");
+ goto skip_power_off;
+ }
+
+ rc = __prepare_pc(core);
+ if (rc) {
+ d_vpr_e("Failed __prepare_pc %d\n", rc);
+ goto skip_power_off;
+ }
+
+ rc = __read_register_with_poll_timeout(core, CTRL_STATUS_IRIS3,
+ CTRL_STATUS_PC_READY_IRIS3,
+ CTRL_STATUS_PC_READY_IRIS3, 250, 2500);
+ if (rc) {
+ d_vpr_e("%s: Skip PC. Ctrl status not set\n", __func__);
+ goto skip_power_off;
+ }
+
+ rc = __read_register_with_poll_timeout(core, WRAPPER_TZ_CPU_STATUS,
+ BIT(0), 0x1, 250, 2500);
+ if (rc) {
+ d_vpr_e("%s: Skip PC. Wfi status not set\n", __func__);
+ goto skip_power_off;
+ }
+ return rc;
+
+skip_power_off:
+ rc = __read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
+ if (rc)
+ return rc;
+ rc = __read_register(core, WRAPPER_TZ_CPU_STATUS, &wfi_status);
+ if (rc)
+ return rc;
+ wfi_status &= BIT(0);
+ d_vpr_e("Skip PC, wfi=%#x, idle=%#x, pcr=%#x, ctrl=%#x)\n",
+ wfi_status, idle_status, pc_ready, ctrl_status);
+ return -EAGAIN;
+}
+
+static int __raise_interrupt_iris3(struct msm_vidc_core *core)
+{
+ int rc = 0;
+
+ rc = __write_register(core, CPU_IC_SOFTINT_IRIS3, 1 << CPU_IC_SOFTINT_H2A_SHFT_IRIS3);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int __watchdog_iris3(struct msm_vidc_core *core, u32 intr_status)
+{
+ int rc = 0;
+
+ if (intr_status & WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3) {
+ d_vpr_e("%s: received watchdog interrupt\n", __func__);
+ rc = 1;
+ }
+
+ return rc;
+}
+
+static int __clear_interrupt_iris3(struct msm_vidc_core *core)
+{
+ u32 intr_status = 0, mask = 0;
+ int rc = 0;
+
+ rc = __read_register(core, WRAPPER_INTR_STATUS_IRIS3, &intr_status);
+ if (rc)
+ return rc;
+
+ mask = (WRAPPER_INTR_STATUS_A2H_BMSK_IRIS3 |
+ WRAPPER_INTR_STATUS_A2HWD_BMSK_IRIS3 |
+ CTRL_INIT_IDLE_MSG_BMSK_IRIS3);
+
+ if (intr_status & mask) {
+ core->intr_status |= intr_status;
+ core->reg_count++;
+ d_vpr_l("INTERRUPT: times: %d interrupt_status: %d\n",
+ core->reg_count, intr_status);
+ } else {
+ core->spur_count++;
+ }
+
+ rc = __write_register(core, CPU_CS_A2HSOFTINTCLR_IRIS3, 1);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int __boot_firmware_iris3(struct msm_vidc_core *core)
+{
+ int rc = 0;
+ u32 ctrl_init_val = 0, ctrl_status = 0, count = 0, max_tries = 1000;
+
+ rc = __setup_ucregion_memory_map_iris3(core);
+ if (rc)
+ return rc;
+
+ ctrl_init_val = BIT(0);
+
+ rc = __write_register(core, CTRL_INIT_IRIS3, ctrl_init_val);
+ if (rc)
+ return rc;
+
+ while (!ctrl_status && count < max_tries) {
+ rc = __read_register(core, CTRL_STATUS_IRIS3, &ctrl_status);
+ if (rc)
+ return rc;
+
+ if ((ctrl_status & CTRL_ERROR_STATUS__M_IRIS3) == 0x4) {
+ d_vpr_e("invalid setting for UC_REGION\n");
+ break;
+ }
+
+ usleep_range(50, 100);
+ count++;
+ }
+
+ if (count >= max_tries) {
+ d_vpr_e("Error booting up vidc firmware\n");
+ return -ETIME;
+ }
+
+ /* Enable interrupt before sending commands to venus */
+ rc = __write_register(core, CPU_CS_H2XSOFTINTEN_IRIS3, 0x1);
+ if (rc)
+ return rc;
+
+ rc = __write_register(core, CPU_CS_X2RPMH_IRIS3, 0x0);
+
+ return rc;
+}
+
+int msm_vidc_decide_work_mode_iris3(struct msm_vidc_inst *inst)
+{
+ u32 work_mode;
+ struct v4l2_format *inp_f;
+ u32 width, height;
+ bool res_ok = false;
+
+ work_mode = MSM_VIDC_STAGE_2;
+ inp_f = &inst->fmts[INPUT_PORT];
+
+ if (is_decode_session(inst)) {
+ height = inp_f->fmt.pix_mp.height;
+ width = inp_f->fmt.pix_mp.width;
+ res_ok = res_is_less_than(width, height, 1280, 720);
+ if (inst->capabilities[CODED_FRAMES].value ==
+ CODED_FRAMES_INTERLACE || res_ok) {
+ work_mode = MSM_VIDC_STAGE_1;
+ }
+ } else if (is_encode_session(inst)) {
+ height = inst->crop.height;
+ width = inst->crop.width;
+ res_ok = !res_is_greater_than(width, height, 4096, 2160);
+ if (inst->capabilities[SLICE_MODE].value ==
+ V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) {
+ work_mode = MSM_VIDC_STAGE_1;
+ }
+ if (inst->capabilities[LOSSLESS].value)
+ work_mode = MSM_VIDC_STAGE_2;
+
+ if (!inst->capabilities[GOP_SIZE].value)
+ work_mode = MSM_VIDC_STAGE_2;
+ } else {
+ i_vpr_e(inst, "%s: invalid session type\n", __func__);
+ return -EINVAL;
+ }
+
+ i_vpr_h(inst, "Configuring work mode = %u gop size = %u\n",
+ work_mode, inst->capabilities[GOP_SIZE].value);
+ msm_vidc_update_cap_value(inst, STAGE, work_mode, __func__);
+
+ return 0;
+}
+
+int msm_vidc_decide_work_route_iris3(struct msm_vidc_inst *inst)
+{
+ u32 work_route;
+ struct msm_vidc_core *core;
+
+ core = inst->core;
+ work_route = core->capabilities[NUM_VPP_PIPE].value;
+
+ if (is_decode_session(inst)) {
+ if (inst->capabilities[CODED_FRAMES].value ==
+ CODED_FRAMES_INTERLACE)
+ work_route = MSM_VIDC_PIPE_1;
+ } else if (is_encode_session(inst)) {
+ u32 slice_mode;
+
+ slice_mode = inst->capabilities[SLICE_MODE].value;
+
+ /*TODO Pipe=1 for legacy CBR*/
+ if (slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES)
+ work_route = MSM_VIDC_PIPE_1;
+
+ } else {
+ i_vpr_e(inst, "%s: invalid session type\n", __func__);
+ return -EINVAL;
+ }
+
+ i_vpr_h(inst, "Configuring work route = %u", work_route);
+ msm_vidc_update_cap_value(inst, PIPE, work_route, __func__);
+
+ return 0;
+}
+
+int msm_vidc_decide_quality_mode_iris3(struct msm_vidc_inst *inst)
+{
+ struct msm_vidc_core *core;
+ u32 mbpf, mbps, max_hq_mbpf, max_hq_mbps;
+ u32 mode = MSM_VIDC_POWER_SAVE_MODE;
+
+ if (!is_encode_session(inst))
+ return 0;
+
+ /* lossless or all intra runs at quality mode */
+ if (inst->capabilities[LOSSLESS].value ||
+ inst->capabilities[ALL_INTRA].value) {
+ mode = MSM_VIDC_MAX_QUALITY_MODE;
+ goto decision_done;
+ }
+
+ mbpf = msm_vidc_get_mbs_per_frame(inst);
+ mbps = mbpf * msm_vidc_get_fps(inst);
+ core = inst->core;
+ max_hq_mbpf = core->capabilities[MAX_MBPF_HQ].value;
+ max_hq_mbps = core->capabilities[MAX_MBPS_HQ].value;
+
+ if (mbpf <= max_hq_mbpf && mbps <= max_hq_mbps)
+ mode = MSM_VIDC_MAX_QUALITY_MODE;
+
+decision_done:
+ msm_vidc_update_cap_value(inst, QUALITY_MODE, mode, __func__);
+
+ return 0;
+}
+
+int msm_vidc_adjust_bitrate_boost_iris3(void *instance, struct v4l2_ctrl *ctrl)
+{
+ s32 adjusted_value;
+ struct msm_vidc_inst *inst = (struct msm_vidc_inst *)instance;
+ s32 rc_type = -1;
+ u32 width, height, frame_rate;
+ struct v4l2_format *f;
+ u32 max_bitrate = 0, bitrate = 0;
+
+ adjusted_value = ctrl ? ctrl->val :
+ inst->capabilities[BITRATE_BOOST].value;
+
+ if (inst->bufq[OUTPUT_PORT].vb2q->streaming)
+ return 0;
+
+ if (msm_vidc_get_parent_value(inst, BITRATE_BOOST,
+ BITRATE_MODE, &rc_type, __func__))
+ return -EINVAL;
+
+ /*
+ * Bitrate Boost are supported only for VBR rc type.
+ * Hence, do not adjust or set to firmware for non VBR rc's
+ */
+ if (rc_type != HFI_RC_VBR_CFR) {
+ adjusted_value = 0;
+ goto adjust;
+ }
+
+ frame_rate = inst->capabilities[FRAME_RATE].value >> 16;
+ f = &inst->fmts[OUTPUT_PORT];
+ width = f->fmt.pix_mp.width;
+ height = f->fmt.pix_mp.height;
+
+ /*
+ * honor client set bitrate boost
+ * if client did not set, keep max bitrate boost up to 4k@60fps
+ * and remove bitrate boost after 4k@60fps
+ */
+ if (inst->capabilities[BITRATE_BOOST].flags & CAP_FLAG_CLIENT_SET) {
+ /* accept client set bitrate boost value as is */
+ } else {
+ if (res_is_less_than_or_equal_to(width, height, 4096, 2176) &&
+ frame_rate <= 60)
+ adjusted_value = MAX_BITRATE_BOOST;
+ else
+ adjusted_value = 0;
+ }
+
+ max_bitrate = msm_vidc_get_max_bitrate(inst);
+ bitrate = inst->capabilities[BIT_RATE].value;
+ if (adjusted_value) {
+ if ((bitrate + bitrate / (100 / adjusted_value)) > max_bitrate) {
+ i_vpr_h(inst,
+ "%s: bitrate %d is beyond max bitrate %d, remove bitrate boost\n",
+ __func__, max_bitrate, bitrate);
+ adjusted_value = 0;
+ }
+ }
+adjust:
+ msm_vidc_update_cap_value(inst, BITRATE_BOOST, adjusted_value, __func__);
+
+ return 0;
+}
+
+static struct msm_vidc_iris_ops iris3_ops = {
+ .boot_firmware = __boot_firmware_iris3,
+ .raise_interrupt = __raise_interrupt_iris3,
+ .clear_interrupt = __clear_interrupt_iris3,
+ .power_on = __power_on_iris3,
+ .power_off = __power_off_iris3,
+ .prepare_pc = __prepare_pc_iris3,
+ .watchdog = __watchdog_iris3,
+};
+
+static struct msm_vidc_session_ops msm_session_ops = {
+ .buffer_size = msm_buffer_size_iris3,
+ .min_count = msm_buffer_min_count_iris3,
+ .extra_count = msm_buffer_extra_count_iris3,
+ .calc_freq = msm_vidc_calc_freq_iris3,
+ .calc_bw = msm_vidc_calc_bw_iris3,
+ .decide_work_route = msm_vidc_decide_work_route_iris3,
+ .decide_work_mode = msm_vidc_decide_work_mode_iris3,
+ .decide_quality_mode = msm_vidc_decide_quality_mode_iris3,
+};
+
+int msm_vidc_init_iris3(struct msm_vidc_core *core)
+{
+ core->iris_ops = &iris3_ops;
+ core->session_ops = &msm_session_ops;
+
+ return 0;
+}
--
2.7.4


2023-07-28 16:40:49

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 27/33] iris: variant: add helper functions for register handling

From: Dikshita Agarwal <[email protected]>

This implements the functions to read and write different regsiters.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../iris/variant/common/inc/msm_vidc_variant.h | 22 +++
.../iris/variant/common/src/msm_vidc_variant.c | 163 +++++++++++++++++++++
2 files changed, 185 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h
create mode 100644 drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c

diff --git a/drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h b/drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h
new file mode 100644
index 0000000..58ba276
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _MSM_VIDC_VARIANT_H_
+#define _MSM_VIDC_VARIANT_H_
+
+#include <linux/types.h>
+
+struct msm_vidc_core;
+
+int __write_register_masked(struct msm_vidc_core *core, u32 reg, u32 value,
+ u32 mask);
+int __write_register(struct msm_vidc_core *core, u32 reg, u32 value);
+int __read_register(struct msm_vidc_core *core, u32 reg, u32 *value);
+int __read_register_with_poll_timeout(struct msm_vidc_core *core, u32 reg,
+ u32 mask, u32 exp_val, u32 sleep_us, u32 timeout_us);
+int __set_registers(struct msm_vidc_core *core);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c b/drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c
new file mode 100644
index 0000000..4901844
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/errno.h>
+#include <linux/iopoll.h>
+
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_platform.h"
+#include "msm_vidc_state.h"
+#include "msm_vidc_variant.h"
+#include "venus_hfi.h"
+
+int __write_register(struct msm_vidc_core *core, u32 reg, u32 value)
+{
+ u32 hwiosymaddr = reg;
+ u8 *base_addr;
+ int rc = 0;
+
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return rc;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE)) {
+ d_vpr_e("HFI Write register failed : Power is OFF\n");
+ return -EINVAL;
+ }
+
+ base_addr = core->resource->register_base_addr;
+ d_vpr_l("regwrite(%pK + %#x) = %#x\n", base_addr, hwiosymaddr, value);
+ base_addr += hwiosymaddr;
+ writel_relaxed(value, base_addr);
+
+ /* Memory barrier to make sure value is written into the register */
+ wmb();
+
+ return rc;
+}
+
+/*
+ * Argument mask is used to specify which bits to update. In case mask is 0x11,
+ * only bits 0 & 4 will be updated with corresponding bits from value. To update
+ * entire register with value, set mask = 0xFFFFFFFF.
+ */
+int __write_register_masked(struct msm_vidc_core *core, u32 reg, u32 value,
+ u32 mask)
+{
+ u32 prev_val, new_val;
+ u8 *base_addr;
+ int rc = 0;
+
+ rc = __strict_check(core, __func__);
+ if (rc)
+ return rc;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE)) {
+ d_vpr_e("%s: register write failed, power is off\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ base_addr = core->resource->register_base_addr;
+ 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);
+ d_vpr_l("Base addr: %pK, writing to: %#x, mask: %#x\n",
+ base_addr, reg, mask);
+
+ d_vpr_l("previous-value: %#x, value: %#x, new-value: %#x...\n",
+ prev_val, value, new_val);
+ writel_relaxed(new_val, base_addr);
+ /*
+ * Memory barrier to make sure value is written into the register.
+ */
+ wmb();
+
+ return rc;
+}
+
+int __read_register(struct msm_vidc_core *core, u32 reg, u32 *value)
+{
+ int rc = 0;
+ u8 *base_addr;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE)) {
+ d_vpr_e("HFI Read register failed : Power is OFF\n");
+ return -EINVAL;
+ }
+
+ base_addr = core->resource->register_base_addr;
+
+ *value = readl_relaxed(base_addr + reg);
+ /*
+ * Memory barrier to make sure value is read correctly from the
+ * register.
+ */
+ rmb();
+ d_vpr_l("regread(%pK + %#x) = %#x\n", base_addr, reg, *value);
+
+ return rc;
+}
+
+int __read_register_with_poll_timeout(struct msm_vidc_core *core, u32 reg,
+ u32 mask, u32 exp_val, u32 sleep_us,
+ u32 timeout_us)
+{
+ int rc = 0;
+ u32 val = 0;
+ u8 *addr;
+
+ if (!is_core_sub_state(core, CORE_SUBSTATE_POWER_ENABLE)) {
+ d_vpr_e("%s failed: Power is OFF\n", __func__);
+ return -EINVAL;
+ }
+
+ addr = (u8 *)core->resource->register_base_addr + reg;
+
+ rc = readl_relaxed_poll_timeout(addr, val, ((val & mask) == exp_val), sleep_us, timeout_us);
+ /*
+ * Memory barrier to make sure value is read correctly from the
+ * register.
+ */
+ rmb();
+ d_vpr_l("regread(%pK + %#x) = %#x. rc %d, mask %#x, exp_val %#x\n",
+ core->resource->register_base_addr, reg, val, rc, mask, exp_val);
+ d_vpr_l("cond %u, sleep %u, timeout %u\n",
+ ((val & mask) == exp_val), sleep_us, timeout_us);
+
+ return rc;
+}
+
+int __set_registers(struct msm_vidc_core *core)
+{
+ const struct reg_preset_table *reg_prst;
+ unsigned int prst_count;
+ int cnt, rc = 0;
+
+ reg_prst = core->platform->data.reg_prst_tbl;
+ prst_count = core->platform->data.reg_prst_tbl_size;
+
+ /* skip if there is no preset reg available */
+ if (!reg_prst || !prst_count)
+ return 0;
+
+ for (cnt = 0; cnt < prst_count; cnt++) {
+ rc = __write_register_masked(core, reg_prst[cnt].reg,
+ reg_prst[cnt].value, reg_prst[cnt].mask);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
--
2.7.4


2023-07-28 16:42:10

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 17/33] iris: vidc: define various structures and enum

Define various structures and enums used by driver like
core capability, instance capability, color space info,
buffer types etc.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../qcom/iris/vidc/inc/msm_vidc_internal.h | 787 +++++++++++++++++++++
1 file changed, 787 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h

diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h
new file mode 100644
index 0000000..4d54834
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h
@@ -0,0 +1,787 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _MSM_VIDC_INTERNAL_H_
+#define _MSM_VIDC_INTERNAL_H_
+
+#include <linux/bits.h>
+#include <linux/spinlock.h>
+#include <linux/sync_file.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+struct msm_vidc_core;
+struct msm_vidc_inst;
+
+static const char video_banner[] = "Video-Banner: (" __stringify(VIDEO_COMPILE_BY) "@"
+ __stringify(VIDEO_COMPILE_HOST) ") (" __stringify(VIDEO_COMPILE_TIME) ")";
+
+#define MAX_NAME_LENGTH 128
+#define VENUS_VERSION_LENGTH 128
+#define MAX_MATRIX_COEFFS 9
+#define MAX_BIAS_COEFFS 3
+#define MAX_LIMIT_COEFFS 6
+#define MAX_DEBUGFS_NAME 50
+#define DEFAULT_HEIGHT 240
+#define DEFAULT_WIDTH 320
+#define DEFAULT_FPS 30
+#define MAXIMUM_VP9_FPS 60
+#define RT_DEC_DOWN_PRORITY_OFFSET 1
+#define MAX_SUPPORTED_INSTANCES 16
+#define DEFAULT_BSE_VPP_DELAY 2
+#define MAX_CAP_PARENTS 20
+#define MAX_CAP_CHILDREN 20
+#define DEFAULT_MAX_HOST_BUF_COUNT 64
+#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
+#define BIT_DEPTH_8 (8 << 16 | 8)
+#define BIT_DEPTH_10 (10 << 16 | 10)
+#define CODED_FRAMES_PROGRESSIVE 0x0
+#define CODED_FRAMES_INTERLACE 0x1
+#define MAX_VP9D_INST_COUNT 6
+/* TODO: move below macros to waipio.c */
+#define MAX_ENH_LAYER_HB 3
+#define MAX_HEVC_VBR_ENH_LAYER_SLIDING_WINDOW 5
+#define MAX_HEVC_NON_VBR_ENH_LAYER_SLIDING_WINDOW 3
+#define MAX_AVC_ENH_LAYER_SLIDING_WINDOW 3
+#define MAX_AVC_ENH_LAYER_HYBRID_HP 5
+#define INVALID_DEFAULT_MARK_OR_USE_LTR -1
+#define MAX_SLICES_PER_FRAME 10
+#define MAX_SLICES_FRAME_RATE 60
+#define MAX_MB_SLICE_WIDTH 4096
+#define MAX_MB_SLICE_HEIGHT 2160
+#define MAX_BYTES_SLICE_WIDTH 1920
+#define MAX_BYTES_SLICE_HEIGHT 1088
+#define MIN_HEVC_SLICE_WIDTH 384
+#define MIN_AVC_SLICE_WIDTH 192
+#define MIN_SLICE_HEIGHT 128
+#define MAX_BITRATE_BOOST 25
+#define MAX_SUPPORTED_MIN_QUALITY 70
+#define MIN_CHROMA_QP_OFFSET -12
+#define MAX_CHROMA_QP_OFFSET 0
+#define MIN_QP_10BIT -11
+#define MIN_QP_8BIT 1
+#define INVALID_FD -1
+#define MAX_ENCODING_REFERNCE_FRAMES 7
+#define MAX_LTR_FRAME_COUNT_5 5
+#define MAX_LTR_FRAME_COUNT_2 2
+#define MAX_ENC_RING_BUF_COUNT 5 /* to be tuned */
+#define MAX_TRANSCODING_STATS_FRAME_RATE 60
+#define MAX_TRANSCODING_STATS_WIDTH 4096
+#define MAX_TRANSCODING_STATS_HEIGHT 2304
+
+#define DCVS_WINDOW 16
+#define ENC_FPS_WINDOW 3
+#define DEC_FPS_WINDOW 10
+#define INPUT_TIMER_LIST_SIZE 30
+
+#define INPUT_MPLANE V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
+#define OUTPUT_MPLANE V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
+
+#define VIDC_IFACEQ_MAX_PKT_SIZE 1024
+#define VIDC_IFACEQ_MED_PKT_SIZE 768
+#define VIDC_IFACEQ_MIN_PKT_SIZE 8
+#define VIDC_IFACEQ_VAR_SMALL_PKT_SIZE 100
+#define VIDC_IFACEQ_VAR_LARGE_PKT_SIZE 512
+#define VIDC_IFACEQ_VAR_HUGE_PKT_SIZE (1024 * 4)
+
+#define NUM_MBS_PER_SEC(__height, __width, __fps) \
+ (NUM_MBS_PER_FRAME(__height, __width) * (__fps))
+
+#define NUM_MBS_PER_FRAME(__height, __width) \
+ ((ALIGN(__height, 16) / 16) * (ALIGN(__width, 16) / 16))
+
+#ifdef V4L2_CTRL_CLASS_CODEC
+#define IS_PRIV_CTRL(idx) ( \
+ (V4L2_CTRL_ID2WHICH(idx) == V4L2_CTRL_CLASS_CODEC) && \
+ V4L2_CTRL_DRIVER_PRIV(idx))
+#else
+#define IS_PRIV_CTRL(idx) ( \
+ (V4L2_CTRL_ID2WHICH(idx) == V4L2_CTRL_CLASS_MPEG) && \
+ V4L2_CTRL_DRIVER_PRIV(idx))
+#endif
+
+#define BUFFER_ALIGNMENT_SIZE(x) x
+#define NUM_MBS_360P (((480 + 15) >> 4) * ((360 + 15) >> 4))
+#define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4))
+#define NUM_MBS_4k (((4096 + 15) >> 4) * ((2304 + 15) >> 4))
+#define MB_SIZE_IN_PIXEL (16 * 16)
+
+#define DB_H264_DISABLE_SLICE_BOUNDARY \
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+
+#define DB_HEVC_DISABLE_SLICE_BOUNDARY \
+ V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+
+/*
+ * Convert Q16 number into Integer and Fractional part up to 2 places.
+ * Ex : 105752 / 65536 = 1.61; 1.61 in Q16 = 105752;
+ * Integer part = 105752 / 65536 = 1;
+ * Reminder = 105752 * 0xFFFF = 40216; Last 16 bits.
+ * Fractional part = 40216 * 100 / 65536 = 61;
+ * Now convert to FP(1, 61, 100).
+ */
+#define Q16_INT(q) ((q) >> 16)
+#define Q16_FRAC(q) ((((q) & 0xFFFF) * 100) >> 16)
+
+/* define timeout values */
+#define HW_RESPONSE_TIMEOUT_VALUE (1000)
+#define SW_PC_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500)
+#define FW_UNLOAD_DELAY_VALUE (SW_PC_DELAY_VALUE + 1500)
+
+#define MAX_DPB_COUNT 32
+ /*
+ * max dpb count in firmware = 16
+ * each dpb: 4 words - <base_address, addr_offset, data_offset>
+ * dpb list array size = 16 * 4
+ * dpb payload size = 16 * 4 * 4
+ */
+#define MAX_DPB_LIST_ARRAY_SIZE (16 * 4)
+#define MAX_DPB_LIST_PAYLOAD_SIZE (16 * 4 * 4)
+
+enum msm_vidc_domain_type {
+ MSM_VIDC_ENCODER = BIT(0),
+ MSM_VIDC_DECODER = BIT(1),
+};
+
+enum msm_vidc_codec_type {
+ MSM_VIDC_H264 = BIT(0),
+ MSM_VIDC_HEVC = BIT(1),
+ MSM_VIDC_VP9 = BIT(2),
+};
+
+enum msm_vidc_colorformat_type {
+ MSM_VIDC_FMT_NONE = 0,
+ MSM_VIDC_FMT_NV12C = BIT(0),
+ MSM_VIDC_FMT_NV12 = BIT(1),
+ MSM_VIDC_FMT_NV21 = BIT(2),
+ MSM_VIDC_FMT_TP10C = BIT(3),
+ MSM_VIDC_FMT_P010 = BIT(4),
+ MSM_VIDC_FMT_RGBA8888C = BIT(5),
+ MSM_VIDC_FMT_RGBA8888 = BIT(6),
+};
+
+enum msm_vidc_buffer_type {
+ MSM_VIDC_BUF_NONE,
+ MSM_VIDC_BUF_INPUT,
+ MSM_VIDC_BUF_OUTPUT,
+ MSM_VIDC_BUF_READ_ONLY,
+ MSM_VIDC_BUF_INTERFACE_QUEUE,
+ MSM_VIDC_BUF_BIN,
+ MSM_VIDC_BUF_ARP,
+ MSM_VIDC_BUF_COMV,
+ MSM_VIDC_BUF_NON_COMV,
+ MSM_VIDC_BUF_LINE,
+ MSM_VIDC_BUF_DPB,
+ MSM_VIDC_BUF_PERSIST,
+ MSM_VIDC_BUF_VPSS,
+};
+
+/* always match with v4l2 flags V4L2_BUF_FLAG_* */
+enum msm_vidc_buffer_flags {
+ MSM_VIDC_BUF_FLAG_KEYFRAME = 0x00000008,
+ MSM_VIDC_BUF_FLAG_PFRAME = 0x00000010,
+ MSM_VIDC_BUF_FLAG_BFRAME = 0x00000020,
+ MSM_VIDC_BUF_FLAG_ERROR = 0x00000040,
+ MSM_VIDC_BUF_FLAG_LAST = 0x00100000,
+};
+
+enum msm_vidc_buffer_attributes {
+ MSM_VIDC_ATTR_DEFERRED = BIT(0),
+ MSM_VIDC_ATTR_READ_ONLY = BIT(1),
+ MSM_VIDC_ATTR_PENDING_RELEASE = BIT(2),
+ MSM_VIDC_ATTR_QUEUED = BIT(3),
+ MSM_VIDC_ATTR_DEQUEUED = BIT(4),
+ MSM_VIDC_ATTR_BUFFER_DONE = BIT(5),
+ MSM_VIDC_ATTR_RELEASE_ELIGIBLE = BIT(6),
+};
+
+enum msm_vidc_buffer_region {
+ MSM_VIDC_REGION_NONE = 0,
+ MSM_VIDC_NON_SECURE,
+ MSM_VIDC_NON_SECURE_PIXEL,
+ MSM_VIDC_SECURE_PIXEL,
+ MSM_VIDC_SECURE_NONPIXEL,
+ MSM_VIDC_SECURE_BITSTREAM,
+ MSM_VIDC_REGION_MAX,
+};
+
+enum msm_vidc_port_type {
+ INPUT_PORT = 0,
+ OUTPUT_PORT,
+ PORT_NONE,
+ MAX_PORT,
+};
+
+enum msm_vidc_stage_type {
+ MSM_VIDC_STAGE_NONE = 0,
+ MSM_VIDC_STAGE_1 = 1,
+ MSM_VIDC_STAGE_2 = 2,
+};
+
+enum msm_vidc_pipe_type {
+ MSM_VIDC_PIPE_NONE = 0,
+ MSM_VIDC_PIPE_1 = 1,
+ MSM_VIDC_PIPE_2 = 2,
+ MSM_VIDC_PIPE_4 = 4,
+};
+
+enum msm_vidc_quality_mode {
+ MSM_VIDC_MAX_QUALITY_MODE = 0x1,
+ MSM_VIDC_POWER_SAVE_MODE = 0x2,
+};
+
+enum msm_vidc_color_primaries {
+ MSM_VIDC_PRIMARIES_RESERVED = 0,
+ MSM_VIDC_PRIMARIES_BT709 = 1,
+ MSM_VIDC_PRIMARIES_UNSPECIFIED = 2,
+ MSM_VIDC_PRIMARIES_BT470_SYSTEM_M = 4,
+ MSM_VIDC_PRIMARIES_BT470_SYSTEM_BG = 5,
+ MSM_VIDC_PRIMARIES_BT601_525 = 6,
+ MSM_VIDC_PRIMARIES_SMPTE_ST240M = 7,
+ MSM_VIDC_PRIMARIES_GENERIC_FILM = 8,
+ MSM_VIDC_PRIMARIES_BT2020 = 9,
+ MSM_VIDC_PRIMARIES_SMPTE_ST428_1 = 10,
+ MSM_VIDC_PRIMARIES_SMPTE_RP431_2 = 11,
+ MSM_VIDC_PRIMARIES_SMPTE_EG431_1 = 12,
+ MSM_VIDC_PRIMARIES_SMPTE_EBU_TECH = 22,
+};
+
+enum msm_vidc_transfer_characteristics {
+ MSM_VIDC_TRANSFER_RESERVED = 0,
+ MSM_VIDC_TRANSFER_BT709 = 1,
+ MSM_VIDC_TRANSFER_UNSPECIFIED = 2,
+ MSM_VIDC_TRANSFER_BT470_SYSTEM_M = 4,
+ MSM_VIDC_TRANSFER_BT470_SYSTEM_BG = 5,
+ MSM_VIDC_TRANSFER_BT601_525_OR_625 = 6,
+ MSM_VIDC_TRANSFER_SMPTE_ST240M = 7,
+ MSM_VIDC_TRANSFER_LINEAR = 8,
+ MSM_VIDC_TRANSFER_LOG_100_1 = 9,
+ MSM_VIDC_TRANSFER_LOG_SQRT = 10,
+ MSM_VIDC_TRANSFER_XVYCC = 11,
+ MSM_VIDC_TRANSFER_BT1361_0 = 12,
+ MSM_VIDC_TRANSFER_SRGB_SYCC = 13,
+ MSM_VIDC_TRANSFER_BT2020_14 = 14,
+ MSM_VIDC_TRANSFER_BT2020_15 = 15,
+ MSM_VIDC_TRANSFER_SMPTE_ST2084_PQ = 16,
+ MSM_VIDC_TRANSFER_SMPTE_ST428_1 = 17,
+ MSM_VIDC_TRANSFER_BT2100_2_HLG = 18,
+};
+
+enum msm_vidc_matrix_coefficients {
+ MSM_VIDC_MATRIX_COEFF_SRGB_SMPTE_ST428_1 = 0,
+ MSM_VIDC_MATRIX_COEFF_BT709 = 1,
+ MSM_VIDC_MATRIX_COEFF_UNSPECIFIED = 2,
+ MSM_VIDC_MATRIX_COEFF_RESERVED = 3,
+ MSM_VIDC_MATRIX_COEFF_FCC_TITLE_47 = 4,
+ MSM_VIDC_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625 = 5,
+ MSM_VIDC_MATRIX_COEFF_BT601_525_BT1358_525_OR_625 = 6,
+ MSM_VIDC_MATRIX_COEFF_SMPTE_ST240 = 7,
+ MSM_VIDC_MATRIX_COEFF_YCGCO = 8,
+ MSM_VIDC_MATRIX_COEFF_BT2020_NON_CONSTANT = 9,
+ MSM_VIDC_MATRIX_COEFF_BT2020_CONSTANT = 10,
+ MSM_VIDC_MATRIX_COEFF_SMPTE_ST2085 = 11,
+ MSM_VIDC_MATRIX_COEFF_SMPTE_CHROM_DERV_NON_CONSTANT = 12,
+ MSM_VIDC_MATRIX_COEFF_SMPTE_CHROM_DERV_CONSTANT = 13,
+ MSM_VIDC_MATRIX_COEFF_BT2100 = 14,
+};
+
+enum msm_vidc_preprocess_type {
+ MSM_VIDC_PREPROCESS_NONE = BIT(0),
+ MSM_VIDC_PREPROCESS_TYPE0 = BIT(1),
+};
+
+enum msm_vidc_core_capability_type {
+ CORE_CAP_NONE = 0,
+ ENC_CODECS,
+ DEC_CODECS,
+ MAX_SESSION_COUNT,
+ MAX_NUM_720P_SESSIONS,
+ MAX_NUM_1080P_SESSIONS,
+ MAX_NUM_4K_SESSIONS,
+ MAX_NUM_8K_SESSIONS,
+ MAX_LOAD,
+ MAX_RT_MBPF,
+ MAX_MBPF,
+ MAX_MBPS,
+ MAX_MBPF_HQ,
+ MAX_MBPS_HQ,
+ MAX_MBPF_B_FRAME,
+ MAX_MBPS_B_FRAME,
+ MAX_MBPS_ALL_INTRA,
+ MAX_ENH_LAYER_COUNT,
+ NUM_VPP_PIPE,
+ SW_PC,
+ SW_PC_DELAY,
+ FW_UNLOAD,
+ FW_UNLOAD_DELAY,
+ HW_RESPONSE_TIMEOUT,
+ PREFIX_BUF_COUNT_PIX,
+ PREFIX_BUF_SIZE_PIX,
+ PREFIX_BUF_COUNT_NON_PIX,
+ PREFIX_BUF_SIZE_NON_PIX,
+ PAGEFAULT_NON_FATAL,
+ PAGETABLE_CACHING,
+ DCVS,
+ DECODE_BATCH,
+ DECODE_BATCH_TIMEOUT,
+ STATS_TIMEOUT_MS,
+ AV_SYNC_WINDOW_SIZE,
+ CLK_FREQ_THRESHOLD,
+ NON_FATAL_FAULTS,
+ DEVICE_CAPS,
+ CORE_CAP_MAX,
+};
+
+/**
+ * msm_vidc_prepare_dependency_list() api will prepare caps_list by looping over
+ * enums(msm_vidc_inst_capability_type) from 0 to INST_CAP_MAX and arranges the
+ * node in such a way that parents willbe at the front and dependent children
+ * in the back.
+ *
+ * caps_list preparation may become CPU intensive task, so to save CPU cycles,
+ * organize enum in proper order(leaf caps at the beginning and dependent parent caps
+ * at back), so that during caps_list preparation num CPU cycles spent will reduce.
+ *
+ * Note: It will work, if enum kept at different places, but not efficient.
+ *
+ * - place all leaf(no child) enums before PROFILE cap.
+ * - place all intermittent(having both parent and child) enums before FRAME_WIDTH cap.
+ * - place all root(no parent) enums before INST_CAP_MAX cap.
+ */
+
+enum msm_vidc_inst_capability_type {
+ INST_CAP_NONE = 0,
+ MIN_FRAME_QP,
+ MAX_FRAME_QP,
+ I_FRAME_QP,
+ P_FRAME_QP,
+ B_FRAME_QP,
+ TIME_DELTA_BASED_RC,
+ CONSTANT_QUALITY,
+ VBV_DELAY,
+ PEAK_BITRATE,
+ ENTROPY_MODE,
+ TRANSFORM_8X8,
+ STAGE,
+ LTR_COUNT,
+ IR_PERIOD,
+ BITRATE_BOOST,
+ OUTPUT_ORDER,
+ INPUT_BUF_HOST_MAX_COUNT,
+ OUTPUT_BUF_HOST_MAX_COUNT,
+ VUI_TIMING_INFO,
+ SLICE_DECODE,
+ PROFILE,
+ ENH_LAYER_COUNT,
+ BIT_RATE,
+ GOP_SIZE,
+ B_FRAME,
+ ALL_INTRA,
+ MIN_QUALITY,
+ SLICE_MODE,
+ FRAME_WIDTH,
+ LOSSLESS_FRAME_WIDTH,
+ FRAME_HEIGHT,
+ LOSSLESS_FRAME_HEIGHT,
+ PIX_FMTS,
+ MIN_BUFFERS_INPUT,
+ MIN_BUFFERS_OUTPUT,
+ MBPF,
+ BATCH_MBPF,
+ BATCH_FPS,
+ LOSSLESS_MBPF,
+ FRAME_RATE,
+ OPERATING_RATE,
+ INPUT_RATE,
+ TIMESTAMP_RATE,
+ SCALE_FACTOR,
+ MB_CYCLES_VSP,
+ MB_CYCLES_VPP,
+ MB_CYCLES_LP,
+ MB_CYCLES_FW,
+ MB_CYCLES_FW_VPP,
+ ENC_RING_BUFFER_COUNT,
+ HFLIP,
+ VFLIP,
+ ROTATION,
+ HEADER_MODE,
+ PREPEND_SPSPPS_TO_IDR,
+ WITHOUT_STARTCODE,
+ NAL_LENGTH_FIELD,
+ REQUEST_I_FRAME,
+ BITRATE_MODE,
+ LOSSLESS,
+ FRAME_SKIP_MODE,
+ FRAME_RC_ENABLE,
+ GOP_CLOSURE,
+ USE_LTR,
+ MARK_LTR,
+ BASELAYER_PRIORITY,
+ IR_TYPE,
+ AU_DELIMITER,
+ GRID_ENABLE,
+ GRID_SIZE,
+ I_FRAME_MIN_QP,
+ P_FRAME_MIN_QP,
+ B_FRAME_MIN_QP,
+ I_FRAME_MAX_QP,
+ P_FRAME_MAX_QP,
+ B_FRAME_MAX_QP,
+ LAYER_TYPE,
+ LAYER_ENABLE,
+ L0_BR,
+ L1_BR,
+ L2_BR,
+ L3_BR,
+ L4_BR,
+ L5_BR,
+ LEVEL,
+ HEVC_TIER,
+ DISPLAY_DELAY_ENABLE,
+ DISPLAY_DELAY,
+ CONCEAL_COLOR_8BIT,
+ CONCEAL_COLOR_10BIT,
+ LF_MODE,
+ LF_ALPHA,
+ LF_BETA,
+ SLICE_MAX_BYTES,
+ SLICE_MAX_MB,
+ MB_RC,
+ CHROMA_QP_INDEX_OFFSET,
+ PIPE,
+ POC,
+ CODED_FRAMES,
+ BIT_DEPTH,
+ BITSTREAM_SIZE_OVERWRITE,
+ DEFAULT_HEADER,
+ RAP_FRAME,
+ SEQ_CHANGE_AT_SYNC_FRAME,
+ QUALITY_MODE,
+ CABAC_MAX_BITRATE,
+ CAVLC_MAX_BITRATE,
+ ALLINTRA_MAX_BITRATE,
+ NUM_COMV,
+ SIGNAL_COLOR_INFO,
+ INST_CAP_MAX,
+};
+
+enum msm_vidc_inst_capability_flags {
+ CAP_FLAG_NONE = 0,
+ CAP_FLAG_DYNAMIC_ALLOWED = BIT(0),
+ CAP_FLAG_MENU = BIT(1),
+ CAP_FLAG_INPUT_PORT = BIT(2),
+ CAP_FLAG_OUTPUT_PORT = BIT(3),
+ CAP_FLAG_CLIENT_SET = BIT(4),
+ CAP_FLAG_BITMASK = BIT(5),
+ CAP_FLAG_VOLATILE = BIT(6),
+};
+
+struct msm_vidc_inst_cap {
+ enum msm_vidc_inst_capability_type cap_id;
+ s32 min;
+ s32 max;
+ u32 step_or_mask;
+ s32 value;
+ u32 v4l2_id;
+ u32 hfi_id;
+ enum msm_vidc_inst_capability_flags flags;
+ enum msm_vidc_inst_capability_type children[MAX_CAP_CHILDREN];
+ int (*adjust)(void *inst,
+ struct v4l2_ctrl *ctrl);
+ int (*set)(void *inst,
+ enum msm_vidc_inst_capability_type cap_id);
+};
+
+struct msm_vidc_inst_capability {
+ enum msm_vidc_domain_type domain;
+ enum msm_vidc_codec_type codec;
+ struct msm_vidc_inst_cap cap[INST_CAP_MAX + 1];
+};
+
+struct msm_vidc_core_capability {
+ enum msm_vidc_core_capability_type type;
+ u32 value;
+};
+
+struct msm_vidc_inst_cap_entry {
+ /* list of struct msm_vidc_inst_cap_entry */
+ struct list_head list;
+ enum msm_vidc_inst_capability_type cap_id;
+};
+
+struct msm_vidc_event_data {
+ union {
+ bool bval;
+ u32 uval;
+ u64 uval64;
+ s32 val;
+ s64 val64;
+ void *ptr;
+ } edata;
+};
+
+struct debug_buf_count {
+ u64 etb;
+ u64 ftb;
+ u64 fbd;
+ u64 ebd;
+};
+
+struct msm_vidc_statistics {
+ struct debug_buf_count count;
+ u64 data_size;
+ u64 time_ms;
+ u32 avg_bw_llcc;
+ u32 avg_bw_ddr;
+};
+
+enum msm_vidc_cache_op {
+ MSM_VIDC_CACHE_CLEAN,
+ MSM_VIDC_CACHE_INVALIDATE,
+ MSM_VIDC_CACHE_CLEAN_INVALIDATE,
+};
+
+enum msm_vidc_dcvs_flags {
+ MSM_VIDC_DCVS_INCR = BIT(0),
+ MSM_VIDC_DCVS_DECR = BIT(1),
+};
+
+enum msm_vidc_clock_properties {
+ CLOCK_PROP_HAS_SCALING = BIT(0),
+ CLOCK_PROP_HAS_MEM_RETENTION = BIT(1),
+};
+
+enum signal_session_response {
+ SIGNAL_CMD_STOP_INPUT = 0,
+ SIGNAL_CMD_STOP_OUTPUT,
+ SIGNAL_CMD_CLOSE,
+ MAX_SIGNAL,
+};
+
+struct msm_vidc_input_cr_data {
+ struct list_head list;
+ u32 index;
+ u32 input_cr;
+};
+
+struct msm_vidc_session_idle {
+ bool idle;
+ u64 last_activity_time_ns;
+};
+
+struct msm_vidc_color_info {
+ u32 colorspace;
+ u32 ycbcr_enc;
+ u32 xfer_func;
+ u32 quantization;
+};
+
+struct msm_vidc_rectangle {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
+struct msm_vidc_subscription_params {
+ u32 bitstream_resolution;
+ u32 crop_offsets[2];
+ u32 bit_depth;
+ u32 coded_frames;
+ u32 fw_min_count;
+ u32 pic_order_cnt;
+ u32 color_info;
+ u32 profile;
+ u32 level;
+ u32 tier;
+};
+
+struct msm_vidc_hfi_frame_info {
+ u32 picture_type;
+ u32 no_output;
+ u32 subframe_input;
+ u32 cr;
+ u32 cf;
+ u32 data_corrupt;
+ u32 overflow;
+};
+
+struct msm_vidc_decode_vpp_delay {
+ bool enable;
+ u32 size;
+};
+
+struct msm_vidc_decode_batch {
+ bool enable;
+ u32 size;
+ struct delayed_work work;
+};
+
+enum msm_vidc_power_mode {
+ VIDC_POWER_NORMAL = 0,
+ VIDC_POWER_LOW,
+ VIDC_POWER_TURBO,
+};
+
+struct vidc_bus_vote_data {
+ enum msm_vidc_domain_type domain;
+ enum msm_vidc_codec_type codec;
+ enum msm_vidc_power_mode power_mode;
+ u32 color_formats[2];
+ int num_formats; /* 1 = DPB-OPB unified; 2 = split */
+ int input_height, input_width, bitrate;
+ int output_height, output_width;
+ int rotation;
+ int compression_ratio;
+ int complexity_factor;
+ int input_cr;
+ u32 lcu_size;
+ u32 fps;
+ u32 work_mode;
+ bool use_sys_cache;
+ bool b_frames_enabled;
+ u64 calc_bw_ddr;
+ u64 calc_bw_llcc;
+ u32 num_vpp_pipes;
+};
+
+struct msm_vidc_power {
+ enum msm_vidc_power_mode power_mode;
+ u32 buffer_counter;
+ u32 min_threshold;
+ u32 nom_threshold;
+ u32 max_threshold;
+ bool dcvs_mode;
+ u32 dcvs_window;
+ u64 min_freq;
+ u64 curr_freq;
+ u32 ddr_bw;
+ u32 sys_cache_bw;
+ u32 dcvs_flags;
+ u32 fw_cr;
+ u32 fw_cf;
+};
+
+struct msm_vidc_mem {
+ struct list_head list;
+ enum msm_vidc_buffer_type type;
+ enum msm_vidc_buffer_region region;
+ u32 size;
+ u8 secure:1;
+ u8 map_kernel:1;
+ struct dma_buf *dmabuf;
+ struct iosys_map dmabuf_map;
+ void *kvaddr;
+ dma_addr_t device_addr;
+ unsigned long attrs;
+ u32 refcount;
+ struct sg_table *table;
+ struct dma_buf_attachment *attach;
+ enum dma_data_direction direction;
+};
+
+struct msm_vidc_mem_list {
+ struct list_head list; // list of "struct msm_vidc_mem"
+};
+
+struct msm_vidc_buffer {
+ struct list_head list;
+ struct msm_vidc_inst *inst;
+ enum msm_vidc_buffer_type type;
+ enum msm_vidc_buffer_region region;
+ u32 index;
+ int fd;
+ u32 buffer_size;
+ u32 data_offset;
+ u32 data_size;
+ u64 device_addr;
+ u32 flags;
+ u64 timestamp;
+ enum msm_vidc_buffer_attributes attr;
+ void *dmabuf;
+ struct sg_table *sg_table;
+ struct dma_buf_attachment *attach;
+ u32 dbuf_get:1;
+ u32 start_time_ms;
+ u32 end_time_ms;
+};
+
+struct msm_vidc_buffers {
+ struct list_head list; // list of "struct msm_vidc_buffer"
+ u32 min_count;
+ u32 extra_count;
+ u32 actual_count;
+ u32 size;
+ bool reuse;
+};
+
+struct msm_vidc_buffer_stats {
+ struct list_head list;
+ u32 frame_num;
+ u64 timestamp;
+ u32 etb_time_ms;
+ u32 ebd_time_ms;
+ u32 ftb_time_ms;
+ u32 fbd_time_ms;
+ u32 data_size;
+ u32 flags;
+ u32 ts_offset;
+};
+
+enum msm_vidc_buffer_stats_flag {
+ MSM_VIDC_STATS_FLAG_CORRUPT = BIT(0),
+ MSM_VIDC_STATS_FLAG_OVERFLOW = BIT(1),
+ MSM_VIDC_STATS_FLAG_NO_OUTPUT = BIT(2),
+ MSM_VIDC_STATS_FLAG_SUBFRAME_INPUT = BIT(3),
+};
+
+struct msm_vidc_sort {
+ struct list_head list;
+ s64 val;
+};
+
+struct msm_vidc_timestamp {
+ struct msm_vidc_sort sort;
+ u64 rank;
+};
+
+struct msm_vidc_timestamps {
+ struct list_head list;
+ u32 count;
+ u64 rank;
+};
+
+struct msm_vidc_input_timer {
+ struct list_head list;
+ u64 time_us;
+};
+
+enum msm_vidc_allow {
+ MSM_VIDC_DISALLOW,
+ MSM_VIDC_ALLOW,
+ MSM_VIDC_DEFER,
+ MSM_VIDC_DISCARD,
+ MSM_VIDC_IGNORE,
+};
+
+struct msm_vidc_sfr {
+ u32 bufsize;
+ u8 rg_data[];
+};
+
+struct msm_vidc_ctrl_data {
+ bool skip_s_ctrl;
+};
+
+#endif // _MSM_VIDC_INTERNAL_H_
--
2.7.4


2023-07-28 16:43:05

by Vikash Garodia

[permalink] [raw]
Subject: [PATCH 30/33] iris: variant: iris3: add helper for bus and clock calculation

From: Dikshita Agarwal <[email protected]>

This adds the helper function to calculate the required bus
bandwidth and clock frequency for the given video usecase/s.

Signed-off-by: Dikshita Agarwal <[email protected]>
Signed-off-by: Vikash Garodia <[email protected]>
---
.../iris/variant/iris3/inc/msm_vidc_power_iris3.h | 17 +
.../iris/variant/iris3/src/msm_vidc_power_iris3.c | 345 +++++++++++++++++++++
2 files changed, 362 insertions(+)
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h
create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c

diff --git a/drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h b/drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h
new file mode 100644
index 0000000..a6f3e54
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __H_MSM_VIDC_POWER_IRIS3_H__
+#define __H_MSM_VIDC_POWER_IRIS3_H__
+
+#include "msm_vidc_inst.h"
+#include "msm_vidc_power.h"
+
+u64 msm_vidc_calc_freq_iris3(struct msm_vidc_inst *inst, u32 data_size);
+int msm_vidc_calc_bw_iris3(struct msm_vidc_inst *inst,
+ struct vidc_bus_vote_data *vote_data);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c b/drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c
new file mode 100644
index 0000000..32b549c
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "msm_vidc_core.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_driver.h"
+#include "msm_vidc_inst.h"
+#include "msm_vidc_power.h"
+#include "msm_vidc_power_iris3.h"
+#include "perf_static_model.h"
+
+static int msm_vidc_init_codec_input_freq(struct msm_vidc_inst *inst, u32 data_size,
+ struct api_calculation_input *codec_input)
+{
+ enum msm_vidc_port_type port;
+ u32 color_fmt;
+
+ if (is_encode_session(inst)) {
+ codec_input->decoder_or_encoder = CODEC_ENCODER;
+ } else if (is_decode_session(inst)) {
+ codec_input->decoder_or_encoder = CODEC_DECODER;
+ } else {
+ d_vpr_e("%s: invalid domain %d\n", __func__, inst->domain);
+ return -EINVAL;
+ }
+
+ codec_input->chipset_gen = MSM_SM8550;
+
+ if (inst->codec == MSM_VIDC_H264) {
+ codec_input->codec = CODEC_H264;
+ codec_input->lcu_size = 16;
+ if (inst->capabilities[ENTROPY_MODE].value ==
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+ codec_input->entropy_coding_mode = CODEC_ENTROPY_CODING_CABAC;
+ else
+ codec_input->entropy_coding_mode = CODEC_ENTROPY_CODING_CAVLC;
+ } else if (inst->codec == MSM_VIDC_HEVC) {
+ codec_input->codec = CODEC_HEVC;
+ codec_input->lcu_size = 32;
+ } else if (inst->codec == MSM_VIDC_VP9) {
+ codec_input->codec = CODEC_VP9;
+ codec_input->lcu_size = 16;
+ } else {
+ d_vpr_e("%s: invalid codec %d\n", __func__, inst->codec);
+ return -EINVAL;
+ }
+
+ codec_input->pipe_num = inst->capabilities[PIPE].value;
+ codec_input->frame_rate = inst->max_rate;
+
+ port = is_decode_session(inst) ? INPUT_PORT : OUTPUT_PORT;
+ codec_input->frame_width = inst->fmts[port].fmt.pix_mp.width;
+ codec_input->frame_height = inst->fmts[port].fmt.pix_mp.height;
+
+ if (inst->capabilities[STAGE].value == MSM_VIDC_STAGE_1) {
+ codec_input->vsp_vpp_mode = CODEC_VSPVPP_MODE_1S;
+ } else if (inst->capabilities[STAGE].value == MSM_VIDC_STAGE_2) {
+ codec_input->vsp_vpp_mode = CODEC_VSPVPP_MODE_2S;
+ } else {
+ d_vpr_e("%s: invalid stage %d\n", __func__,
+ inst->capabilities[STAGE].value);
+ return -EINVAL;
+ }
+
+ if (inst->capabilities[BIT_DEPTH].value == BIT_DEPTH_8)
+ codec_input->bitdepth = CODEC_BITDEPTH_8;
+ else
+ codec_input->bitdepth = CODEC_BITDEPTH_10;
+
+ /*
+ * Used for calculating Encoder GOP Complexity
+ * hierachical_layer= 0..7 used as Array Index
+ * inst->capabilities[B_FRAME].value=[ 0 1 2]
+ * TODO how to map?
+ */
+
+ /* set as IPP */
+ codec_input->hierachical_layer = 0;
+
+ if (is_decode_session(inst))
+ color_fmt =
+ v4l2_colorformat_to_driver(inst,
+ inst->fmts[OUTPUT_PORT].fmt.pix_mp.pixelformat,
+ __func__);
+ else
+ color_fmt =
+ v4l2_colorformat_to_driver(inst,
+ inst->fmts[INPUT_PORT].fmt.pix_mp.pixelformat,
+ __func__);
+
+ codec_input->linear_opb = is_linear_colorformat(color_fmt);
+ codec_input->bitrate_mbps =
+ (codec_input->frame_rate * data_size * 8) / 1000000;
+
+ /* set as sanity mode */
+ codec_input->regression_mode = 1;
+
+ return 0;
+}
+
+static int msm_vidc_init_codec_input_bus(struct msm_vidc_inst *inst, struct vidc_bus_vote_data *d,
+ struct api_calculation_input *codec_input)
+{
+ u32 complexity_factor_int = 0, complexity_factor_frac = 0;
+ bool opb_compression_enabled = false;
+
+ if (!d)
+ return -EINVAL;
+
+ if (d->domain == MSM_VIDC_ENCODER) {
+ codec_input->decoder_or_encoder = CODEC_ENCODER;
+ } else if (d->domain == MSM_VIDC_DECODER) {
+ codec_input->decoder_or_encoder = CODEC_DECODER;
+ } else {
+ d_vpr_e("%s: invalid domain %d\n", __func__, d->domain);
+ return -EINVAL;
+ }
+
+ codec_input->chipset_gen = MSM_SM8550;
+
+ if (d->codec == MSM_VIDC_H264) {
+ codec_input->codec = CODEC_H264;
+ } else if (d->codec == MSM_VIDC_HEVC) {
+ codec_input->codec = CODEC_HEVC;
+ } else if (d->codec == MSM_VIDC_VP9) {
+ codec_input->codec = CODEC_VP9;
+ } else {
+ d_vpr_e("%s: invalid codec %d\n", __func__, d->codec);
+ return -EINVAL;
+ }
+
+ codec_input->lcu_size = d->lcu_size;
+ codec_input->pipe_num = d->num_vpp_pipes;
+ codec_input->frame_rate = d->fps;
+ codec_input->frame_width = d->input_width;
+ codec_input->frame_height = d->input_height;
+
+ if (d->work_mode == MSM_VIDC_STAGE_1) {
+ codec_input->vsp_vpp_mode = CODEC_VSPVPP_MODE_1S;
+ } else if (d->work_mode == MSM_VIDC_STAGE_2) {
+ codec_input->vsp_vpp_mode = CODEC_VSPVPP_MODE_2S;
+ } else {
+ d_vpr_e("%s: invalid stage %d\n", __func__, d->work_mode);
+ return -EINVAL;
+ }
+
+ if (inst->capabilities[ENTROPY_MODE].value ==
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC) {
+ codec_input->entropy_coding_mode = CODEC_ENTROPY_CODING_CABAC;
+ } else if (inst->capabilities[ENTROPY_MODE].value ==
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) {
+ codec_input->entropy_coding_mode = CODEC_ENTROPY_CODING_CAVLC;
+ } else {
+ d_vpr_e("%s: invalid entropy %d\n", __func__,
+ inst->capabilities[ENTROPY_MODE].value);
+ return -EINVAL;
+ }
+
+ /*
+ * Used for calculating Encoder GOP Complexity
+ * hierachical_layer= 0..7 used as Array Index
+ * TODO how to map?
+ */
+ codec_input->hierachical_layer = 0; /* set as IPP */
+
+ /*
+ * If the calculated motion_vector_complexity is > 2 then set the
+ * complexity_setting and refframe_complexity to be pwc(performance worst case)
+ * values. If the motion_vector_complexity is < 2 then set the complexity_setting
+ * and refframe_complexity to be average case values.
+ */
+
+ complexity_factor_int = Q16_INT(d->complexity_factor);
+ complexity_factor_frac = Q16_FRAC(d->complexity_factor);
+
+ if (complexity_factor_int < COMPLEXITY_THRESHOLD ||
+ (complexity_factor_int == COMPLEXITY_THRESHOLD &&
+ complexity_factor_frac == 0)) {
+ /* set as average case values */
+ codec_input->complexity_setting = COMPLEXITY_SETTING_AVG;
+ codec_input->refframe_complexity = REFFRAME_COMPLEXITY_AVG;
+ } else {
+ /* set as pwc */
+ codec_input->complexity_setting = COMPLEXITY_SETTING_PWC;
+ codec_input->refframe_complexity = REFFRAME_COMPLEXITY_PWC;
+ }
+
+ codec_input->status_llc_onoff = d->use_sys_cache;
+
+ if (__bpp(d->color_formats[0]) == 8)
+ codec_input->bitdepth = CODEC_BITDEPTH_8;
+ else
+ codec_input->bitdepth = CODEC_BITDEPTH_10;
+
+ if (d->num_formats == 1) {
+ codec_input->split_opb = 0;
+ codec_input->linear_opb = !__ubwc(d->color_formats[0]);
+ } else if (d->num_formats == 2) {
+ codec_input->split_opb = 1;
+ codec_input->linear_opb = !__ubwc(d->color_formats[1]);
+ } else {
+ d_vpr_e("%s: invalid num_formats %d\n",
+ __func__, d->num_formats);
+ return -EINVAL;
+ }
+
+ codec_input->linear_ipb = 0; /* set as ubwc ipb */
+
+ /* TODO Confirm if we always LOSSLESS mode ie lossy_ipb = 0*/
+ codec_input->lossy_ipb = 0; /* set as lossless ipb */
+
+ /* TODO Confirm if no multiref */
+ codec_input->encoder_multiref = 0; /* set as no multiref */
+ codec_input->bitrate_mbps = (d->bitrate / 1000000); /* bps 10; set as 10mbps */
+
+ opb_compression_enabled = d->num_formats >= 2 && __ubwc(d->color_formats[1]);
+
+ /* ANDROID CR is in Q16 format, StaticModel CR in x100 format */
+ codec_input->cr_dpb = ((Q16_INT(d->compression_ratio) * 100) +
+ Q16_FRAC(d->compression_ratio));
+
+ codec_input->cr_opb = opb_compression_enabled ?
+ codec_input->cr_dpb : 65536;
+
+ codec_input->cr_ipb = ((Q16_INT(d->input_cr) * 100) + Q16_FRAC(d->input_cr));
+ codec_input->cr_rpb = codec_input->cr_dpb; /* cr_rpb only for encoder */
+
+ /* disable by default, only enable for aurora depth map session */
+ codec_input->lumaonly_decode = 0;
+
+ /* set as custom regression mode, as are using cr,cf values from FW */
+ codec_input->regression_mode = REGRESSION_MODE_CUSTOM;
+
+ /* Dump all the variables for easier debugging */
+ if (msm_vidc_debug & VIDC_BUS) {
+ struct dump dump[] = {
+ {"complexity_factor_int", "%d", complexity_factor_int},
+ {"complexity_factor_frac", "%d", complexity_factor_frac},
+ {"refframe_complexity", "%d", codec_input->refframe_complexity},
+ {"complexity_setting", "%d", codec_input->complexity_setting},
+ {"cr_dpb", "%d", codec_input->cr_dpb},
+ {"cr_opb", "%d", codec_input->cr_opb},
+ {"cr_ipb", "%d", codec_input->cr_ipb},
+ {"cr_rpb", "%d", codec_input->cr_rpb},
+ {"lcu size", "%d", codec_input->lcu_size},
+ {"pipe number", "%d", codec_input->pipe_num},
+ {"frame_rate", "%d", codec_input->frame_rate},
+ {"frame_width", "%d", codec_input->frame_width},
+ {"frame_height", "%d", codec_input->frame_height},
+ {"work_mode", "%d", d->work_mode},
+ {"encoder_or_decode", "%d", inst->domain},
+ {"chipset_gen", "%d", codec_input->chipset_gen},
+ {"codec_input", "%d", codec_input->codec},
+ {"entropy_coding_mode", "%d", codec_input->entropy_coding_mode},
+ {"hierachical_layer", "%d", codec_input->hierachical_layer},
+ {"status_llc_onoff", "%d", codec_input->status_llc_onoff},
+ {"bit_depth", "%d", codec_input->bitdepth},
+ {"split_opb", "%d", codec_input->split_opb},
+ {"linear_opb", "%d", codec_input->linear_opb},
+ {"linear_ipb", "%d", codec_input->linear_ipb},
+ {"lossy_ipb", "%d", codec_input->lossy_ipb},
+ {"encoder_multiref", "%d", codec_input->encoder_multiref},
+ {"bitrate_mbps", "%d", codec_input->bitrate_mbps},
+ {"lumaonly_decode", "%d", codec_input->lumaonly_decode},
+ {"regression_mode", "%d", codec_input->regression_mode},
+ };
+ __dump(dump, ARRAY_SIZE(dump));
+ }
+
+ return 0;
+}
+
+u64 msm_vidc_calc_freq_iris3(struct msm_vidc_inst *inst, u32 data_size)
+{
+ u64 freq = 0;
+ struct msm_vidc_core *core;
+ int ret = 0;
+ struct api_calculation_input codec_input;
+ struct api_calculation_freq_output codec_output;
+ u32 fps, mbpf;
+
+ core = inst->core;
+
+ mbpf = msm_vidc_get_mbs_per_frame(inst);
+ fps = inst->max_rate;
+
+ memset(&codec_input, 0, sizeof(struct api_calculation_input));
+ memset(&codec_output, 0, sizeof(struct api_calculation_freq_output));
+ ret = msm_vidc_init_codec_input_freq(inst, data_size, &codec_input);
+ if (ret)
+ return freq;
+ ret = msm_vidc_calculate_frequency(codec_input, &codec_output);
+ if (ret)
+ return freq;
+ freq = codec_output.hw_min_freq * 1000000; /* Convert to Hz */
+
+ i_vpr_p(inst, "%s: filled len %d, required freq %llu, fps %u, mbpf %u\n",
+ __func__, data_size, freq, fps, mbpf);
+
+ if (inst->iframe && is_hevc_10bit_decode_session(inst)) {
+ /*
+ * for HEVC 10bit and iframe case only allow TURBO and
+ * limit to NOM for all other cases
+ */
+ } else {
+ /* limit to NOM, index 0 is TURBO, index 1 is NOM clock rate */
+ if (core->resource->freq_set.count >= 2 &&
+ freq > core->resource->freq_set.freq_tbl[1].freq)
+ freq = core->resource->freq_set.freq_tbl[1].freq;
+ }
+
+ return freq;
+}
+
+int msm_vidc_calc_bw_iris3(struct msm_vidc_inst *inst,
+ struct vidc_bus_vote_data *vidc_data)
+{
+ int ret = 0;
+ struct api_calculation_input codec_input;
+ struct api_calculation_bw_output codec_output;
+
+ if (!vidc_data)
+ return ret;
+
+ memset(&codec_input, 0, sizeof(struct api_calculation_input));
+ memset(&codec_output, 0, sizeof(struct api_calculation_bw_output));
+
+ ret = msm_vidc_init_codec_input_bus(inst, vidc_data, &codec_input);
+ if (ret)
+ return ret;
+ ret = msm_vidc_calculate_bandwidth(codec_input, &codec_output);
+ if (ret)
+ return ret;
+
+ vidc_data->calc_bw_ddr = kbps(codec_output.ddr_bw_rd + codec_output.ddr_bw_wr);
+ vidc_data->calc_bw_llcc = kbps(codec_output.noc_bw_rd + codec_output.noc_bw_wr);
+
+ i_vpr_l(inst, "%s: calc_bw_ddr %llu calc_bw_llcc %llu",
+ __func__, vidc_data->calc_bw_ddr, vidc_data->calc_bw_llcc);
+
+ return ret;
+}
--
2.7.4


2023-07-28 18:00:03

by Nicolas Dufresne

[permalink] [raw]
Subject: Re: [PATCH 00/33] Qualcomm video decoder/encoder driver

Hi Dmitry,

Le vendredi 28 juillet 2023 à 16:32 +0300, Dmitry Baryshkov a écrit :
> On 28/07/2023 16:23, Vikash Garodia wrote:
> > This patch series introduces support for Qualcomm new video acceleration
> > hardware architecture, used for video stream decoding/encoding. This driver
> > is based on new communication protocol between video hardware and application
> > processor.
> >
> > This driver comes with below capabilities:
> > - V4L2 complaint video driver with M2M and STREAMING capability.
> > - Supports H264, H265, VP9 decoders.
> > - Supports H264, H265 encoders.
> >
> > This driver comes with below features:
> > - Centralized resource and memory management.
> > - Centralized management of core and instance states.
> > - Defines platform specific capabilities and features. As a results, it provides
> > a single point of control to enable/disable a given feature depending on
> > specific platform capabilities.
> > - Handles hardware interdependent configurations. For a given configuration from
> > client, the driver checks for hardware dependent configuration/s and updates
> > the same.
> > - Handles multiple complex video scenarios involving state transitions - DRC,
> > Drain, Seek, back to back DRC, DRC during Drain sequence, DRC during Seek
> > sequence.
> > - Introduces a flexible way for driver to subscribe for any property with
> > hardware. Hardware would inform driver with those subscribed property with any
> > change in value.
> > - Introduces performance (clock and bus) model based on new hardware
> > architecture.
> > - Introduces multi thread safe design to handle communication between client and
> > hardware.
> > - Adapts encoder quality improvements available in new hardware architecture.
> > - Implements asynchronous communication with hardware to achieve better
> > experience in low latency usecases.
> > - Supports multi stage hardware architecture for encode/decode.
> > - Output and capture planes are controlled independently. Thereby providing a
> > way to reconfigure individual plane.
> > - Hardware packetization layer supports synchronization between configuration
> > packet and data packet.
> > - Introduces a flexibility to receive a hardware response for a given command
> > packet.
> > - Native hardware support of LAST flag which is mandatory to align with port
> > reconfiguration and DRAIN sequence as per V4L guidelines.
> > - Native hardware support for drain sequence.
> >
> > I think that the driver is in good shape for mainline kernel, and I hope the
> > review comments will help to improve it, so please do review, and make comments.
>
> No bindings, no driver. Please post start the series from the bindings.

In your next iteration, make sure to include full v4l2-compliance report in your
cover letter since we cannot assume maintainers.

In addition to this, we now ask for fluster scores for each of your supported
decoders. We expect the results to have no timeout, and ideally the
error/failure explained (aka unsupported resolution, profile, subsampling, bit
depth, etc.). Note that inter-resolution change is not possible with V4L2 today,
so no need to explain why these VP9 tests fails. Fluster supports V4L2 decoding
through GStreamer (gst-launch + video4linux plugin) and FFMPEG at the moment. It
will run through ITU conformance vectors for HEVC and H.264, and run through
libvpx and and chromium test vectors for VP9.

https://github.com/fluendo/fluster

regards,
Nicolas

>
> >
> > Dikshita Agarwal (17):
> > iris: vidc: add core functions
> > iris: add vidc wrapper file
> > iris: vidc: add vb2 ops
> > iris: vidc: add helpers for memory management
> > iris: vidc: add helper functions for resource management
> > iris: vidc: add helper functions for power management
> > iris: add helpers for media format
> > iris: vidc: add PIL functionality for video firmware
> > iris: platform: add platform files
> > iris: platform: sm8550: add capability file for sm8550
> > iris: variant: add helper functions for register handling
> > iris: variant: iris3: add iris3 specific ops
> > iris: variant: iris3: add helpers for buffer size calculations
> > iris: variant: iris3: add helper for bus and clock calculation
> > iris: variant: iris: implement the logic to compute bus bandwidth
> > iris: variant: iris3: implement logic to compute clock frequency
> > iris: enable building of iris video driver
> >
> > Vikash Garodia (16):
> > MAINTAINERS: Add Qualcomm Iris video accelerator driver
> > iris: vidc: add v4l2 wrapper file
> > iris: vidc: define video core and instance context
> > iris: iris: add video encoder files
> > iris: vidc: add video decoder files
> > iris: vidc: add control files
> > iris: vidc: add helper functions
> > iris: vidc: add helpers for state management
> > iris: add vidc buffer files
> > iris: vidc: define various structures and enum
> > iris: vidc: hfi: add Host Firmware Interface (HFI)
> > iris: vidc: hfi: add Host Firmware Interface (HFI) response handling
> > iris: vidc: hfi: add helpers for handling shared queues
> > iris: vidc: hfi: Add packetization layer
> > iris: vidc: hfi: defines HFI properties and enums
> > iris: vidc: add debug files
> >
> > MAINTAINERS | 10 +
> > drivers/media/platform/qcom/Kconfig | 1 +
> > drivers/media/platform/qcom/Makefile | 1 +
> > drivers/media/platform/qcom/iris/Kconfig | 15 +
> > drivers/media/platform/qcom/iris/Makefile | 46 +
> > .../iris/platform/common/inc/msm_vidc_platform.h | 305 ++
> > .../iris/platform/common/src/msm_vidc_platform.c | 2499 ++++++++++++
> > .../iris/platform/sm8550/inc/msm_vidc_sm8550.h | 14 +
> > .../iris/platform/sm8550/src/msm_vidc_sm8550.c | 1727 ++++++++
> > .../iris/variant/common/inc/msm_vidc_variant.h | 22 +
> > .../iris/variant/common/src/msm_vidc_variant.c | 163 +
> > .../qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h | 1481 +++++++
> > .../iris/variant/iris3/inc/msm_vidc_buffer_iris3.h | 19 +
> > .../qcom/iris/variant/iris3/inc/msm_vidc_iris3.h | 15 +
> > .../iris/variant/iris3/inc/msm_vidc_power_iris3.h | 17 +
> > .../iris/variant/iris3/inc/perf_static_model.h | 229 ++
> > .../iris/variant/iris3/src/msm_vidc_buffer_iris3.c | 595 +++
> > .../iris/variant/iris3/src/msm_vidc_bus_iris3.c | 884 ++++
> > .../iris/variant/iris3/src/msm_vidc_clock_iris3.c | 627 +++
> > .../qcom/iris/variant/iris3/src/msm_vidc_iris3.c | 954 +++++
> > .../iris/variant/iris3/src/msm_vidc_power_iris3.c | 345 ++
> > .../media/platform/qcom/iris/vidc/inc/firmware.h | 18 +
> > .../platform/qcom/iris/vidc/inc/hfi_command.h | 190 +
> > .../media/platform/qcom/iris/vidc/inc/hfi_packet.h | 52 +
> > .../platform/qcom/iris/vidc/inc/hfi_property.h | 666 +++
> > .../platform/qcom/iris/vidc/inc/msm_media_info.h | 599 +++
> > .../media/platform/qcom/iris/vidc/inc/msm_vdec.h | 40 +
> > .../media/platform/qcom/iris/vidc/inc/msm_venc.h | 34 +
> > .../media/platform/qcom/iris/vidc/inc/msm_vidc.h | 60 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_buffer.h | 32 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_control.h | 26 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_core.h | 165 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_debug.h | 186 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_driver.h | 352 ++
> > .../platform/qcom/iris/vidc/inc/msm_vidc_inst.h | 207 +
> > .../qcom/iris/vidc/inc/msm_vidc_internal.h | 787 ++++
> > .../platform/qcom/iris/vidc/inc/msm_vidc_memory.h | 83 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_power.h | 94 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_state.h | 102 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h | 77 +
> > .../platform/qcom/iris/vidc/inc/msm_vidc_vb2.h | 39 +
> > .../media/platform/qcom/iris/vidc/inc/resources.h | 259 ++
> > .../media/platform/qcom/iris/vidc/inc/venus_hfi.h | 66 +
> > .../platform/qcom/iris/vidc/inc/venus_hfi_queue.h | 89 +
> > .../qcom/iris/vidc/inc/venus_hfi_response.h | 26 +
> > .../media/platform/qcom/iris/vidc/src/firmware.c | 294 ++
> > .../media/platform/qcom/iris/vidc/src/hfi_packet.c | 657 +++
> > .../media/platform/qcom/iris/vidc/src/msm_vdec.c | 2091 ++++++++++
> > .../media/platform/qcom/iris/vidc/src/msm_venc.c | 1484 +++++++
> > .../media/platform/qcom/iris/vidc/src/msm_vidc.c | 841 ++++
> > .../platform/qcom/iris/vidc/src/msm_vidc_buffer.c | 290 ++
> > .../platform/qcom/iris/vidc/src/msm_vidc_control.c | 824 ++++
> > .../platform/qcom/iris/vidc/src/msm_vidc_debug.c | 581 +++
> > .../platform/qcom/iris/vidc/src/msm_vidc_driver.c | 4276 ++++++++++++++++++++
> > .../platform/qcom/iris/vidc/src/msm_vidc_memory.c | 448 ++
> > .../platform/qcom/iris/vidc/src/msm_vidc_power.c | 560 +++
> > .../platform/qcom/iris/vidc/src/msm_vidc_probe.c | 660 +++
> > .../platform/qcom/iris/vidc/src/msm_vidc_state.c | 1607 ++++++++
> > .../platform/qcom/iris/vidc/src/msm_vidc_v4l2.c | 953 +++++
> > .../platform/qcom/iris/vidc/src/msm_vidc_vb2.c | 605 +++
> > .../media/platform/qcom/iris/vidc/src/resources.c | 1321 ++++++
> > .../media/platform/qcom/iris/vidc/src/venus_hfi.c | 1503 +++++++
> > .../platform/qcom/iris/vidc/src/venus_hfi_queue.c | 537 +++
> > .../qcom/iris/vidc/src/venus_hfi_response.c | 1607 ++++++++
> > 64 files changed, 35357 insertions(+)
> > create mode 100644 drivers/media/platform/qcom/iris/Kconfig
> > create mode 100644 drivers/media/platform/qcom/iris/Makefile
> > create mode 100644 drivers/media/platform/qcom/iris/platform/common/inc/msm_vidc_platform.h
> > create mode 100644 drivers/media/platform/qcom/iris/platform/common/src/msm_vidc_platform.c
> > create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/inc/msm_vidc_sm8550.h
> > create mode 100644 drivers/media/platform/qcom/iris/platform/sm8550/src/msm_vidc_sm8550.c
> > create mode 100644 drivers/media/platform/qcom/iris/variant/common/inc/msm_vidc_variant.h
> > create mode 100644 drivers/media/platform/qcom/iris/variant/common/src/msm_vidc_variant.c
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/hfi_buffer_iris3.h
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_buffer_iris3.h
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_iris3.h
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/msm_vidc_power_iris3.h
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/inc/perf_static_model.h
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_buffer_iris3.c
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_bus_iris3.c
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_clock_iris3.c
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_iris3.c
> > create mode 100644 drivers/media/platform/qcom/iris/variant/iris3/src/msm_vidc_power_iris3.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/firmware.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_command.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_packet.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/hfi_property.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_media_info.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vdec.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_venc.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_buffer.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_control.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_core.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_driver.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_inst.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_internal.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_memory.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_power.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_state.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_v4l2.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_vb2.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/resources.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_queue.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/venus_hfi_response.h
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/firmware.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/hfi_packet.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vdec.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_venc.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_buffer.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_control.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_driver.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_memory.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_power.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_probe.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_state.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_v4l2.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_vb2.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/resources.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_queue.c
> > create mode 100644 drivers/media/platform/qcom/iris/vidc/src/venus_hfi_response.c
> >
>


2023-07-28 18:22:01

by Konrad Dybcio

[permalink] [raw]
Subject: Re: [PATCH 12/33] iris: vidc: add helper functions for resource management

On 28.07.2023 15:23, Vikash Garodia wrote:
> From: Dikshita Agarwal <[email protected]>
>
> This implements ops to initialize, enable and disable extrenal
> resources needed by video driver like power domains, clocks etc.
>
> Signed-off-by: Dikshita Agarwal <[email protected]>
> Signed-off-by: Vikash Garodia <[email protected]>
> ---
There's a whole bunch of kerneldoc abuses (comments should start with
/* and not /**). Make sure you have proper spaces between single-line
C-style comments (e.g. /*Get should be /* Get etc.)

Capitalizing the first word within the comment would be nice too.


Do we need a separate bus table? i.e. does it make sense to adjust the
bandwidth values separately from the clock rates?

Do you think there will be more than one set of msm_vidc_resources_ops?
Perhaps it'd make sense to drop that layer of abstraction if not. Many
function names could drop the __ prefix.

A whole bunch of d_vpr_h seem almost excessive.

MSM_VIDC_CLKFLAG_* are unused.

Konrad

2023-07-28 18:24:25

by Konrad Dybcio

[permalink] [raw]
Subject: Re: [PATCH 14/33] iris: vidc: add helpers for state management

On 28.07.2023 15:23, Vikash Garodia wrote:
> This implements the functions to handle different core
> and instance state transitions.
>
> Signed-off-by: Dikshita Agarwal <[email protected]>
> Signed-off-by: Vikash Garodia <[email protected]>
> ---
[...]

> +enum msm_vidc_core_sub_state {
> + CORE_SUBSTATE_NONE = 0x0,
> + CORE_SUBSTATE_POWER_ENABLE = BIT(0),
> + CORE_SUBSTATE_GDSC_HANDOFF = BIT(1),
> + CORE_SUBSTATE_PM_SUSPEND = BIT(2),
> + CORE_SUBSTATE_FW_PWR_CTRL = BIT(3),
> + CORE_SUBSTATE_PAGE_FAULT = BIT(4),
> + CORE_SUBSTATE_CPU_WATCHDOG = BIT(5),
> + CORE_SUBSTATE_VIDEO_UNRESPONSIVE = BIT(6),
> + CORE_SUBSTATE_MAX = BIT(7),
Why store it in an enum if they're not consecutive? You can make them
preprocessor #defines.

> +};
> +
> +enum msm_vidc_core_event_type {
> + CORE_EVENT_NONE = BIT(0),
> + CORE_EVENT_UPDATE_SUB_STATE = BIT(1),
> +};
Ditto (even though techinically they're consecutive)

> +
> +enum msm_vidc_state {
> + MSM_VIDC_OPEN,
> + MSM_VIDC_INPUT_STREAMING,
> + MSM_VIDC_OUTPUT_STREAMING,
> + MSM_VIDC_STREAMING,
> + MSM_VIDC_CLOSE,
> + MSM_VIDC_ERROR,
> +};
> +
> +#define MSM_VIDC_SUB_STATE_NONE 0
> +#define MSM_VIDC_MAX_SUB_STATES 6
> +/*
> + * max value of inst->sub_state if all
> + * the 6 valid bits are set i.e 111111==>63
> + */
> +#define MSM_VIDC_MAX_SUB_STATE_VALUE ((1 << MSM_VIDC_MAX_SUB_STATES) - 1)
> +
> +enum msm_vidc_sub_state {
> + MSM_VIDC_DRAIN = BIT(0),
> + MSM_VIDC_DRC = BIT(1),
> + MSM_VIDC_DRAIN_LAST_BUFFER = BIT(2),
> + MSM_VIDC_DRC_LAST_BUFFER = BIT(3),
> + MSM_VIDC_INPUT_PAUSE = BIT(4),
> + MSM_VIDC_OUTPUT_PAUSE = BIT(5),
Ditto

[...]

> +static int msm_vidc_core_init_wait_state(struct msm_vidc_core *core,
> + enum msm_vidc_core_event_type type,
> + struct msm_vidc_event_data *data)
> +{
> + int rc = 0;
rc seems never assigned again, good to drop

[...]

> +
> +static int msm_vidc_core_init_state(struct msm_vidc_core *core,
> + enum msm_vidc_core_event_type type,
> + struct msm_vidc_event_data *data)
> +{
> + int rc = 0;
Ditto

[...]

> +static int msm_vidc_core_error_state(struct msm_vidc_core *core,
> + enum msm_vidc_core_event_type type,
> + struct msm_vidc_event_data *data)
> +{
> + int rc = 0;
Ditto

[...]

> +int msm_vidc_update_core_state(struct msm_vidc_core *core,
> + enum msm_vidc_core_state request_state, const char *func)
> +{
> + struct msm_vidc_core_state_handle *state_handle = NULL;
> + int rc = 0;
Ditto

[...]

> +int msm_vidc_change_core_state(struct msm_vidc_core *core,
> + enum msm_vidc_core_state request_state, const char *func)
> +{
> + enum msm_vidc_allow allow;
> + int rc = 0;
Ditto

[...]

> +bool is_state(struct msm_vidc_inst *inst, enum msm_vidc_state state)
> +{
> + return inst->state == state;
> +}
> +
> +bool is_sub_state(struct msm_vidc_inst *inst, enum msm_vidc_sub_state sub_state)
> +{
> + return (inst->sub_state & sub_state);
> +}
Why are there 2 separate funcs for core and inst? Don't we have
a pointer within one to the other?


[...]

> +
> +int msm_vidc_update_state(struct msm_vidc_inst *inst,
> + enum msm_vidc_state request_state, const char *func)
> +{
> + struct msm_vidc_state_handle *state_handle = NULL;
> + int rc = 0;
rc is unused

[...]

> +static int msm_vidc_set_sub_state(struct msm_vidc_inst *inst,
> + enum msm_vidc_sub_state sub_state, const char *func)
> +{
> + char sub_state_name[MAX_NAME_LENGTH];
> + int cnt, rc = 0;
ditto

Konrad

2023-07-31 10:52:47

by Bryan O'Donoghue

[permalink] [raw]
Subject: Re: [PATCH 18/33] iris: vidc: hfi: add Host Firmware Interface (HFI)

On 28/07/2023 14:23, Vikash Garodia wrote:
> This implements the interface for communication between
> host driver and firmware through interface commands and messages.
>
> Signed-off-by: Dikshita Agarwal <[email protected]>
> Signed-off-by: Vikash Garodia <[email protected]>

More dead code here

drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c:542: //if
(core->last_packet_type != HFI_CMD_SYS_PC_PREP)
drivers/media/platform/qcom/iris/vidc/src/venus_hfi.c:543: //
core->skip_pc_count = 0;

---
bod


2023-07-31 23:15:21

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 24/33] iris: vidc: add debug files

On 28/07/2023 15:23, Vikash Garodia wrote:
> this implements the debugging framework.

Your commit msgs are not helping to understand why do you need it and
what is this doing. Based on this commit description I would ask you to
drop most of this code as it looks useless. Extend the commit msg to
provide proper justification and list of features each unit provides.

Please do not use "This commit/patch", but imperative mood. See longer
explanation here:
https://elixir.bootlin.com/linux/v5.17.1/source/Documentation/process/submitting-patches.rst#L95

>
> Signed-off-by: Dikshita Agarwal <[email protected]>
> Signed-off-by: Vikash Garodia <[email protected]>
> ---
> .../platform/qcom/iris/vidc/inc/msm_vidc_debug.h | 186 +++++++
> .../platform/qcom/iris/vidc/src/msm_vidc_debug.c | 581 +++++++++++++++++++++
> 2 files changed, 767 insertions(+)
> create mode 100644 drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
> create mode 100644 drivers/media/platform/qcom/iris/vidc/src/msm_vidc_debug.c
>
> diff --git a/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
> new file mode 100644
> index 0000000..ffced01
> --- /dev/null
> +++ b/drivers/media/platform/qcom/iris/vidc/inc/msm_vidc_debug.h
> @@ -0,0 +1,186 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#ifndef __MSM_VIDC_DEBUG__
> +#define __MSM_VIDC_DEBUG__
> +
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/types.h>
> +
> +struct msm_vidc_core;
> +struct msm_vidc_inst;
> +
> +#ifndef VIDC_DBG_LABEL
> +#define VIDC_DBG_LABEL "msm_vidc"
> +#endif

Drop these three. Don't re-invent Linux kernel API.

> +
> +/* Allow only 6 prints/sec */
> +#define VIDC_DBG_SESSION_RATELIMIT_INTERVAL (1 * HZ)
> +#define VIDC_DBG_SESSION_RATELIMIT_BURST 6
> +
> +#define VIDC_DBG_TAG_INST VIDC_DBG_LABEL ": %4s: %s: "
> +#define VIDC_DBG_TAG_CORE VIDC_DBG_LABEL ": %4s: %08x: %s: "
> +#define FW_DBG_TAG VIDC_DBG_LABEL ": %6s: "
> +#define DEFAULT_SID ((u32)-1)
> +
> +#ifndef MSM_VIDC_EMPTY_BRACE
> +#define MSM_VIDC_EMPTY_BRACE {},

That's the funniest code I saw since some time.

> +#endif
> +
> +extern unsigned int msm_vidc_debug;

Nope.

> +extern unsigned int msm_fw_debug;

Nope.

> +extern bool msm_vidc_fw_dump;

Nope.

> +
> +/* do not modify the log message as it is used in test scripts */
> +#define FMT_STRING_SET_CTRL \
> + "%s: state %s, name %s, id 0x%x value %d\n"
> +#define FMT_STRING_STATE_CHANGE \
> + "%s: state changed to %s from %s\n"
> +#define FMT_STRING_MSG_SFR \
> + "SFR Message from FW: %s\n"
> +#define FMT_STRING_FAULT_HANDLER \
> + "%s: faulting address: %lx\n"
> +#define FMT_STRING_SET_CAP \
> + "set cap: name: %24s, cap value: %#10x, hfi: %#10llx\n"
> +
> +/* To enable messages OR these values and
> + * echo the result to debugfs file.
> + *
> + * To enable all messages set msm_vidc_debug = 0x101F
> + */
> +
> +enum vidc_msg_prio_drv {
> + VIDC_ERR = 0x00000001,
> + VIDC_HIGH = 0x00000002,
> + VIDC_LOW = 0x00000004,
> + VIDC_PERF = 0x00000008,
> + VIDC_PKT = 0x00000010,
> + VIDC_BUS = 0x00000020,
> + VIDC_STAT = 0x00000040,
> + VIDC_ENCODER = 0x00000100,
> + VIDC_DECODER = 0x00000200,
> + VIDC_PRINTK = 0x10000000,
> + VIDC_FTRACE = 0x20000000,
> +};
> +
> +enum vidc_msg_prio_fw {
> + FW_LOW = 0x00000001,
> + FW_MED = 0x00000002,
> + FW_HIGH = 0x00000004,
> + FW_ERROR = 0x00000008,
> + FW_FATAL = 0x00000010,
> + FW_PERF = 0x00000020,
> + FW_CACHE_LOW = 0x00000100,
> + FW_CACHE_MED = 0x00000200,
> + FW_CACHE_HIGH = 0x00000400,
> + FW_CACHE_ERROR = 0x00000800,
> + FW_CACHE_FATAL = 0x00001000,
> + FW_CACHE_PERF = 0x00002000,
> + FW_PRINTK = 0x10000000,
> + FW_FTRACE = 0x20000000,
> +};
> +
> +#define DRV_LOG (VIDC_ERR | VIDC_PRINTK)
> +#define DRV_LOGSHIFT (0)
> +#define DRV_LOGMASK (0x0FFFFFFF)
> +
> +#define FW_LOG (FW_ERROR | FW_FATAL | FW_PRINTK)
> +#define FW_LOGSHIFT (0)
> +#define FW_LOGMASK (0x0FFFFFFF)
> +
> +#define dprintk_inst(__level, __level_str, inst, __fmt, ...) \
> + do { \
> + if (inst && (msm_vidc_debug & (__level))) { \
> + pr_info(VIDC_DBG_TAG_INST __fmt, \
> + __level_str, \
> + inst->debug_str, \
> + ##__VA_ARGS__); \
> + } \
> + } while (0)
> +
> +#define i_vpr_e(inst, __fmt, ...) dprintk_inst(VIDC_ERR, "err ", inst, __fmt, ##__VA_ARGS__)
> +#define i_vpr_i(inst, __fmt, ...) dprintk_inst(VIDC_HIGH, "high", inst, __fmt, ##__VA_ARGS__)
> +#define i_vpr_h(inst, __fmt, ...) dprintk_inst(VIDC_HIGH, "high", inst, __fmt, ##__VA_ARGS__)
> +#define i_vpr_l(inst, __fmt, ...) dprintk_inst(VIDC_LOW, "low ", inst, __fmt, ##__VA_ARGS__)
> +#define i_vpr_p(inst, __fmt, ...) dprintk_inst(VIDC_PERF, "perf", inst, __fmt, ##__VA_ARGS__)
> +#define i_vpr_t(inst, __fmt, ...) dprintk_inst(VIDC_PKT, "pkt ", inst, __fmt, ##__VA_ARGS__)
> +#define i_vpr_b(inst, __fmt, ...) dprintk_inst(VIDC_BUS, "bus ", inst, __fmt, ##__VA_ARGS__)

NAK for entire interface. Please use standard debugging functions, not
pr_info for everything.

dev_dbg, dev_info, dev_warn, dev_err. Only these.


> +#define i_vpr_s(inst, __fmt, ...) dprintk_inst(VIDC_STAT, "stat", inst, __fmt, ##__VA_ARGS__)
> +
> +#define i_vpr_hp(inst, __fmt, ...) \
> + dprintk_inst(VIDC_HIGH | VIDC_PERF, "high", inst, __fmt, ##__VA_ARGS__)
> +#define i_vpr_hs(inst, __fmt, ...) \
> + dprintk_inst(VIDC_HIGH | VIDC_STAT, "stat", inst, __fmt, ##__VA_ARGS__)
> +> +#define dprintk_core(__level, __level_str, __fmt, ...) \

NAK

> + do { \
> + if (msm_vidc_debug & (__level)) { \
> + pr_info(VIDC_DBG_TAG_CORE __fmt, \
> + __level_str, \
> + DEFAULT_SID, \
> + "codec", \
> + ##__VA_ARGS__); \
> + } \
> + } while (0)
> +
> +#define d_vpr_e(__fmt, ...) dprintk_core(VIDC_ERR, "err ", __fmt, ##__VA_ARGS__)
> +#define d_vpr_h(__fmt, ...) dprintk_core(VIDC_HIGH, "high", __fmt, ##__VA_ARGS__)
> +#define d_vpr_l(__fmt, ...) dprintk_core(VIDC_LOW, "low ", __fmt, ##__VA_ARGS__)
> +#define d_vpr_p(__fmt, ...) dprintk_core(VIDC_PERF, "perf", __fmt, ##__VA_ARGS__)
> +#define d_vpr_t(__fmt, ...) dprintk_core(VIDC_PKT, "pkt ", __fmt, ##__VA_ARGS__)
> +#define d_vpr_b(__fmt, ...) dprintk_core(VIDC_BUS, "bus ", __fmt, ##__VA_ARGS__)
> +#define d_vpr_s(__fmt, ...) dprintk_core(VIDC_STAT, "stat", __fmt, ##__VA_ARGS__)
> +#define d_vpr_hs(__fmt, ...) \
> + dprintk_core(VIDC_HIGH | VIDC_STAT, "high", __fmt, ##__VA_ARGS__)
> +
> +#define dprintk_ratelimit(__level, __level_str, __fmt, ...) \
> + do { \
> + if (msm_vidc_check_ratelimit()) { \
> + dprintk_core(__level, __level_str, __fmt, ##__VA_ARGS__); \
> + } \
> + } while (0)
> +
> +#define dprintk_firmware(__level, __fmt, ...) \
> + do { \
> + if ((msm_fw_debug & (__level)) & FW_PRINTK) { \
> + pr_info(FW_DBG_TAG __fmt, \
> + "fw", \
> + ##__VA_ARGS__); \
> + } \
> + } while (0)
> +
> +enum msm_vidc_debugfs_event {
> + MSM_VIDC_DEBUGFS_EVENT_ETB,
> + MSM_VIDC_DEBUGFS_EVENT_EBD,
> + MSM_VIDC_DEBUGFS_EVENT_FTB,
> + MSM_VIDC_DEBUGFS_EVENT_FBD,
> +};
> +
> +enum msm_vidc_bug_on_error {
> + MSM_VIDC_BUG_ON_FATAL = BIT(0),
> + MSM_VIDC_BUG_ON_NOC = BIT(1),
> + MSM_VIDC_BUG_ON_WD_TIMEOUT = BIT(2),
> +};
> +
> +struct dentry *msm_vidc_debugfs_init_drv(void);
> +struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core);
> +struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
> + struct dentry *parent);
> +void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst);
> +void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
> + enum msm_vidc_debugfs_event e);
> +int msm_vidc_check_ratelimit(void);
> +
> +static inline bool is_stats_enabled(void)
> +{
> + return !!(msm_vidc_debug & VIDC_STAT);

...

> +struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core)
> +{
> + struct dentry *dir = NULL;
> + char debugfs_name[MAX_DEBUGFS_NAME];
> + struct dentry *parent;
> +
> + if (!core->debugfs_parent) {
> + d_vpr_e("%s: invalid params\n", __func__);
> + goto failed_create_dir;
> + }
> + parent = core->debugfs_parent;
> +
> + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core");
> + dir = debugfs_create_dir(debugfs_name, parent);
> + if (IS_ERR_OR_NULL(dir)) {
> + dir = NULL;
> + d_vpr_e("Failed to create debugfs for msm_vidc\n");
> + goto failed_create_dir;
> + }
> + if (!debugfs_create_file("info", 0444, dir, core, &core_info_fops)) {
> + d_vpr_e("debugfs_create_file: fail\n");
> + goto failed_create_dir;
> + }
> +
> + if (!debugfs_create_file("stats_delay_ms", 0644, dir, core, &stats_delay_fops)) {
> + d_vpr_e("debugfs_create_file: fail\n");


What is this entire debugfs supposed to provide?



Best regards,
Krzysztof


2023-08-14 13:37:15

by Stanimir Varbanov

[permalink] [raw]
Subject: Re: [PATCH 00/33] Qualcomm video decoder/encoder driver

Hi Dmitry,

On 28.07.23 г. 17:01 ч., Dmitry Baryshkov wrote:
> On 28/07/2023 16:23, Vikash Garodia wrote:
>> This patch series introduces support for Qualcomm new video acceleration
>> hardware architecture, used for video stream decoding/encoding. This
>> driver
>> is based on new communication protocol between video hardware and
>> application
>> processor.
>>
>> This driver comes with below capabilities:
>> - V4L2 complaint video driver with M2M and STREAMING capability.
>> - Supports H264, H265, VP9 decoders.
>> - Supports H264, H265 encoders.
>
> Please describe, why is it impossible to support this hardware in the
> venus driver. We do not usually add new drivers for the new generations
> of the hardware, unless it is fully incompatible with the previous
> generations. Let me point you to camss or drm/msm drivers. They have
> successfully solved the issue of supporting multiple generations of the
> hardware in the same driver.
>
> Unless the "iris3" is completely different from all the previous
> generations, I strongly suggest spending time on restructuring existing
> venus driver and then adding support for the new hardware there instead
> of dumping out something completely new.

AFAIK the major differences are HW IP and firmware interface (by
firmware interface I mean a protocol, API and API behavior). The
firmware and its interface has been re-written to align closely with the
current v4l2 specs for encoders/decoders state machines [1][2]. On the
other side current mainline Venus driver firmware is following interface
similar to OpenMAX.

There are incompatibilities between both firmware interfaces which
cannot easily combined in a common driver. Even if there is a
possibility to do that it will lead us to a unreadable driver source
code and maintenance burden.

Vikash, could elaborate more on firmware interface differences.

[1]
https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-decoder.html

[2]
https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-encoder.html

--
regards,
Stan

2023-08-14 15:13:58

by Dmitry Baryshkov

[permalink] [raw]
Subject: Re: [PATCH 00/33] Qualcomm video decoder/encoder driver

Hi Stan,

On Mon, 14 Aug 2023 at 15:58, Stanimir Varbanov
<[email protected]> wrote:
>
> Hi Dmitry,
>
> On 28.07.23 г. 17:01 ч., Dmitry Baryshkov wrote:
> > On 28/07/2023 16:23, Vikash Garodia wrote:
> >> This patch series introduces support for Qualcomm new video acceleration
> >> hardware architecture, used for video stream decoding/encoding. This
> >> driver
> >> is based on new communication protocol between video hardware and
> >> application
> >> processor.
> >>
> >> This driver comes with below capabilities:
> >> - V4L2 complaint video driver with M2M and STREAMING capability.
> >> - Supports H264, H265, VP9 decoders.
> >> - Supports H264, H265 encoders.
> >
> > Please describe, why is it impossible to support this hardware in the
> > venus driver. We do not usually add new drivers for the new generations
> > of the hardware, unless it is fully incompatible with the previous
> > generations. Let me point you to camss or drm/msm drivers. They have
> > successfully solved the issue of supporting multiple generations of the
> > hardware in the same driver.
> >
> > Unless the "iris3" is completely different from all the previous
> > generations, I strongly suggest spending time on restructuring existing
> > venus driver and then adding support for the new hardware there instead
> > of dumping out something completely new.
>
> AFAIK the major differences are HW IP and firmware interface (by
> firmware interface I mean a protocol, API and API behavior). The
> firmware and its interface has been re-written to align closely with the
> current v4l2 specs for encoders/decoders state machines [1][2]. On the
> other side current mainline Venus driver firmware is following interface
> similar to OpenMAX.
>
> There are incompatibilities between both firmware interfaces which
> cannot easily combined in a common driver. Even if there is a
> possibility to do that it will lead us to a unreadable driver source
> code and maintenance burden.

Thank you for your explanation!

If the hardware is more or less the same, then the existing venus
driver should be refactored and split into hardware driver and the
firmware interface. Then iris3 can come up as a second driver
implementing support for new firmware interface but utilising common
hardware-related code.

> Vikash, could elaborate more on firmware interface differences.

Do we have any details on firmware versions that implement older
(OpenMAX-like) interface vs versions implementing new (v4l2-like)
interface?

> [1]
> https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-decoder.html
>
> [2]
> https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-encoder.html

--
With best wishes
Dmitry

2023-08-15 01:47:43

by Dikshita Agarwal

[permalink] [raw]
Subject: Re: [PATCH 14/33] iris: vidc: add helpers for state management



On 7/28/2023 11:22 PM, Konrad Dybcio wrote:
> On 28.07.2023 15:23, Vikash Garodia wrote:
>> This implements the functions to handle different core
>> and instance state transitions.
>>
>> Signed-off-by: Dikshita Agarwal <[email protected]>
>> Signed-off-by: Vikash Garodia <[email protected]>
>> ---
> [...]
>
>> +enum msm_vidc_core_sub_state {
>> + CORE_SUBSTATE_NONE = 0x0,
>> + CORE_SUBSTATE_POWER_ENABLE = BIT(0),
>> + CORE_SUBSTATE_GDSC_HANDOFF = BIT(1),
>> + CORE_SUBSTATE_PM_SUSPEND = BIT(2),
>> + CORE_SUBSTATE_FW_PWR_CTRL = BIT(3),
>> + CORE_SUBSTATE_PAGE_FAULT = BIT(4),
>> + CORE_SUBSTATE_CPU_WATCHDOG = BIT(5),
>> + CORE_SUBSTATE_VIDEO_UNRESPONSIVE = BIT(6),
>> + CORE_SUBSTATE_MAX = BIT(7),
> Why store it in an enum if they're not consecutive? You can make them
> preprocessor #defines.
>
I understand that these are not consecutive but a enum for these makes them
under one roof which is easy to read and maintain, we will loose this if
replaced with macro.
>> +};
>> +
>> +enum msm_vidc_core_event_type {
>> + CORE_EVENT_NONE = BIT(0),
>> + CORE_EVENT_UPDATE_SUB_STATE = BIT(1),
>> +};
> Ditto (even though techinically they're consecutive)
>
>> +
>> +enum msm_vidc_state {
>> + MSM_VIDC_OPEN,
>> + MSM_VIDC_INPUT_STREAMING,
>> + MSM_VIDC_OUTPUT_STREAMING,
>> + MSM_VIDC_STREAMING,
>> + MSM_VIDC_CLOSE,
>> + MSM_VIDC_ERROR,
>> +};
>> +
>> +#define MSM_VIDC_SUB_STATE_NONE 0
>> +#define MSM_VIDC_MAX_SUB_STATES 6
>> +/*
>> + * max value of inst->sub_state if all
>> + * the 6 valid bits are set i.e 111111==>63
>> + */
>> +#define MSM_VIDC_MAX_SUB_STATE_VALUE ((1 << MSM_VIDC_MAX_SUB_STATES) - 1)
>> +
>> +enum msm_vidc_sub_state {
>> + MSM_VIDC_DRAIN = BIT(0),
>> + MSM_VIDC_DRC = BIT(1),
>> + MSM_VIDC_DRAIN_LAST_BUFFER = BIT(2),
>> + MSM_VIDC_DRC_LAST_BUFFER = BIT(3),
>> + MSM_VIDC_INPUT_PAUSE = BIT(4),
>> + MSM_VIDC_OUTPUT_PAUSE = BIT(5),
> Ditto
>
these are bit wise and are being used in state machine. At a time, two or
more bits can be set to define the state of and instance, hence needed.

> [...]
>
>> +static int msm_vidc_core_init_wait_state(struct msm_vidc_core *core,
>> + enum msm_vidc_core_event_type type,
>> + struct msm_vidc_event_data *data)
>> +{
>> + int rc = 0;
> rc seems never assigned again, good to drop
>
> [...]
>
Sure, will remove in next version
>> +
>> +static int msm_vidc_core_init_state(struct msm_vidc_core *core,
>> + enum msm_vidc_core_event_type type,
>> + struct msm_vidc_event_data *data)
>> +{
>> + int rc = 0;
> Ditto
>
> [...]
>
>> +static int msm_vidc_core_error_state(struct msm_vidc_core *core,
>> + enum msm_vidc_core_event_type type,
>> + struct msm_vidc_event_data *data)
>> +{
>> + int rc = 0;
> Ditto
>
> [...]
>
>> +int msm_vidc_update_core_state(struct msm_vidc_core *core,
>> + enum msm_vidc_core_state request_state, const char *func)
>> +{
>> + struct msm_vidc_core_state_handle *state_handle = NULL;
>> + int rc = 0;
> Ditto
>
> [...]
>
>> +int msm_vidc_change_core_state(struct msm_vidc_core *core,
>> + enum msm_vidc_core_state request_state, const char *func)
>> +{
>> + enum msm_vidc_allow allow;
>> + int rc = 0;
> Ditto
>
will remove all such instances of unused rc in next version
> [...]
>
>> +bool is_state(struct msm_vidc_inst *inst, enum msm_vidc_state state)
>> +{
>> + return inst->state == state;
>> +}
>> +
>> +bool is_sub_state(struct msm_vidc_inst *inst, enum msm_vidc_sub_state sub_state)
>> +{
>> + return (inst->sub_state & sub_state);
>> +}
> Why are there 2 separate funcs for core and inst? Don't we have
> a pointer within one to the other?
>
>
core and instance sub states are maintained differently for ex in SSR, we
need to check the core sub state, if we combine instance and core state
checks, we won't know against which sub state we should check.
> [...]
>

>> +
>> +int msm_vidc_update_state(struct msm_vidc_inst *inst,
>> + enum msm_vidc_state request_state, const char *func)
>> +{
>> + struct msm_vidc_state_handle *state_handle = NULL;
>> + int rc = 0;
> rc is unused
>
> [...]
>
>> +static int msm_vidc_set_sub_state(struct msm_vidc_inst *inst,
>> + enum msm_vidc_sub_state sub_state, const char *func)
>> +{
>> + char sub_state_name[MAX_NAME_LENGTH];
>> + int cnt, rc = 0;
> ditto
>
Thanks for pointing these out, will remove all unused rc.

Thanks,
Dikshita
> Konrad

2023-08-20 16:39:11

by Dikshita Agarwal

[permalink] [raw]
Subject: Re: [PATCH 18/33] iris: vidc: hfi: add Host Firmware Interface (HFI)



On 7/28/2023 9:28 PM, Bryan O'Donoghue wrote:
> On 28/07/2023 14:23, Vikash Garodia wrote:
>> +    rc = hfi_packet_sys_intraframe_powercollapse(core, core->packet,
>> +                             core->packet_size, enable);
>> +    if (rc)
>> +        return rc;
>
> I'm 99.9999999999 % sure this is misnamed.
>
> "Inter" means in-between two things.
> "Intra" means inside of one thing.
>
> So "intraframe" means inside of one frame "interframe" would mean power
> collapsing in-between two frames, which is what I think this does.
>
> And I'd still rather be adding inter-frame power-collapse to as many
> different versions of the existing silicon and new silicon as opposed to
> segregating it off in a new driver.
>
> I'm assuming that more than sm8550 supports it since @ the end of the day
> this is a firmware feature to power-collapse during an active session when
> we aren't busy.
>
You are actually 100% correct here, it is indeed inter frame power collapse,
Will rename this api with hfi_packet_sys_interframe_powercollapse

Thanks,
Dikshita
> ---
> bod