@@ -6,6 +6,7 @@ iris-objs += iris_probe.o \
iris_vidc.o \
iris_vdec.o \
iris_state.o \
+ iris_ctrls.o \
iris_helpers.o \
iris_hfi.o \
iris_hfi_response.o \
@@ -50,8 +50,10 @@
* @header_id: id of packet header
* @packet_id: id of packet
* @vpu_ops: a pointer to vpu ops
+ * @dec_codecs_count: supported codec count for decoder
* @platform_data: a structure for platform data
* @cap: an array for supported core capabilities
+ * @inst_caps: a pointer to supported instance capabilities
* @instances: a list_head of all instances
* @intr_status: interrupt status
*/
@@ -89,8 +91,10 @@ struct iris_core {
u32 header_id;
u32 packet_id;
const struct vpu_ops *vpu_ops;
+ u32 dec_codecs_count;
struct platform_data *platform_data;
struct plat_core_cap cap[CORE_CAP_MAX + 1];
+ struct plat_inst_caps *inst_caps;
struct list_head instances;
u32 intr_status;
};
new file mode 100644
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/list.h>
+
+#include "platform_common.h"
+#include "iris_helpers.h"
+#include "iris_hfi_packet.h"
+#include "iris_hfi.h"
+#include "iris_ctrls.h"
+
+#define MIN_CAPTURE_BUFFERS 4
+#define MIN_OUTPUT_BUFFERS 4
+
+static inline bool are_all_childrens_visited(struct plat_inst_cap *cap,
+ bool lookup[INST_CAP_MAX])
+{
+ bool found = true;
+ int i;
+
+ for (i = 0; i < MAX_NUM_CHILD; i++) {
+ if (cap->children[i] == INST_CAP_NONE)
+ continue;
+
+ if (!lookup[cap->children[i]]) {
+ found = false;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static bool is_valid_cap_id(enum plat_inst_cap_type cap_id)
+{
+ return cap_id > INST_CAP_NONE && cap_id < INST_CAP_MAX;
+}
+
+static enum plat_inst_cap_type get_cap_id(struct iris_inst *inst, u32 id)
+{
+ enum plat_inst_cap_type iter = INST_CAP_NONE;
+ enum plat_inst_cap_type cap_id = INST_CAP_NONE;
+
+ do {
+ if (inst->cap[iter].v4l2_id == id) {
+ cap_id = inst->cap[iter].cap_id;
+ break;
+ }
+ iter++;
+ } while (iter < INST_CAP_MAX);
+
+ return cap_id;
+}
+
+static int add_node_list(struct list_head *list, enum plat_inst_cap_type cap_id)
+{
+ struct cap_entry *entry = NULL;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&entry->list);
+ entry->cap_id = cap_id;
+ list_add(&entry->list, list);
+
+ return 0;
+}
+
+static int add_children(struct list_head *list,
+ struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id)
+{
+ struct plat_inst_cap *cap;
+ int i, ret = 0;
+
+ cap = &inst->cap[cap_id];
+
+ for (i = 0; i < MAX_NUM_CHILD; i++) {
+ if (!cap->children[i])
+ break;
+
+ if (!is_valid_cap_id(cap->children[i]))
+ continue;
+
+ ret = add_node_list(list, cap->children[i]);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int adjust_cap(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id,
+ struct v4l2_ctrl *ctrl)
+{
+ struct plat_inst_cap *cap;
+
+ cap = &inst->cap[cap_id];
+ if (!inst->cap[cap_id].cap_id)
+ return 0;
+
+ if (!cap->adjust) {
+ if (ctrl)
+ inst->cap[cap_id].value = ctrl->val;
+ return 0;
+ }
+
+ return cap->adjust(inst, ctrl);
+}
+
+static int adjust_dynamic_property(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id,
+ struct v4l2_ctrl *ctrl,
+ struct list_head *firmware_list,
+ struct list_head *children_list,
+ bool cap_present[INST_CAP_MAX])
+{
+ struct cap_entry *entry = NULL, *temp = NULL;
+ struct plat_inst_cap *cap;
+ s32 prev_value;
+ int ret;
+
+ cap = &inst->cap[0];
+
+ if (!(cap[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED))
+ return -EBUSY;
+
+ prev_value = cap[cap_id].value;
+ ret = adjust_cap(inst, cap_id, ctrl);
+ if (ret)
+ return ret;
+
+ ret = add_node_list(firmware_list, cap_id);
+ if (ret)
+ return ret;
+ cap_present[cap->cap_id] = true;
+
+ if (cap[cap_id].value == prev_value)
+ return 0;
+
+ ret = add_children(children_list, inst, cap_id);
+ if (ret)
+ return ret;
+
+ list_for_each_entry_safe(entry, temp, children_list, list) {
+ if (!cap[entry->cap_id].adjust) {
+ list_del_init(&entry->list);
+ kfree(entry);
+ continue;
+ }
+
+ prev_value = cap[entry->cap_id].value;
+ ret = adjust_cap(inst, entry->cap_id, NULL);
+ if (ret)
+ return ret;
+
+ if (cap[entry->cap_id].value != prev_value) {
+ if (!cap_present[cap->cap_id]) {
+ ret = add_node_list(firmware_list, cap_id);
+ if (ret)
+ return ret;
+ cap_present[cap->cap_id] = true;
+ }
+
+ ret = add_children(children_list, inst, entry->cap_id);
+ if (ret)
+ return ret;
+ }
+
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+
+ if (!list_empty(children_list))
+ return -EINVAL;
+
+ return ret;
+}
+
+static int set_dynamic_property(struct iris_inst *inst,
+ struct list_head *firmware_list)
+{
+ struct cap_entry *entry = NULL, *temp = NULL;
+ struct plat_inst_cap *cap;
+ int ret = 0;
+
+ list_for_each_entry_safe(entry, temp, firmware_list, list) {
+ cap = &inst->cap[entry->cap_id];
+ if (cap->set) {
+ ret = cap->set(inst, entry->cap_id);
+ if (ret)
+ return -EINVAL;
+ }
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+
+ return ret;
+}
+
+static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ enum plat_inst_cap_type cap_id;
+ struct iris_inst *inst = NULL;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ ctrl->val = MIN_CAPTURE_BUFFERS;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ ctrl->val = MIN_OUTPUT_BUFFERS;
+ break;
+ default:
+ inst = container_of(ctrl->handler, struct iris_inst, ctrl_handler);
+ cap_id = get_cap_id(inst, ctrl->id);
+ if (is_valid_cap_id(cap_id))
+ ctrl->val = inst->cap[cap_id].value;
+ else
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct cap_entry *entry = NULL, *temp = NULL;
+ struct list_head children_list, firmware_list;
+ enum plat_inst_cap_type cap_id;
+ bool cap_present[INST_CAP_MAX];
+ struct plat_inst_cap *cap;
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = container_of(ctrl->handler, struct iris_inst, ctrl_handler);
+ cap = &inst->cap[0];
+
+ INIT_LIST_HEAD(&firmware_list);
+ INIT_LIST_HEAD(&children_list);
+ memset(&cap_present, 0, sizeof(cap_present));
+
+ cap_id = get_cap_id(inst, ctrl->id);
+ if (!is_valid_cap_id(cap_id))
+ return -EINVAL;
+
+ cap[cap_id].flags |= CAP_FLAG_CLIENT_SET;
+
+ if (!inst->vb2q_src->streaming) {
+ inst->cap[cap_id].value = ctrl->val;
+ } else {
+ ret = adjust_dynamic_property(inst, cap_id, ctrl,
+ &firmware_list, &children_list,
+ cap_present);
+ if (ret)
+ goto free_list;
+
+ ret = set_dynamic_property(inst, &firmware_list);
+ }
+
+free_list:
+ list_for_each_entry_safe(entry, temp, &children_list, list) {
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+
+ list_for_each_entry_safe(entry, temp, &firmware_list, list) {
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ctrl_ops = {
+ .s_ctrl = vdec_op_s_ctrl,
+ .g_volatile_ctrl = vdec_op_g_volatile_ctrl,
+};
+
+int ctrls_init(struct iris_inst *inst)
+{
+ int num_ctrls = 0, ctrl_idx = 0;
+ struct plat_inst_cap *cap;
+ struct iris_core *core;
+ int idx = 0;
+ int ret = 0;
+
+ core = inst->core;
+ cap = &inst->cap[0];
+
+ for (idx = 0; idx < INST_CAP_MAX; idx++) {
+ if (cap[idx].v4l2_id)
+ num_ctrls++;
+ }
+ if (!num_ctrls)
+ return -EINVAL;
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler,
+ INST_CAP_MAX * core->dec_codecs_count);
+ if (ret)
+ return ret;
+
+ for (idx = 0; idx < INST_CAP_MAX; idx++) {
+ struct v4l2_ctrl *ctrl;
+
+ if (!cap[idx].v4l2_id)
+ continue;
+
+ if (ctrl_idx >= num_ctrls) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (cap[idx].flags & CAP_FLAG_MENU) {
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler,
+ &ctrl_ops,
+ cap[idx].v4l2_id,
+ cap[idx].max,
+ ~(cap[idx].step_or_mask),
+ cap[idx].value);
+ } else {
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
+ &ctrl_ops,
+ cap[idx].v4l2_id,
+ cap[idx].min,
+ cap[idx].max,
+ cap[idx].step_or_mask,
+ cap[idx].value);
+ }
+ if (!ctrl) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = inst->ctrl_handler.error;
+ if (ret)
+ goto error;
+
+ if ((cap[idx].flags & CAP_FLAG_VOLATILE) ||
+ (ctrl->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE ||
+ ctrl->id == V4L2_CID_MIN_BUFFERS_FOR_OUTPUT))
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+ ctrl_idx++;
+ }
+ inst->num_ctrls = num_ctrls;
+
+ return 0;
+error:
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+
+ return ret;
+}
+
+int iris_init_core_caps(struct iris_core *core)
+{
+ struct plat_core_cap *core_platform_data;
+ int i, num_core_caps;
+
+ core_platform_data = core->platform_data->core_data;
+ if (!core_platform_data)
+ return -EINVAL;
+
+ num_core_caps = core->platform_data->core_data_size;
+
+ for (i = 0; i < num_core_caps && i < CORE_CAP_MAX; i++) {
+ core->cap[core_platform_data[i].type].type = core_platform_data[i].type;
+ core->cap[core_platform_data[i].type].value = core_platform_data[i].value;
+ }
+
+ return 0;
+}
+
+static int update_inst_capability(struct plat_inst_cap *in,
+ struct plat_inst_caps *capability)
+{
+ if (!in || !capability)
+ return -EINVAL;
+
+ if (in->cap_id >= INST_CAP_MAX)
+ return -EINVAL;
+
+ capability->cap[in->cap_id].cap_id = in->cap_id;
+ capability->cap[in->cap_id].min = in->min;
+ capability->cap[in->cap_id].max = in->max;
+ capability->cap[in->cap_id].step_or_mask = in->step_or_mask;
+ capability->cap[in->cap_id].value = in->value;
+ capability->cap[in->cap_id].flags = in->flags;
+ capability->cap[in->cap_id].v4l2_id = in->v4l2_id;
+ capability->cap[in->cap_id].hfi_id = in->hfi_id;
+ memcpy(capability->cap[in->cap_id].children, in->children,
+ sizeof(capability->cap[in->cap_id].children));
+ capability->cap[in->cap_id].adjust = in->adjust;
+ capability->cap[in->cap_id].set = in->set;
+
+ return 0;
+}
+
+int iris_init_instance_caps(struct iris_core *core)
+{
+ struct plat_inst_cap *inst_plat_cap_data = NULL;
+ u8 dec_codecs_count = 0;
+ int num_inst_cap;
+ u32 dec_valid_codecs;
+ int i, j, check_bit = 0;
+ int ret = 0;
+
+ inst_plat_cap_data = core->platform_data->inst_cap_data;
+ if (!inst_plat_cap_data)
+ return -EINVAL;
+
+ dec_valid_codecs = core->cap[DEC_CODECS].value;
+ dec_codecs_count = hweight32(dec_valid_codecs);
+ core->dec_codecs_count = dec_codecs_count;
+
+ core->inst_caps = devm_kzalloc(core->dev,
+ dec_codecs_count * sizeof(struct plat_inst_caps),
+ GFP_KERNEL);
+ if (!core->inst_caps)
+ return -ENOMEM;
+
+ for (i = 0; i < dec_codecs_count; i++) {
+ while (check_bit < (sizeof(dec_valid_codecs) * 8)) {
+ if (dec_valid_codecs & BIT(check_bit)) {
+ core->inst_caps[i].codec = dec_valid_codecs &
+ BIT(check_bit);
+ check_bit++;
+ break;
+ }
+ check_bit++;
+ }
+ }
+
+ num_inst_cap = core->platform_data->inst_cap_data_size;
+
+ for (i = 0; i < num_inst_cap; i++) {
+ for (j = 0; j < dec_codecs_count; j++) {
+ if ((inst_plat_cap_data[i].codec &
+ core->inst_caps[j].codec)) {
+ ret = update_inst_capability(&inst_plat_cap_data[i],
+ &core->inst_caps[j]);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int get_inst_capability(struct iris_inst *inst)
+{
+ struct iris_core *core;
+ int i;
+
+ core = inst->core;
+
+ for (i = 0; i < core->dec_codecs_count; i++) {
+ if (core->inst_caps[i].codec == inst->codec) {
+ memcpy(&inst->cap[0], &core->inst_caps[i].cap[0],
+ (INST_CAP_MAX + 1) * sizeof(struct plat_inst_cap));
+ }
+ }
+
+ return 0;
+}
+
+int prepare_dependency_list(struct iris_inst *inst)
+{
+ struct cap_entry *entry = NULL, *temp = NULL;
+ struct plat_inst_cap *cap, *temp_cap;
+ int caps_to_prepare, pending_list_counter,
+ pending_at_start = 0;
+ struct list_head prepared_list, pending_list;
+ bool is_prepared[INST_CAP_MAX];
+ bool is_pending[INST_CAP_MAX];
+ int i, ret = 0;
+
+ cap = &inst->cap[0];
+
+ if (!list_empty(&inst->caps_list))
+ return 0;
+
+ INIT_LIST_HEAD(&prepared_list);
+ INIT_LIST_HEAD(&pending_list);
+ memset(&is_prepared, 0, sizeof(is_prepared));
+ memset(&is_pending, 0, sizeof(is_pending));
+
+ for (i = 1; i < INST_CAP_MAX; i++) {
+ temp_cap = &cap[i];
+ if (!is_valid_cap_id(temp_cap->cap_id))
+ continue;
+
+ if (!temp_cap->children[0]) {
+ if (!is_prepared[temp_cap->cap_id]) {
+ ret = add_node_list(&prepared_list, temp_cap->cap_id);
+ if (ret)
+ goto free_list;
+ is_prepared[temp_cap->cap_id] = true;
+ }
+ } else {
+ if (!is_pending[temp_cap->cap_id]) {
+ ret = add_node_list(&pending_list, temp_cap->cap_id);
+ if (ret)
+ goto free_list;
+ is_pending[temp_cap->cap_id] = true;
+ }
+ }
+ }
+
+ list_for_each_entry(entry, &pending_list, list)
+ pending_at_start++;
+
+ caps_to_prepare = pending_at_start;
+ pending_list_counter = pending_at_start;
+
+ list_for_each_entry_safe(entry, temp, &pending_list, list) {
+ list_del_init(&entry->list);
+ is_pending[entry->cap_id] = false;
+ pending_list_counter--;
+ temp_cap = &cap[entry->cap_id];
+
+ if (are_all_childrens_visited(temp_cap, is_prepared)) {
+ list_add(&entry->list, &prepared_list);
+ is_prepared[entry->cap_id] = true;
+ caps_to_prepare--;
+ } else {
+ list_add_tail(&entry->list, &pending_list);
+ is_pending[entry->cap_id] = true;
+ }
+
+ if (!pending_list_counter) {
+ if (pending_at_start == caps_to_prepare) {
+ ret = -EINVAL;
+ goto free_list;
+ }
+ pending_at_start = caps_to_prepare;
+ pending_list_counter = caps_to_prepare;
+ }
+ }
+
+ if (!list_empty(&pending_list)) {
+ ret = -EINVAL;
+ goto free_list;
+ }
+
+ list_replace_init(&prepared_list, &inst->caps_list);
+
+free_list:
+ list_for_each_entry_safe(entry, temp, &pending_list, list) {
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+ list_for_each_entry_safe(entry, temp, &prepared_list, list) {
+ list_del_init(&entry->list);
+ kfree(entry);
+ }
+
+ return ret;
+}
+
+int set_u32_enum(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id)
+{
+ u32 hfi_value = inst->cap[cap_id].value;
+ u32 hfi_id = inst->cap[cap_id].hfi_id;
+
+ return iris_hfi_set_property(inst, hfi_id, HFI_HOST_FLAGS_NONE,
+ get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32_ENUM,
+ &hfi_value, sizeof(u32));
+}
+
+int set_u32(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id)
+{
+ u32 hfi_value = inst->cap[cap_id].value;
+ u32 hfi_id = inst->cap[cap_id].hfi_id;
+
+ return iris_hfi_set_property(inst, hfi_id, HFI_HOST_FLAGS_NONE,
+ get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &hfi_value, sizeof(u32));
+}
+
+int set_stage(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id)
+{
+ struct v4l2_format *inp_f;
+ u32 work_mode = STAGE_2;
+ u32 width, height;
+ u32 hfi_id;
+
+ hfi_id = inst->cap[cap_id].hfi_id;
+
+ inp_f = inst->fmt_src;
+ height = inp_f->fmt.pix_mp.height;
+ width = inp_f->fmt.pix_mp.width;
+ if (res_is_less_than(width, height, 1280, 720))
+ work_mode = STAGE_1;
+
+ return iris_hfi_set_property(inst, hfi_id, HFI_HOST_FLAGS_NONE,
+ get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &work_mode, sizeof(u32));
+}
+
+int set_pipe(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id)
+{
+ u32 work_route, hfi_id;
+
+ work_route = inst->cap[PIPE].value;
+ hfi_id = inst->cap[cap_id].hfi_id;
+
+ return iris_hfi_set_property(inst, hfi_id, HFI_HOST_FLAGS_NONE,
+ get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &work_route, sizeof(u32));
+}
+
+int adjust_v4l2_properties(struct iris_inst *inst)
+{
+ struct cap_entry *entry = NULL, *temp = NULL;
+ int ret = 0;
+
+ list_for_each_entry_safe(entry, temp, &inst->caps_list, list) {
+ ret = adjust_cap(inst, entry->cap_id, NULL);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+int adjust_output_order(struct iris_inst *inst, struct v4l2_ctrl *ctrl)
+{
+ s32 display_delay = -1, display_delay_enable = -1;
+ u32 adjusted_value;
+
+ adjusted_value = ctrl ? ctrl->val :
+ inst->cap[OUTPUT_ORDER].value;
+
+ display_delay = inst->cap[DISPLAY_DELAY].value;
+ display_delay_enable = inst->cap[DISPLAY_DELAY_ENABLE].value;
+
+ if (display_delay_enable && !display_delay)
+ adjusted_value = 1;
+
+ inst->cap[OUTPUT_ORDER].value = adjusted_value;
+
+ return 0;
+}
+
+int adjust_profile(struct iris_inst *inst, struct v4l2_ctrl *ctrl)
+{
+ u32 adjusted_value;
+ s32 pix_fmt = -1;
+
+ adjusted_value = ctrl ? ctrl->val :
+ inst->cap[PROFILE].value;
+
+ pix_fmt = inst->cap[PIX_FMTS].value;
+
+ if (pix_fmt == FMT_TP10C)
+ adjusted_value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10;
+ else
+ adjusted_value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN;
+
+ inst->cap[PROFILE].value = adjusted_value;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _IRIS_CTRLS_H_
+#define _IRIS_CTRLS_H_
+
+#include "iris_instance.h"
+
+struct cap_entry {
+ struct list_head list;
+ enum plat_inst_cap_type cap_id;
+};
+
+int set_u32_enum(struct iris_inst *inst, enum plat_inst_cap_type cap_id);
+int set_stage(struct iris_inst *inst, enum plat_inst_cap_type cap_id);
+int set_pipe(struct iris_inst *inst, enum plat_inst_cap_type cap_id);
+int set_u32(struct iris_inst *inst, enum plat_inst_cap_type cap_id);
+int adjust_output_order(struct iris_inst *inst, struct v4l2_ctrl *ctrl);
+int adjust_profile(struct iris_inst *inst, struct v4l2_ctrl *ctrl);
+int vidc_ctrl_handler_deinit(struct iris_inst *inst);
+int prepare_dependency_list(struct iris_inst *inst);
+int iris_init_instance_caps(struct iris_core *core);
+int iris_init_core_caps(struct iris_core *core);
+int get_inst_capability(struct iris_inst *inst);
+int adjust_v4l2_properties(struct iris_inst *inst);
+int ctrls_init(struct iris_inst *inst);
+
+#endif
@@ -5,6 +5,7 @@
#include "iris_helpers.h"
#include "iris_hfi.h"
+#include "iris_hfi_packet.h"
#include "iris_instance.h"
int check_core_lock(struct iris_core *core)
@@ -16,23 +17,29 @@ int check_core_lock(struct iris_core *core)
return fatal ? -EINVAL : 0;
}
-int iris_init_core_caps(struct iris_core *core)
+bool res_is_less_than(u32 width, u32 height,
+ u32 ref_width, u32 ref_height)
{
- struct plat_core_cap *core_platform_data;
- int i, num_core_caps;
+ u32 num_mbs = NUM_MBS_PER_FRAME(height, width);
+ u32 max_side = max(ref_width, ref_height);
- core_platform_data = core->platform_data->core_data;
- if (!core_platform_data)
- return -EINVAL;
+ if (num_mbs < NUM_MBS_PER_FRAME(ref_height, ref_width) &&
+ width < max_side &&
+ height < max_side)
+ return true;
- num_core_caps = core->platform_data->core_data_size;
+ return false;
+}
- for (i = 0; i < num_core_caps && i < CORE_CAP_MAX; i++) {
- core->cap[core_platform_data[i].type].type = core_platform_data[i].type;
- core->cap[core_platform_data[i].type].value = core_platform_data[i].value;
- }
+u32 get_port_info(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id)
+{
+ if (inst->cap[cap_id].flags & CAP_FLAG_INPUT_PORT)
+ return HFI_PORT_BITSTREAM;
+ else if (inst->cap[cap_id].flags & CAP_FLAG_OUTPUT_PORT)
+ return HFI_PORT_RAW;
- return 0;
+ return HFI_PORT_NONE;
}
static int process_inst_timeout(struct iris_inst *inst)
@@ -6,11 +6,22 @@
#ifndef _IRIS_HELPERS_H_
#define _IRIS_HELPERS_H_
+#include <linux/align.h>
+#include <linux/types.h>
+
#include "iris_core.h"
#include "iris_instance.h"
+#include "platform_common.h"
+
+#define NUM_MBS_PER_FRAME(__height, __width) \
+ ((ALIGN(__height, 16) / 16) * (ALIGN(__width, 16) / 16))
int check_core_lock(struct iris_core *core);
-int iris_init_core_caps(struct iris_core *core);
+bool res_is_less_than(u32 width, u32 height,
+ u32 ref_width, u32 ref_height);
+u32 get_port_info(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id);
+
int close_session(struct iris_inst *inst);
#endif
@@ -10,6 +10,7 @@
#include "iris_hfi.h"
#include "iris_hfi_packet.h"
#include "iris_hfi_response.h"
+#include "iris_helpers.h"
#include "vpu_common.h"
static int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt)
@@ -280,3 +281,31 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data)
return IRQ_HANDLED;
}
+
+int iris_hfi_set_property(struct iris_inst *inst,
+ u32 packet_type, u32 flag, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size)
+{
+ struct iris_core *core;
+ int ret;
+
+ core = inst->core;
+ mutex_lock(&core->lock);
+
+ ret = hfi_packet_session_property(inst,
+ packet_type,
+ flag,
+ plane,
+ payload_type,
+ payload,
+ payload_size);
+ if (ret)
+ goto unlock;
+
+ ret = iris_hfi_queue_cmd_write(core, inst->packet);
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
@@ -13,6 +13,9 @@ int iris_hfi_core_init(struct iris_core *core);
int iris_hfi_core_deinit(struct iris_core *core);
int iris_hfi_session_open(struct iris_inst *inst);
int iris_hfi_session_close(struct iris_inst *inst);
+int iris_hfi_set_property(struct iris_inst *inst,
+ u32 packet_type, u32 flag, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size);
irqreturn_t iris_hfi_isr(int irq, void *data);
irqreturn_t iris_hfi_isr_handler(int irq, void *data);
@@ -239,3 +239,29 @@ int hfi_packet_session_command(struct iris_inst *inst, u32 pkt_type,
return ret;
}
+
+int hfi_packet_session_property(struct iris_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload, u32 payload_size)
+{
+ struct iris_core *core;
+ int ret;
+
+ core = inst->core;
+
+ ret = hfi_create_header(inst->packet, inst->packet_size,
+ inst->session_id, core->header_id++);
+ if (ret)
+ return ret;
+
+ ret = hfi_create_packet(inst->packet, inst->packet_size,
+ pkt_type,
+ flags,
+ payload_type,
+ port,
+ core->packet_id++,
+ payload,
+ payload_size);
+
+ return ret;
+}
@@ -74,5 +74,8 @@ int hfi_packet_session_command(struct iris_inst *inst, u32 pkt_type,
u32 flags, u32 port, u32 session_id,
u32 payload_type, void *payload,
u32 payload_size);
+int hfi_packet_session_property(struct iris_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload, u32 payload_size);
#endif
@@ -10,6 +10,7 @@
#include "iris_core.h"
#include "iris_common.h"
+#include "platform_common.h"
/**
* struct iris_inst - holds per video instance parameters
@@ -27,6 +28,10 @@
* @packet: HFI packet
* @packet_size: HFI packet size
* @completions: structure of signal completions
+ * @cap: array of supported instance capabilities
+ * @num_ctrls: supported number of controls
+ * @caps_list: list head of capability
+ * @codec: codec type
*/
struct iris_inst {
@@ -43,6 +48,10 @@ struct iris_inst {
void *packet;
u32 packet_size;
struct completion completions[MAX_SIGNAL];
+ struct plat_inst_cap cap[INST_CAP_MAX + 1];
+ u32 num_ctrls;
+ struct list_head caps_list;
+ enum codec_type codec;
};
#endif
@@ -13,6 +13,7 @@
#include "iris_hfi.h"
#include "resources.h"
#include "iris_vidc.h"
+#include "iris_ctrls.h"
static int init_iris_isr(struct iris_core *core)
{
@@ -158,6 +159,13 @@ static int iris_probe(struct platform_device *pdev)
return ret;
}
+ ret = iris_init_instance_caps(core);
+ if (ret) {
+ dev_err_probe(core->dev, ret,
+ "%s: init inst caps failed with %d\n", __func__, ret);
+ return ret;
+ }
+
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret)
return ret;
@@ -9,6 +9,7 @@
#include "iris_instance.h"
#include "iris_vdec.h"
#include "iris_vidc.h"
+#include "iris_ctrls.h"
static int vidc_v4l2_fh_init(struct iris_inst *inst)
{
@@ -174,6 +175,10 @@ int vidc_open(struct file *filp)
if (ret)
goto fail_free_inst;
+ INIT_LIST_HEAD(&inst->caps_list);
+ for (i = 0; i < MAX_SIGNAL; i++)
+ init_completion(&inst->completions[i]);
+
ret = vidc_v4l2_fh_init(inst);
if (ret)
goto fail_remove_session;
@@ -184,6 +189,14 @@ int vidc_open(struct file *filp)
if (ret)
goto fail_inst_deinit;
+ ret = get_inst_capability(inst);
+ if (ret)
+ goto fail_queue_deinit;
+
+ ret = ctrls_init(inst);
+ if (ret)
+ goto fail_queue_deinit;
+
ret = iris_hfi_session_open(inst);
if (ret) {
dev_err(core->dev, "%s: session open failed\n", __func__);
@@ -194,7 +207,9 @@ int vidc_open(struct file *filp)
return 0;
fail_core_deinit:
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
iris_core_deinit(core);
+fail_queue_deinit:
vidc_vb2_queue_deinit(inst);
fail_inst_deinit:
vdec_inst_deinit(inst);
@@ -216,6 +231,7 @@ int vidc_close(struct file *filp)
if (!inst)
return -EINVAL;
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
vdec_inst_deinit(inst);
close_session(inst);
vidc_vb2_queue_deinit(inst);
@@ -7,8 +7,10 @@
#define _PLATFORM_COMMON_H_
#include <linux/bits.h>
+#include <media/v4l2-ctrls.h>
struct iris_core;
+struct iris_inst;
#define HW_RESPONSE_TIMEOUT_VALUE (1000)
@@ -17,6 +19,7 @@ struct iris_core;
#define CODED_FRAMES_PROGRESSIVE 0x0
#define CODED_FRAMES_INTERLACE 0x1
+#define MAX_NUM_CHILD 10
#define DEFAULT_MAX_HOST_BUF_COUNT 64
#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
@@ -163,6 +166,16 @@ struct plat_inst_cap {
u32 v4l2_id;
u32 hfi_id;
enum plat_inst_cap_flags flags;
+ enum plat_inst_cap_type children[MAX_NUM_CHILD];
+ int (*adjust)(struct iris_inst *inst,
+ struct v4l2_ctrl *ctrl);
+ int (*set)(struct iris_inst *inst,
+ enum plat_inst_cap_type cap_id);
+};
+
+struct plat_inst_caps {
+ enum codec_type codec;
+ struct plat_inst_cap cap[INST_CAP_MAX + 1];
};
struct platform_data {
@@ -9,6 +9,7 @@
#include <media/v4l2-ctrls.h>
#include "hfi_defines.h"
+#include "iris_ctrls.h"
#include "platform_common.h"
#include "resources.h"
@@ -47,7 +48,16 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
FMT_NV12 | FMT_NV21 | FMT_NV12C,
FMT_NV12C},
- {PIX_FMTS, HEVC | VP9,
+ {PIX_FMTS, HEVC,
+ FMT_NV12,
+ FMT_TP10C,
+ FMT_NV12 | FMT_NV21 | FMT_NV12C | FMT_TP10C,
+ FMT_NV12C,
+ 0, 0,
+ CAP_FLAG_NONE,
+ {PROFILE}},
+
+ {PIX_FMTS, VP9,
FMT_NV12,
FMT_TP10C,
FMT_NV12 | FMT_NV21 | FMT_NV12C | FMT_TP10C,
@@ -88,7 +98,10 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
V4L2_CID_MPEG_VIDEO_H264_PROFILE,
HFI_PROP_PROFILE,
- CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU},
+ CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ {0},
+ NULL,
+ set_u32_enum},
{PROFILE, HEVC,
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
@@ -99,7 +112,10 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
HFI_PROP_PROFILE,
- CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU},
+ CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ {0},
+ adjust_profile,
+ set_u32_enum},
{PROFILE, VP9,
V4L2_MPEG_VIDEO_VP9_PROFILE_0,
@@ -109,7 +125,10 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
V4L2_MPEG_VIDEO_VP9_PROFILE_0,
V4L2_CID_MPEG_VIDEO_VP9_PROFILE,
HFI_PROP_PROFILE,
- CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU},
+ CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ {0},
+ NULL,
+ set_u32_enum},
{LEVEL, H264,
V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
@@ -137,7 +156,10 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
V4L2_MPEG_VIDEO_H264_LEVEL_6_1,
V4L2_CID_MPEG_VIDEO_H264_LEVEL,
HFI_PROP_LEVEL,
- CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU},
+ CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ {0},
+ NULL,
+ set_u32_enum},
{LEVEL, HEVC,
V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
@@ -158,7 +180,10 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
HFI_PROP_LEVEL,
- CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU},
+ CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ {0},
+ NULL,
+ set_u32_enum},
{LEVEL, VP9,
V4L2_MPEG_VIDEO_VP9_LEVEL_1_0,
@@ -178,7 +203,10 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
V4L2_MPEG_VIDEO_VP9_LEVEL_6_0,
V4L2_CID_MPEG_VIDEO_VP9_LEVEL,
HFI_PROP_LEVEL,
- CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU},
+ CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ {0},
+ NULL,
+ set_u32_enum},
{HEVC_TIER, HEVC,
V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
@@ -188,46 +216,69 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
V4L2_CID_MPEG_VIDEO_HEVC_TIER,
HFI_PROP_TIER,
- CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU},
+ CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ {0},
+ NULL,
+ set_u32_enum},
{DISPLAY_DELAY_ENABLE, CODECS_ALL,
0, 1, 1, 0,
V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE,
HFI_PROP_DECODE_ORDER_OUTPUT,
- CAP_FLAG_INPUT_PORT},
+ CAP_FLAG_INPUT_PORT,
+ {OUTPUT_ORDER},
+ NULL,
+ NULL},
{DISPLAY_DELAY, CODECS_ALL,
0, 1, 1, 0,
V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY,
HFI_PROP_DECODE_ORDER_OUTPUT,
- CAP_FLAG_INPUT_PORT},
+ CAP_FLAG_INPUT_PORT,
+ {OUTPUT_ORDER},
+ NULL,
+ NULL},
{OUTPUT_ORDER, CODECS_ALL,
0, 1, 1, 0,
0,
HFI_PROP_DECODE_ORDER_OUTPUT,
- CAP_FLAG_INPUT_PORT},
+ CAP_FLAG_INPUT_PORT,
+ {0},
+ adjust_output_order,
+ set_u32},
{INPUT_BUF_HOST_MAX_COUNT, CODECS_ALL,
DEFAULT_MAX_HOST_BUF_COUNT, DEFAULT_MAX_HOST_BURST_BUF_COUNT,
1, DEFAULT_MAX_HOST_BUF_COUNT,
0,
HFI_PROP_BUFFER_HOST_MAX_COUNT,
- CAP_FLAG_INPUT_PORT},
+ CAP_FLAG_INPUT_PORT,
+ {0},
+ NULL,
+ set_u32},
{STAGE, CODECS_ALL,
STAGE_1,
STAGE_2, 1,
STAGE_2,
0,
- HFI_PROP_STAGE},
+ HFI_PROP_STAGE,
+ CAP_FLAG_NONE,
+ {0},
+ NULL,
+ set_stage},
{PIPE, CODECS_ALL,
PIPE_1,
PIPE_4, 1,
PIPE_4,
0,
- HFI_PROP_PIPE},
+ HFI_PROP_PIPE,
+ CAP_FLAG_NONE,
+ {0},
+ NULL,
+ set_pipe},
{POC, H264, 0, 2, 1, 1,
0,
@@ -252,7 +303,10 @@ static struct plat_inst_cap instance_cap_data_sm8550[] = {
0, 1, 1, 1,
0,
HFI_PROP_DEC_START_FROM_RAP_FRAME,
- CAP_FLAG_INPUT_PORT},
+ CAP_FLAG_INPUT_PORT,
+ {0},
+ NULL,
+ set_u32},
};
static const struct bus_info sm8550_bus_table[] = {
Implement iris v4l2_ctrl_ops - s_ctrl and g_volatile_ctrl. Core capability structure has all the supported capabilities for all supported codecs. Initializes instance capability from core capability based on the codec type. Add following to each capability: Children: define dependencies for a specific capability. Adjust: define function to adjust the value of capability based on client configuration or dependency with other capability. Set: define function to set the adjusted value to firmware. Prepare dependency graph for all inter-dependent capabilities. This is used to adjust the value of capabilities and set the same to firmware. Signed-off-by: Dikshita Agarwal <quic_dikshita@quicinc.com> --- drivers/media/platform/qcom/vcodec/iris/Makefile | 1 + .../media/platform/qcom/vcodec/iris/iris_core.h | 4 + .../media/platform/qcom/vcodec/iris/iris_ctrls.c | 679 +++++++++++++++++++++ .../media/platform/qcom/vcodec/iris/iris_ctrls.h | 30 + .../media/platform/qcom/vcodec/iris/iris_helpers.c | 31 +- .../media/platform/qcom/vcodec/iris/iris_helpers.h | 13 +- drivers/media/platform/qcom/vcodec/iris/iris_hfi.c | 29 + drivers/media/platform/qcom/vcodec/iris/iris_hfi.h | 3 + .../platform/qcom/vcodec/iris/iris_hfi_packet.c | 26 + .../platform/qcom/vcodec/iris/iris_hfi_packet.h | 3 + .../platform/qcom/vcodec/iris/iris_instance.h | 9 + .../media/platform/qcom/vcodec/iris/iris_probe.c | 8 + .../media/platform/qcom/vcodec/iris/iris_vidc.c | 16 + .../platform/qcom/vcodec/iris/platform_common.h | 13 + .../platform/qcom/vcodec/iris/platform_sm8550.c | 84 ++- 15 files changed, 921 insertions(+), 28 deletions(-) create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_ctrls.c create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_ctrls.h