@@ -6,6 +6,7 @@ iris-objs += iris_probe.o \
iris_vidc.o \
iris_vb2.o \
iris_vdec.o \
+ iris_venc.o \
iris_state.o \
iris_ctrls.o \
iris_helpers.o \
@@ -25,7 +25,8 @@
* @vdev_dec: iris video device structure for decoder
* @vdev_enc: iris video device structure for encoder
* @v4l2_file_ops: iris v4l2 file ops
- * @v4l2_ioctl_ops: iris v4l2 ioctl ops
+ * @v4l2_ioctl_ops_dec: iris v4l2 ioctl ops for decoder
+ * @v4l2_ioctl_ops_enc: iris v4l2 ioctl ops for encoder
* @bus_tbl: table of iris buses
* @bus_count: count of iris buses
* @power_domain_tbl: table of iris power domains
@@ -52,6 +53,7 @@
* @packet_id: id of packet
* @vpu_ops: a pointer to vpu ops
* @session_ops: a pointer to session level ops
+ * @enc_codecs_count: supported codec count for encoder
* @dec_codecs_count: supported codec count for decoder
* @platform_data: a structure for platform data
* @cap: an array for supported core capabilities
@@ -69,7 +71,8 @@ struct iris_core {
struct video_device *vdev_dec;
struct video_device *vdev_enc;
const struct v4l2_file_operations *v4l2_file_ops;
- const struct v4l2_ioctl_ops *v4l2_ioctl_ops;
+ const struct v4l2_ioctl_ops *v4l2_ioctl_ops_dec;
+ const struct v4l2_ioctl_ops *v4l2_ioctl_ops_enc;
struct bus_info *bus_tbl;
u32 bus_count;
struct power_domain_info *power_domain_tbl;
@@ -97,6 +100,7 @@ struct iris_core {
const struct vpu_ops *vpu_ops;
const struct vpu_session_ops *session_ops;
u32 dec_codecs_count;
+ u32 enc_codecs_count;
struct platform_data *platform_data;
struct plat_core_cap cap[CORE_CAP_MAX + 1];
struct plat_inst_caps *inst_caps;
@@ -217,7 +217,7 @@ static int set_dynamic_property(struct iris_inst *inst,
return ret;
}
-static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+static int iris_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
enum plat_inst_cap_type cap_id;
struct iris_inst *inst = NULL;
@@ -242,7 +242,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
return ret;
}
-static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
+static int iris_op_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct cap_entry *entry = NULL, *temp = NULL;
struct list_head children_list, firmware_list;
@@ -273,7 +273,8 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
cap[cap_id].flags |= CAP_FLAG_CLIENT_SET;
- if (!inst->vb2q_src->streaming) {
+ if ((inst->domain == ENCODER && !inst->vb2q_dst->streaming) ||
+ (inst->domain == DECODER && !inst->vb2q_src->streaming)) {
inst->cap[cap_id].value = ctrl->val;
} else {
ret = adjust_dynamic_property(inst, cap_id, ctrl,
@@ -300,16 +301,16 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
}
static const struct v4l2_ctrl_ops ctrl_ops = {
- .s_ctrl = vdec_op_s_ctrl,
- .g_volatile_ctrl = vdec_op_g_volatile_ctrl,
+ .s_ctrl = iris_op_s_ctrl,
+ .g_volatile_ctrl = iris_op_g_volatile_ctrl,
};
int ctrls_init(struct iris_inst *inst, bool init)
{
int num_ctrls = 0, ctrl_idx = 0;
+ u64 codecs_count, step_or_mask;
struct plat_inst_cap *cap;
struct iris_core *core;
- u64 step_or_mask;
int idx = 0;
int ret = 0;
@@ -324,8 +325,11 @@ int ctrls_init(struct iris_inst *inst, bool init)
return -EINVAL;
if (init) {
+ codecs_count = inst->domain == ENCODER ?
+ core->enc_codecs_count :
+ core->dec_codecs_count;
ret = v4l2_ctrl_handler_init(&inst->ctrl_handler,
- INST_CAP_MAX * core->dec_codecs_count);
+ INST_CAP_MAX * codecs_count);
if (ret)
return ret;
}
@@ -451,29 +455,49 @@ static int update_inst_capability(struct plat_inst_cap *in,
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;
+ u8 enc_codecs_count = 0, dec_codecs_count = 0;
+ u32 enc_valid_codecs, dec_valid_codecs;
int i, j, check_bit = 0;
+ u8 codecs_count = 0;
+ int num_inst_cap;
int ret = 0;
inst_plat_cap_data = core->platform_data->inst_cap_data;
if (!inst_plat_cap_data)
return -EINVAL;
+ enc_valid_codecs = core->cap[ENC_CODECS].value;
+ enc_codecs_count = hweight32(enc_valid_codecs);
+ core->enc_codecs_count = enc_codecs_count;
+
dec_valid_codecs = core->cap[DEC_CODECS].value;
dec_codecs_count = hweight32(dec_valid_codecs);
core->dec_codecs_count = dec_codecs_count;
+ codecs_count = enc_codecs_count + dec_codecs_count;
core->inst_caps = devm_kzalloc(core->dev,
- dec_codecs_count * sizeof(struct plat_inst_caps),
+ codecs_count * sizeof(struct plat_inst_caps),
GFP_KERNEL);
if (!core->inst_caps)
return -ENOMEM;
- for (i = 0; i < dec_codecs_count; i++) {
+ for (i = 0; i < enc_codecs_count; i++) {
+ while (check_bit < (sizeof(enc_valid_codecs) * 8)) {
+ if (enc_valid_codecs & BIT(check_bit)) {
+ core->inst_caps[i].domain = ENCODER;
+ core->inst_caps[i].codec = enc_valid_codecs &
+ BIT(check_bit);
+ check_bit++;
+ break;
+ }
+ check_bit++;
+ }
+ }
+
+ for (; i < codecs_count; i++) {
while (check_bit < (sizeof(dec_valid_codecs) * 8)) {
if (dec_valid_codecs & BIT(check_bit)) {
+ core->inst_caps[i].domain = DECODER;
core->inst_caps[i].codec = dec_valid_codecs &
BIT(check_bit);
check_bit++;
@@ -486,9 +510,9 @@ int iris_init_instance_caps(struct iris_core *core)
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)) {
+ for (j = 0; j < codecs_count; j++) {
+ if ((inst_plat_cap_data[i].domain & core->inst_caps[j].domain) &&
+ (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)
@@ -503,11 +527,14 @@ int iris_init_instance_caps(struct iris_core *core)
int get_inst_capability(struct iris_inst *inst)
{
struct iris_core *core;
+ u32 codecs_count = 0;
int i;
core = inst->core;
- for (i = 0; i < core->dec_codecs_count; i++) {
+ codecs_count = core->enc_codecs_count + core->dec_codecs_count;
+
+ for (i = 0; i < 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));
@@ -7,6 +7,7 @@
#include "hfi_defines.h"
#include "iris_core.h"
+#include "iris_ctrls.h"
#include "iris_helpers.h"
#include "iris_hfi.h"
#include "iris_hfi_packet.h"
@@ -273,7 +274,7 @@ int close_session(struct iris_inst *inst)
return ret;
}
-static int check_core_mbps_mbpf(struct iris_inst *inst)
+int check_core_mbps_mbpf(struct iris_inst *inst)
{
u32 mbpf = 0, mbps = 0, total_mbpf = 0, total_mbps = 0;
struct iris_core *core;
@@ -284,7 +285,9 @@ static int check_core_mbps_mbpf(struct iris_inst *inst)
mutex_lock(&core->lock);
list_for_each_entry(instance, &core->instances, list) {
- fps = inst->cap[QUEUED_RATE].value >> 16;
+ fps = max3(inst->cap[QUEUED_RATE].value >> 16,
+ inst->cap[FRAME_RATE].value >> 16,
+ inst->cap[OPERATING_RATE].value >> 16);
mbpf = get_mbpf(inst);
mbps = mbpf * fps;
total_mbpf += mbpf;
@@ -814,6 +817,88 @@ int session_streamoff(struct iris_inst *inst, u32 plane)
return ret;
}
+int process_resume(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state clear_sub_state = IRIS_INST_SUB_NONE;
+ int ret;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST) {
+ clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+ } else if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) {
+ clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_DRAIN);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_DRAIN);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+ }
+
+ ret = iris_inst_change_sub_state(inst, clear_sub_state, 0);
+
+ return ret;
+}
+
+int codec_change(struct iris_inst *inst, u32 v4l2_codec)
+{
+ bool session_init = false;
+ int ret;
+
+ if (!inst->codec)
+ session_init = true;
+
+ if (inst->codec &&
+ ((inst->domain == DECODER && inst->fmt_src->fmt.pix_mp.pixelformat == v4l2_codec) ||
+ (inst->domain == ENCODER && inst->fmt_dst->fmt.pix_mp.pixelformat == v4l2_codec)))
+ return 0;
+
+ inst->codec = v4l2_codec_to_driver(inst, v4l2_codec);
+ if (!inst->codec)
+ return -EINVAL;
+
+ if (inst->domain == DECODER)
+ inst->fmt_src->fmt.pix_mp.pixelformat = v4l2_codec;
+ else if (inst->domain == ENCODER)
+ inst->fmt_dst->fmt.pix_mp.pixelformat = v4l2_codec;
+
+ ret = get_inst_capability(inst);
+ if (ret)
+ return ret;
+
+ ret = ctrls_init(inst, session_init);
+ if (ret)
+ return ret;
+
+ ret = update_buffer_count(inst, INPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = update_buffer_count(inst, OUTPUT_MPLANE);
+
+ return ret;
+}
+
int iris_pm_get(struct iris_core *core)
{
int ret;
@@ -41,6 +41,7 @@ enum codec_type v4l2_codec_to_driver(struct iris_inst *inst, u32 v4l2_codec);
u32 v4l2_colorformat_from_driver(struct iris_inst *inst, enum colorformat_type colorformat);
enum colorformat_type v4l2_colorformat_to_driver(struct iris_inst *inst, u32 v4l2_colorformat);
struct vb2_queue *get_vb2q(struct iris_inst *inst, u32 type);
+int check_core_mbps_mbpf(struct iris_inst *inst);
int check_session_supported(struct iris_inst *inst);
struct iris_buffer *get_driver_buf(struct iris_inst *inst, u32 plane, u32 index);
@@ -51,6 +52,8 @@ int iris_vb2_buffer_done(struct iris_inst *inst,
int iris_release_nonref_buffers(struct iris_inst *inst);
void iris_destroy_buffers(struct iris_inst *inst);
int session_streamoff(struct iris_inst *inst, u32 plane);
+int process_resume(struct iris_inst *inst);
+int codec_change(struct iris_inst *inst, u32 v4l2_codec);
int iris_pm_get(struct iris_core *core);
int iris_pm_put(struct iris_core *core, bool autosuspend);
int iris_pm_get_put(struct iris_core *core);
@@ -8,6 +8,7 @@
#include <media/v4l2-ctrls.h>
+#include "hfi_defines.h"
#include "iris_buffer.h"
#include "iris_common.h"
#include "iris_core.h"
@@ -29,6 +30,7 @@
* @fmt_dst: structure of v4l2_format for destination
* @ctrl_handler: reference of v4l2 ctrl handler
* @crop: structure of crop info
+ * @compose: structure of compose info
* @packet: HFI packet
* @packet_size: HFI packet size
* @completions: structure of signal completions
@@ -36,6 +38,7 @@
* @num_ctrls: supported number of controls
* @caps_list: list head of capability
* @codec: codec type
+ * @domain: domain type: encoder or decoder
* @buffers: structure of buffer info
* @fw_min_count: minimnum count of buffers needed by fw
* @state: instance state
@@ -70,6 +73,7 @@ struct iris_inst {
struct v4l2_format *fmt_dst;
struct v4l2_ctrl_handler ctrl_handler;
struct rect_desc crop;
+ struct rect_desc compose;
void *packet;
u32 packet_size;
struct completion completions[MAX_SIGNAL];
@@ -77,6 +81,7 @@ struct iris_inst {
u32 num_ctrls;
struct list_head caps_list;
enum codec_type codec;
+ enum domain_type domain;
struct iris_buffers_info buffers;
u32 fw_min_count;
enum iris_inst_state state;
@@ -42,13 +42,13 @@ static int iris_register_video_device(struct iris_core *core, enum domain_type t
vdev->release = video_device_release;
vdev->fops = core->v4l2_file_ops;
- vdev->ioctl_ops = core->v4l2_ioctl_ops;
vdev->vfl_dir = VFL_DIR_M2M;
vdev->v4l2_dev = &core->v4l2_dev;
vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
if (type == DECODER) {
strscpy(vdev->name, "qcom-iris-decoder", sizeof(vdev->name));
+ vdev->ioctl_ops = core->v4l2_ioctl_ops_dec;
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret)
@@ -57,6 +57,7 @@ static int iris_register_video_device(struct iris_core *core, enum domain_type t
core->vdev_dec = vdev;
} else if (type == ENCODER) {
strscpy(vdev->name, "qcom-iris-encoder", sizeof(vdev->name));
+ vdev->ioctl_ops = core->v4l2_ioctl_ops_enc;
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret)
@@ -198,8 +198,9 @@ bool allow_s_ctrl(struct iris_inst *inst, u32 cap_id)
{
return ((inst->state == IRIS_INST_OPEN) ||
((inst->cap[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED) &&
- (inst->state == IRIS_INST_INPUT_STREAMING ||
- inst->state == IRIS_INST_STREAMING)));
+ ((inst->state == IRIS_INST_INPUT_STREAMING && inst->domain == DECODER) ||
+ (inst->state == IRIS_INST_OUTPUT_STREAMING && inst->domain == ENCODER) ||
+ (inst->state == IRIS_INST_STREAMING))));
}
int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane)
@@ -22,39 +22,6 @@ struct vdec_prop_type_handle {
int (*handle)(struct iris_inst *inst);
};
-static int vdec_codec_change(struct iris_inst *inst, u32 v4l2_codec)
-{
- bool session_init = false;
- int ret;
-
- if (!inst->codec)
- session_init = true;
-
- if (inst->codec && inst->fmt_src->fmt.pix_mp.pixelformat == v4l2_codec)
- return 0;
-
- inst->codec = v4l2_codec_to_driver(inst, v4l2_codec);
- if (!inst->codec)
- return -EINVAL;
-
- inst->fmt_src->fmt.pix_mp.pixelformat = v4l2_codec;
- ret = get_inst_capability(inst);
- if (ret)
- return ret;
-
- ret = ctrls_init(inst, session_init);
- if (ret)
- return ret;
-
- ret = update_buffer_count(inst, INPUT_MPLANE);
- if (ret)
- return ret;
-
- ret = update_buffer_count(inst, OUTPUT_MPLANE);
-
- return ret;
-}
-
int vdec_inst_init(struct iris_inst *inst)
{
struct v4l2_format *f;
@@ -93,7 +60,7 @@ int vdec_inst_init(struct iris_inst *inst)
inst->buffers.output.size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
inst->fw_min_count = 0;
- ret = vdec_codec_change(inst, inst->fmt_src->fmt.pix_mp.pixelformat);
+ ret = codec_change(inst, inst->fmt_src->fmt.pix_mp.pixelformat);
return ret;
}
@@ -233,7 +200,7 @@ int vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
if (f->type == INPUT_MPLANE) {
if (inst->fmt_src->fmt.pix_mp.pixelformat !=
f->fmt.pix_mp.pixelformat) {
- ret = vdec_codec_change(inst, f->fmt.pix_mp.pixelformat);
+ ret = codec_change(inst, f->fmt.pix_mp.pixelformat);
if (ret)
return ret;
}
@@ -1304,49 +1271,6 @@ int vdec_qbuf(struct iris_inst *inst, struct vb2_buffer *vb2)
return ret;
}
-static int process_resume(struct iris_inst *inst)
-{
- enum iris_inst_sub_state clear_sub_state = IRIS_INST_SUB_NONE;
- int ret;
-
- if (inst->sub_state & IRIS_INST_SUB_DRC &&
- inst->sub_state & IRIS_INST_SUB_DRC_LAST) {
- clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
-
- if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
- ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE);
- if (ret)
- return ret;
- clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
- }
- if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
- ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_SETTINGS_CHANGE);
- if (ret)
- return ret;
- clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
- }
- } else if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
- inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) {
- clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
- if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
- ret = iris_hfi_resume(inst, INPUT_MPLANE, HFI_CMD_DRAIN);
- if (ret)
- return ret;
- clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
- }
- if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
- ret = iris_hfi_resume(inst, OUTPUT_MPLANE, HFI_CMD_DRAIN);
- if (ret)
- return ret;
- clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
- }
- }
-
- ret = iris_inst_change_sub_state(inst, clear_sub_state, 0);
-
- return ret;
-}
-
int vdec_start_cmd(struct iris_inst *inst)
{
int ret;
new file mode 100644
@@ -0,0 +1,525 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "hfi_defines.h"
+#include "iris_buffer.h"
+#include "iris_common.h"
+#include "iris_ctrls.h"
+#include "iris_helpers.h"
+#include "iris_hfi.h"
+#include "iris_hfi_packet.h"
+#include "iris_power.h"
+#include "iris_venc.h"
+
+int venc_inst_init(struct iris_inst *inst)
+{
+ struct v4l2_format *f;
+ int ret;
+
+ inst->fmt_src = kzalloc(sizeof(*inst->fmt_src), GFP_KERNEL);
+ if (!inst->fmt_src)
+ return -ENOMEM;
+
+ inst->fmt_dst = kzalloc(sizeof(*inst->fmt_dst), GFP_KERNEL);
+ if (!inst->fmt_dst)
+ return -ENOMEM;
+
+ inst->vb2q_src = kzalloc(sizeof(*inst->vb2q_src), GFP_KERNEL);
+ if (!inst->vb2q_src)
+ return -ENOMEM;
+
+ inst->vb2q_dst = kzalloc(sizeof(*inst->vb2q_dst), GFP_KERNEL);
+ if (!inst->vb2q_dst)
+ return -ENOMEM;
+
+ f = inst->fmt_dst;
+ 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 = iris_get_buffer_size(inst, 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 = iris_get_buf_min_count(inst, BUF_OUTPUT);
+ inst->buffers.output.actual_count = inst->buffers.output.min_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->fmt_src;
+ f->type = INPUT_MPLANE;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_QC08C;
+ f->fmt.pix_mp.width = DEFAULT_WIDTH;
+ f->fmt.pix_mp.height = DEFAULT_HEIGHT;
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128);
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, 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 = iris_get_buf_min_count(inst, BUF_INPUT);
+ inst->buffers.input.actual_count = inst->buffers.input.min_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;
+
+ ret = codec_change(inst, inst->fmt_dst->fmt.pix_mp.pixelformat);
+
+ return ret;
+}
+
+void venc_inst_deinit(struct iris_inst *inst)
+{
+ kfree(inst->fmt_dst);
+ kfree(inst->fmt_src);
+}
+
+int venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
+{
+ struct iris_core *core;
+ u32 array[32] = {0};
+ u32 i = 0;
+
+ core = inst->core;
+
+ if (f->type == OUTPUT_MPLANE) {
+ u32 codecs = core->cap[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]);
+ 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->cap[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]);
+ if (!f->pixelformat)
+ return -EINVAL;
+ strscpy(f->description, "colorformat", sizeof(f->description));
+ }
+
+ memset(f->reserved, 0, sizeof(f->reserved));
+
+ return 0;
+}
+
+int venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
+{
+ 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);
+ if (!pix_fmt) {
+ f->fmt.pix_mp.pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat;
+ f->fmt.pix_mp.width = inst->fmt_src->fmt.pix_mp.width;
+ f->fmt.pix_mp.height = inst->fmt_src->fmt.pix_mp.height;
+ }
+ } else if (f->type == OUTPUT_MPLANE) {
+ pix_fmt = v4l2_codec_to_driver(inst, f->fmt.pix_mp.pixelformat);
+ if (!pix_fmt) {
+ f->fmt.pix_mp.width = inst->fmt_dst->fmt.pix_mp.width;
+ f->fmt.pix_mp.height = inst->fmt_dst->fmt.pix_mp.height;
+ f->fmt.pix_mp.pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ if (pixmp->field == V4L2_FIELD_ANY)
+ pixmp->field = V4L2_FIELD_NONE;
+ pixmp->num_planes = 1;
+
+ return 0;
+}
+
+static int venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_format *fmt;
+ enum codec_type codec;
+ u32 codec_align;
+ u32 width, height;
+ int ret = 0;
+
+ venc_try_fmt(inst, f);
+
+ fmt = inst->fmt_dst;
+ if (fmt->fmt.pix_mp.pixelformat != f->fmt.pix_mp.pixelformat) {
+ ret = codec_change(inst, f->fmt.pix_mp.pixelformat);
+ if (ret)
+ return ret;
+ }
+ fmt->type = OUTPUT_MPLANE;
+
+ codec = v4l2_codec_to_driver(inst, f->fmt.pix_mp.pixelformat);
+
+ codec_align = (codec == HEVC) ? 32 : 16;
+ width = inst->compose.width;
+ height = inst->compose.height;
+ if (inst->cap[ROTATION].value == 90 || inst->cap[ROTATION].value == 270) {
+ width = inst->compose.height;
+ height = inst->compose.width;
+ }
+ 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 =
+ iris_get_buffer_size(inst, BUF_OUTPUT);
+ 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 = iris_get_buf_min_count(inst, BUF_OUTPUT);
+ if (inst->buffers.output.actual_count <
+ inst->buffers.output.min_count) {
+ inst->buffers.output.actual_count =
+ inst->buffers.output.min_count;
+ }
+ inst->buffers.output.size =
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ memcpy(f, fmt, sizeof(struct v4l2_format));
+
+ return ret;
+}
+
+static int venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f)
+{
+ u32 pix_fmt, width, height, size, bytesperline;
+ struct v4l2_format *fmt, *output_fmt;
+ int ret = 0;
+
+ venc_try_fmt(inst, f);
+
+ pix_fmt = v4l2_colorformat_to_driver(inst, f->fmt.pix_mp.pixelformat);
+ inst->cap[PIX_FMTS].value = pix_fmt;
+
+ width = f->fmt.pix_mp.width;
+ height = f->fmt.pix_mp.height;
+
+ bytesperline = pix_fmt == FMT_TP10C ?
+ ALIGN(ALIGN(f->fmt.pix_mp.width, 192) * 4 / 3, 256) :
+ ALIGN(f->fmt.pix_mp.width, 128);
+
+ fmt = inst->fmt_src;
+ 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 = iris_get_buffer_size(inst, BUF_INPUT);
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage = size;
+ 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;
+
+ output_fmt = inst->fmt_dst;
+ 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 = iris_get_buf_min_count(inst, BUF_INPUT);
+ if (inst->buffers.input.actual_count <
+ inst->buffers.input.min_count) {
+ inst->buffers.input.actual_count =
+ inst->buffers.input.min_count;
+ }
+ inst->buffers.input.size = size;
+
+ if (f->fmt.pix_mp.width != inst->crop.width ||
+ f->fmt.pix_mp.height != inst->crop.height) {
+ inst->crop.top = 0;
+ inst->crop.left = 0;
+ inst->crop.width = f->fmt.pix_mp.width;
+ inst->crop.height = f->fmt.pix_mp.height;
+
+ inst->compose.top = 0;
+ inst->compose.left = 0;
+ inst->compose.width = f->fmt.pix_mp.width;
+ inst->compose.height = f->fmt.pix_mp.height;
+
+ ret = venc_s_fmt_output(inst, output_fmt);
+ if (ret)
+ return ret;
+ }
+
+ memcpy(f, fmt, sizeof(struct v4l2_format));
+
+ return ret;
+}
+
+int venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
+{
+ int ret;
+
+ if (f->type == INPUT_MPLANE)
+ ret = venc_s_fmt_input(inst, f);
+ else if (f->type == OUTPUT_MPLANE)
+ ret = venc_s_fmt_output(inst, f);
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+int venc_s_selection(struct iris_inst *inst, struct v4l2_selection *s)
+{
+ struct v4l2_format *output_fmt;
+ int ret;
+
+ if (s->type != INPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ if (s->r.left || s->r.top) {
+ s->r.left = 0;
+ s->r.top = 0;
+ }
+ if (s->r.width > inst->fmt_src->fmt.pix_mp.width)
+ s->r.width = inst->fmt_src->fmt.pix_mp.width;
+
+ if (s->r.height > inst->fmt_src->fmt.pix_mp.height)
+ s->r.height = inst->fmt_src->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;
+ inst->compose.left = inst->crop.left;
+ inst->compose.top = inst->crop.top;
+ inst->compose.width = inst->crop.width;
+ inst->compose.height = inst->crop.height;
+ output_fmt = inst->fmt_dst;
+ ret = venc_s_fmt_output(inst, output_fmt);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ if (s->r.left < inst->crop.left)
+ s->r.left = inst->crop.left;
+
+ if (s->r.top < inst->crop.top)
+ s->r.top = inst->crop.top;
+
+ if (s->r.width > inst->crop.width)
+ s->r.width = inst->crop.width;
+
+ if (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;
+
+ output_fmt = inst->fmt_dst;
+ ret = venc_s_fmt_output(inst, output_fmt);
+ if (ret)
+ return ret;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+int venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm)
+{
+ 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;
+ int ret = 0;
+
+ if (s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ timeperframe = &s_parm->parm.output.timeperframe;
+ max_rate = inst->cap[OPERATING_RATE].max >> 16;
+ default_rate = inst->cap[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->cap[FRAME_RATE].max >> 16;
+ default_rate = inst->cap[FRAME_RATE].value >> 16;
+ s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ }
+
+ if (!timeperframe->denominator || !timeperframe->numerator) {
+ if (!timeperframe->numerator)
+ timeperframe->numerator = 1;
+ if (!timeperframe->denominator)
+ timeperframe->denominator = default_rate;
+ }
+
+ us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
+ us_per_frame = div64_u64(us_per_frame, timeperframe->denominator);
+
+ if (!us_per_frame) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ input_rate = (u64)USEC_PER_SEC;
+ input_rate = div64_u64(input_rate, us_per_frame);
+
+ q16_rate = (u32)input_rate << 16;
+ if (is_frame_rate)
+ inst->cap[FRAME_RATE].value = q16_rate;
+ else
+ inst->cap[OPERATING_RATE].value = q16_rate;
+
+ if ((s_parm->type == INPUT_MPLANE && inst->vb2q_src->streaming) ||
+ (s_parm->type == OUTPUT_MPLANE && inst->vb2q_dst->streaming)) {
+ ret = check_core_mbps_mbpf(inst);
+ if (ret)
+ goto reset_rate;
+ ret = input_rate > max_rate;
+ if (ret) {
+ ret = -ENOMEM;
+ goto reset_rate;
+ }
+ }
+
+ if (is_frame_rate)
+ inst->cap[FRAME_RATE].flags |= CAP_FLAG_CLIENT_SET;
+ else
+ inst->cap[OPERATING_RATE].flags |= CAP_FLAG_CLIENT_SET;
+
+ if (inst->vb2q_dst->streaming) {
+ ret = iris_hfi_set_property(inst,
+ HFI_PROP_FRAME_RATE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_Q16,
+ &q16_rate,
+ sizeof(u32));
+ if (ret)
+ goto exit;
+ }
+
+ return ret;
+
+reset_rate:
+ if (ret) {
+ if (is_frame_rate)
+ inst->cap[FRAME_RATE].value = default_rate << 16;
+ else
+ inst->cap[OPERATING_RATE].value = default_rate << 16;
+ }
+exit:
+ return ret;
+}
+
+int venc_g_param(struct iris_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->cap[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->cap[FRAME_RATE].value >> 16;
+ s_parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ }
+
+ return 0;
+}
+
+int venc_subscribe_event(struct iris_inst *inst,
+ const struct v4l2_event_subscription *sub)
+{
+ int ret;
+
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ ret = v4l2_event_subscribe(&inst->fh, sub, MAX_EVENTS, NULL);
+ break;
+ case V4L2_EVENT_CTRL:
+ ret = v4l2_ctrl_subscribe_event(&inst->fh, sub);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+int venc_start_cmd(struct iris_inst *inst)
+{
+ vb2_clear_last_buffer_dequeued(inst->vb2q_dst);
+
+ return process_resume(inst);
+}
+
+int venc_stop_cmd(struct iris_inst *inst)
+{
+ int ret;
+
+ ret = iris_hfi_drain(inst, INPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN);
+
+ iris_scale_power(inst);
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _IRIS_VENC_H_
+#define _IRIS_VENC_H_
+
+#include "iris_instance.h"
+
+int venc_inst_init(struct iris_inst *inst);
+void venc_inst_deinit(struct iris_inst *inst);
+int venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f);
+int venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f);
+int venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f);
+int venc_s_selection(struct iris_inst *inst, struct v4l2_selection *s);
+int venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm);
+int venc_g_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm);
+int venc_subscribe_event(struct iris_inst *inst,
+ const struct v4l2_event_subscription *sub);
+int venc_start_cmd(struct iris_inst *inst);
+int venc_stop_cmd(struct iris_inst *inst);
+
+#endif
@@ -15,6 +15,7 @@
#include "iris_instance.h"
#include "iris_power.h"
#include "iris_vdec.h"
+#include "iris_venc.h"
#include "iris_vidc.h"
#include "iris_vb2.h"
@@ -30,7 +31,11 @@ static int vidc_v4l2_fh_init(struct iris_inst *inst)
if (inst->fh.vdev)
return -EINVAL;
- v4l2_fh_init(&inst->fh, core->vdev_dec);
+ if (inst->domain == ENCODER)
+ v4l2_fh_init(&inst->fh, core->vdev_enc);
+ else if (inst->domain == DECODER)
+ v4l2_fh_init(&inst->fh, core->vdev_dec);
+
inst->fh.ctrl_handler = &inst->ctrl_handler;
v4l2_fh_add(&inst->fh);
@@ -160,9 +165,20 @@ int vidc_open(struct file *filp)
{
struct iris_core *core = video_drvdata(filp);
struct iris_inst *inst = NULL;
+ struct video_device *vdev;
+ u32 session_type = 0;
int i = 0;
int ret;
+ vdev = video_devdata(filp);
+ if (strcmp(vdev->name, "qcom-iris-decoder") == 0)
+ session_type = DECODER;
+ else if (strcmp(vdev->name, "qcom-iris-encoder") == 0)
+ session_type = ENCODER;
+
+ if (session_type != DECODER && session_type != ENCODER)
+ return -EINVAL;
+
ret = iris_pm_get(core);
if (ret)
return ret;
@@ -182,6 +198,7 @@ int vidc_open(struct file *filp)
}
inst->core = core;
+ inst->domain = session_type;
inst->session_id = hash32_ptr(inst);
inst->ipsc_properties_set = false;
inst->opsc_properties_set = false;
@@ -213,7 +230,12 @@ int vidc_open(struct file *filp)
if (ret)
goto fail_remove_session;
- vdec_inst_init(inst);
+ if (inst->domain == DECODER)
+ ret = vdec_inst_init(inst);
+ else if (inst->domain == ENCODER)
+ ret = venc_inst_init(inst);
+ if (ret)
+ goto fail_fh_deinit;
ret = vidc_vb2_queue_init(inst);
if (ret)
@@ -238,7 +260,11 @@ int vidc_open(struct file *filp)
iris_core_deinit(core);
vidc_vb2_queue_deinit(inst);
fail_inst_deinit:
- vdec_inst_deinit(inst);
+ if (inst->domain == DECODER)
+ vdec_inst_deinit(inst);
+ else if (inst->domain == ENCODER)
+ venc_inst_deinit(inst);
+fail_fh_deinit:
vidc_v4l2_fh_deinit(inst);
fail_remove_session:
vidc_remove_session(inst);
@@ -264,7 +290,11 @@ int vidc_close(struct file *filp)
core = inst->core;
v4l2_ctrl_handler_free(&inst->ctrl_handler);
- vdec_inst_deinit(inst);
+ if (inst->domain == DECODER)
+ vdec_inst_deinit(inst);
+ else if (inst->domain == ENCODER)
+ venc_inst_deinit(inst);
+
mutex_lock(&inst->lock);
iris_pm_get(core);
close_session(inst);
@@ -342,7 +372,7 @@ static __poll_t vidc_poll(struct file *filp, struct poll_table_struct *pt)
static int vidc_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f)
{
struct iris_inst *inst;
- int ret;
+ int ret = 0;
inst = get_vidc_inst(filp, fh);
if (!inst)
@@ -354,7 +384,10 @@ static int vidc_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f)
goto unlock;
}
- ret = vdec_enum_fmt(inst, f);
+ if (inst->domain == DECODER)
+ ret = vdec_enum_fmt(inst, f);
+ else if (inst->domain == ENCODER)
+ ret = venc_enum_fmt(inst, f);
unlock:
mutex_unlock(&inst->lock);
@@ -365,7 +398,7 @@ static int vidc_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f)
static int vidc_try_fmt(struct file *filp, void *fh, struct v4l2_format *f)
{
struct iris_inst *inst;
- int ret;
+ int ret = 0;
inst = get_vidc_inst(filp, fh);
if (!inst)
@@ -382,7 +415,10 @@ static int vidc_try_fmt(struct file *filp, void *fh, struct v4l2_format *f)
goto unlock;
}
- ret = vdec_try_fmt(inst, f);
+ if (inst->domain == DECODER)
+ ret = vdec_try_fmt(inst, f);
+ else if (inst->domain == ENCODER)
+ ret = venc_try_fmt(inst, f);
unlock:
mutex_unlock(&inst->lock);
@@ -393,7 +429,7 @@ static int vidc_try_fmt(struct file *filp, void *fh, struct v4l2_format *f)
static int vidc_s_fmt(struct file *filp, void *fh, struct v4l2_format *f)
{
struct iris_inst *inst;
- int ret;
+ int ret = 0;
inst = get_vidc_inst(filp, fh);
if (!inst)
@@ -410,7 +446,10 @@ static int vidc_s_fmt(struct file *filp, void *fh, struct v4l2_format *f)
goto unlock;
}
- ret = vdec_s_fmt(inst, f);
+ if (inst->domain == DECODER)
+ ret = vdec_s_fmt(inst, f);
+ else if (inst->domain == ENCODER)
+ ret = venc_s_fmt(inst, f);
unlock:
mutex_unlock(&inst->lock);
@@ -488,6 +527,70 @@ static int vidc_enum_framesizes(struct file *filp, void *fh,
return ret;
}
+static int vidc_enum_frameintervals(struct file *filp, void *fh,
+ struct v4l2_frmivalenum *fival)
+
+{
+ enum colorformat_type colorfmt;
+ struct iris_inst *inst;
+ struct iris_core *core;
+ u32 fps, mbpf;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !fival)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (inst->domain == DECODER) {
+ ret = -ENOTTY;
+ goto unlock;
+ }
+
+ core = inst->core;
+
+ if (fival->index) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ colorfmt = v4l2_colorformat_to_driver(inst, fival->pixel_format);
+ if (colorfmt == FMT_NONE) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (fival->width > inst->cap[FRAME_WIDTH].max ||
+ fival->width < inst->cap[FRAME_WIDTH].min ||
+ fival->height > inst->cap[FRAME_HEIGHT].max ||
+ fival->height < inst->cap[FRAME_HEIGHT].min) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ mbpf = NUM_MBS_PER_FRAME(fival->height, fival->width);
+ fps = core->cap[MAX_MBPS].value / mbpf;
+
+ fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+ fival->stepwise.min.numerator = 1;
+ fival->stepwise.min.denominator =
+ min_t(u32, fps, inst->cap[FRAME_RATE].max);
+ fival->stepwise.max.numerator = 1;
+ fival->stepwise.max.denominator = 1;
+ fival->stepwise.step.numerator = 1;
+ fival->stepwise.step.denominator = inst->cap[FRAME_RATE].max;
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
static int vidc_reqbufs(struct file *filp, void *fh, struct v4l2_requestbuffers *b)
{
struct vb2_queue *vb2q = NULL;
@@ -751,7 +854,11 @@ static int vidc_querycap(struct file *filp, void *fh, struct v4l2_capability *ca
strscpy(cap->driver, VIDC_DRV_NAME, sizeof(cap->driver));
strscpy(cap->bus_info, VIDC_BUS_NAME, sizeof(cap->bus_info));
memset(cap->reserved, 0, sizeof(cap->reserved));
- strscpy(cap->card, "iris_decoder", sizeof(cap->card));
+
+ if (inst->domain == DECODER)
+ strscpy(cap->card, "iris_decoder", sizeof(cap->card));
+ else if (inst->domain == ENCODER)
+ strscpy(cap->card, "iris_encoder", sizeof(cap->card));
unlock:
mutex_unlock(&inst->lock);
@@ -839,7 +946,7 @@ static int vidc_querymenu(struct file *filp, void *fh, struct v4l2_querymenu *qm
static int vidc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
{
struct iris_inst *inst;
- int ret;
+ int ret = 0;
inst = container_of(fh, struct iris_inst, fh);
@@ -849,7 +956,10 @@ static int vidc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subs
goto unlock;
}
- ret = vdec_subscribe_event(inst, sub);
+ if (inst->domain == DECODER)
+ ret = vdec_subscribe_event(inst, sub);
+ else if (inst->domain == ENCODER)
+ ret = venc_subscribe_event(inst, sub);
unlock:
mutex_unlock(&inst->lock);
@@ -893,28 +1003,152 @@ static int vidc_g_selection(struct file *filp, void *fh, struct v4l2_selection *
goto unlock;
}
- if (s->type != OUTPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (s->type != OUTPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ inst->domain == DECODER) {
ret = -EINVAL;
goto unlock;
}
- switch (s->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP:
- 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->crop.left;
- s->r.top = inst->crop.top;
- s->r.width = inst->crop.width;
- s->r.height = inst->crop.height;
- break;
- default:
+ if (s->type != INPUT_MPLANE && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ inst->domain == ENCODER) {
ret = -EINVAL;
+ goto unlock;
}
+ if (inst->domain == DECODER) {
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ 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->crop.left;
+ s->r.top = inst->crop.top;
+ s->r.width = inst->crop.width;
+ s->r.height = inst->crop.height;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ } else if (inst->domain == ENCODER) {
+ 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:
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int vidc_s_selection(struct file *filp, void *fh, struct v4l2_selection *s)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !s)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ if (inst->domain == DECODER) {
+ ret = -EINVAL;
+ goto unlock;
+ } else if (inst->domain == ENCODER) {
+ ret = venc_s_selection(inst, s);
+ }
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int vidc_s_parm(struct file *filp, void *fh, struct v4l2_streamparm *a)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !a)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (inst->domain == ENCODER)
+ ret = venc_s_param(inst, a);
+ else
+ ret = -EINVAL;
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int vidc_g_parm(struct file *filp, void *fh, struct v4l2_streamparm *a)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !a)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (inst->domain == ENCODER)
+ ret = venc_g_param(inst, a);
+ else
+ ret = -EINVAL;
+
unlock:
mutex_unlock(&inst->lock);
@@ -955,6 +1189,39 @@ static int vidc_try_dec_cmd(struct file *filp, void *fh,
return ret;
}
+static int vidc_try_enc_cmd(struct file *filp, void *fh,
+ struct v4l2_encoder_cmd *enc)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !enc)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (inst->domain != ENCODER) {
+ ret = -ENOTTY;
+ goto unlock;
+ }
+
+ if (enc->cmd != V4L2_ENC_CMD_STOP && enc->cmd != V4L2_ENC_CMD_START) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ enc->flags = 0;
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
static int vidc_dec_cmd(struct file *filp, void *fh,
struct v4l2_decoder_cmd *dec)
{
@@ -1002,6 +1269,60 @@ static int vidc_dec_cmd(struct file *filp, void *fh,
return ret;
}
+static int vidc_enc_cmd(struct file *filp, void *fh,
+ struct v4l2_encoder_cmd *enc)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = get_vidc_inst(filp, fh);
+ if (!inst || !enc)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ if (IS_SESSION_ERROR(inst)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (inst->domain != ENCODER) {
+ ret = -ENOTTY;
+ goto unlock;
+ }
+
+ if (enc->cmd != V4L2_ENC_CMD_START &&
+ enc->cmd != V4L2_ENC_CMD_STOP) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (enc->cmd == V4L2_ENC_CMD_STOP && inst->state == IRIS_INST_OPEN) {
+ ret = 0;
+ goto unlock;
+ }
+
+ if (!allow_cmd(inst, enc->cmd)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = iris_pm_get(inst->core);
+ if (ret)
+ goto unlock;
+
+ if (enc->cmd == V4L2_ENC_CMD_START)
+ ret = venc_start_cmd(inst);
+ else if (enc->cmd == V4L2_ENC_CMD_STOP)
+ ret = venc_stop_cmd(inst);
+
+ iris_pm_put(inst->core, true);
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
static struct v4l2_file_operations v4l2_file_ops = {
.owner = THIS_MODULE,
.open = vidc_open,
@@ -1027,7 +1348,7 @@ static struct vb2_mem_ops iris_vb2_mem_ops = {
.unmap_dmabuf = iris_vb2_unmap_dmabuf,
};
-static const struct v4l2_ioctl_ops v4l2_ioctl_ops = {
+static const struct v4l2_ioctl_ops v4l2_ioctl_ops_dec = {
.vidioc_enum_fmt_vid_cap = vidc_enum_fmt,
.vidioc_enum_fmt_vid_out = vidc_enum_fmt,
.vidioc_try_fmt_vid_cap_mplane = vidc_try_fmt,
@@ -1055,12 +1376,44 @@ static const struct v4l2_ioctl_ops v4l2_ioctl_ops = {
.vidioc_decoder_cmd = vidc_dec_cmd,
};
+static const struct v4l2_ioctl_ops v4l2_ioctl_ops_enc = {
+ .vidioc_enum_fmt_vid_cap = vidc_enum_fmt,
+ .vidioc_enum_fmt_vid_out = vidc_enum_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vidc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vidc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vidc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidc_s_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = vidc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidc_g_fmt,
+ .vidioc_enum_framesizes = vidc_enum_framesizes,
+ .vidioc_enum_frameintervals = vidc_enum_frameintervals,
+ .vidioc_reqbufs = vidc_reqbufs,
+ .vidioc_querybuf = vidc_querybuf,
+ .vidioc_create_bufs = vidc_create_bufs,
+ .vidioc_prepare_buf = vidc_prepare_buf,
+ .vidioc_qbuf = vidc_qbuf,
+ .vidioc_dqbuf = vidc_dqbuf,
+ .vidioc_streamon = vidc_streamon,
+ .vidioc_streamoff = vidc_streamoff,
+ .vidioc_querycap = vidc_querycap,
+ .vidioc_queryctrl = vidc_queryctrl,
+ .vidioc_querymenu = vidc_querymenu,
+ .vidioc_subscribe_event = vidc_subscribe_event,
+ .vidioc_unsubscribe_event = vidc_unsubscribe_event,
+ .vidioc_g_selection = vidc_g_selection,
+ .vidioc_s_selection = vidc_s_selection,
+ .vidioc_s_parm = vidc_s_parm,
+ .vidioc_g_parm = vidc_g_parm,
+ .vidioc_try_encoder_cmd = vidc_try_enc_cmd,
+ .vidioc_encoder_cmd = vidc_enc_cmd,
+};
+
int init_ops(struct iris_core *core)
{
core->v4l2_file_ops = &v4l2_file_ops;
core->vb2_ops = &iris_vb2_ops;
core->vb2_mem_ops = &iris_vb2_mem_ops;
- core->v4l2_ioctl_ops = &v4l2_ioctl_ops;
-
+ core->v4l2_ioctl_ops_dec = &v4l2_ioctl_ops_dec;
+ core->v4l2_ioctl_ops_enc = &v4l2_ioctl_ops_enc;
return 0;
}
@@ -115,6 +115,7 @@ struct iris_inst_power {
enum plat_core_cap_type {
CORE_CAP_NONE = 0,
DEC_CODECS,
+ ENC_CODECS,
MAX_SESSION_COUNT,
MAX_MBPF,
MAX_MBPS,
@@ -249,6 +250,7 @@ struct plat_inst_cap {
struct plat_inst_caps {
enum codec_type codec;
+ enum domain_type domain;
struct plat_inst_cap cap[INST_CAP_MAX + 1];
};
@@ -63,6 +63,7 @@ static struct color_format_info color_format_data_sm8550[] = {
static struct plat_core_cap core_data_sm8550[] = {
{DEC_CODECS, H264 | HEVC | VP9},
+ {ENC_CODECS, H264 | HEVC},
{MAX_SESSION_COUNT, 16},
{MAX_MBPF, 278528}, /* ((8192x4352)/256) * 2 */
{MAX_MBPS, 7833600}, /* max_load 7680x4320@60fps */
Implement all iris v4l2 ioctls ops supported by encoder. Add state checks to ensure ioctl are allowed in valid instance state only. Codec format can be changed by client during s_fmt. Update the v4l2 control values according to the updated codec format. 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 | 8 +- .../media/platform/qcom/vcodec/iris/iris_ctrls.c | 59 ++- .../media/platform/qcom/vcodec/iris/iris_helpers.c | 89 +++- .../media/platform/qcom/vcodec/iris/iris_helpers.h | 3 + .../platform/qcom/vcodec/iris/iris_instance.h | 5 + .../media/platform/qcom/vcodec/iris/iris_probe.c | 3 +- .../media/platform/qcom/vcodec/iris/iris_state.c | 5 +- .../media/platform/qcom/vcodec/iris/iris_vdec.c | 80 +--- .../media/platform/qcom/vcodec/iris/iris_venc.c | 525 +++++++++++++++++++++ .../media/platform/qcom/vcodec/iris/iris_venc.h | 24 + .../media/platform/qcom/vcodec/iris/iris_vidc.c | 415 ++++++++++++++-- .../platform/qcom/vcodec/iris/platform_common.h | 2 + .../platform/qcom/vcodec/iris/platform_sm8550.c | 1 + 14 files changed, 1088 insertions(+), 132 deletions(-) create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_venc.c create mode 100644 drivers/media/platform/qcom/vcodec/iris/iris_venc.h