Hi, Nicolas. >-----Original Message----- >From: Nicolas Dufresne <nicolas@xxxxxxxxxxxx> >Sent: Tuesday, February 18, 2025 3:33 AM >To: Nas Chung <nas.chung@xxxxxxxxxxxxxxx>; mchehab@xxxxxxxxxx; >hverkuil@xxxxxxxxx; sebastian.fricke@xxxxxxxxxxxxx; robh@xxxxxxxxxx; >krzk+dt@xxxxxxxxxx; conor+dt@xxxxxxxxxx >Cc: linux-media@xxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; linux- >kernel@xxxxxxxxxxxxxxx; linux-imx@xxxxxxx; linux-arm- >kernel@xxxxxxxxxxxxxxxxxxx; jackson.lee <jackson.lee@xxxxxxxxxxxxxxx>; >lafley.kim <lafley.kim@xxxxxxxxxxxxxxx> >Subject: Re: [PATCH 5/8] media: chips-media: wave6: Add v4l2 m2m driver > >Hi Nas. > >Le lundi 10 février 2025 à 18:07 +0900, Nas Chung a écrit : >> Add v4l2 m2m drivers which support stateful decoder and encoder. > >Before sending updates, note that this is quite short of a commit >message for a newly introduce driver. Your readers would certainly like >to know what feature have been included, what is not, etc. My >understanding from the discussion is that the Wave6 design can be >configured with a lot more features then what this driver covers. > >I know you have placed some of that in the cover letter, but no one >will find it when later doing git blame. Thanks for you feedback. Got it! I will ensure that each commit message includes enough details. Thanks. Nas. > >regards, >Nicolas > >> >> Signed-off-by: Nas Chung <nas.chung@xxxxxxxxxxxxxxx> >> --- >> .../chips-media/wave6/wave6-vpu-dec.c | 1883 ++++++++++++ >> .../chips-media/wave6/wave6-vpu-enc.c | 2698 >> +++++++++++++++++ >> .../chips-media/wave6/wave6-vpu-v4l2.c | 381 +++ >> 3 files changed, 4962 insertions(+) >> create mode 100644 drivers/media/platform/chips-media/wave6/wave6- >> vpu-dec.c >> create mode 100644 drivers/media/platform/chips-media/wave6/wave6- >> vpu-enc.c >> create mode 100644 drivers/media/platform/chips-media/wave6/wave6- >> vpu-v4l2.c >> >> diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c >> b/drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c >> new file mode 100644 >> index 000000000000..f6ed078a2824 >> --- /dev/null >> +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-dec.c >> @@ -0,0 +1,1883 @@ >> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) >> +/* >> + * Wave6 series multi-standard codec IP - v4l2 stateful decoder >> interface >> + * >> + * Copyright (C) 2025 CHIPS&MEDIA INC >> + */ >> + >> +#include <linux/pm_runtime.h> >> +#include <linux/delay.h> >> +#include "wave6-vpu.h" >> +#include "wave6-vpu-dbg.h" >> +#include "wave6-trace.h" >> + >> +#define VPU_DEC_DEV_NAME "C&M Wave6 VPU decoder" >> +#define VPU_DEC_DRV_NAME "wave6-dec" >> +#define V4L2_CID_VPU_THUMBNAIL_MODE (V4L2_CID_USER_BASE + 0x1001) >> + >> +static const struct vpu_format wave6_vpu_dec_fmt_list[2][6] = { >> + [VPU_FMT_TYPE_CODEC] = { >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + }, >> + [VPU_FMT_TYPE_RAW] = { >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 3, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 2, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, >> + .max_width = W6_MAX_DEC_PIC_WIDTH, >> + .min_width = W6_MIN_DEC_PIC_WIDTH, >> + .max_height = W6_MAX_DEC_PIC_HEIGHT, >> + .min_height = W6_MIN_DEC_PIC_HEIGHT, >> + .num_planes = 2, >> + }, >> + } >> +}; >> + >> +static int wave6_vpu_dec_seek_header(struct vpu_instance *inst); >> + >> +static const struct vpu_format *wave6_find_vpu_fmt(unsigned int >> v4l2_pix_fmt, >> + enum vpu_fmt_type >> type) >> +{ >> + unsigned int index; >> + >> + for (index = 0; index < >> ARRAY_SIZE(wave6_vpu_dec_fmt_list[type]); index++) { >> + if (wave6_vpu_dec_fmt_list[type][index].v4l2_pix_fmt >> == v4l2_pix_fmt) >> + return &wave6_vpu_dec_fmt_list[type][index]; >> + } >> + >> + return NULL; >> +} >> + >> +static const struct vpu_format *wave6_find_vpu_fmt_by_idx(unsigned >> int idx, >> + enum >> vpu_fmt_type type) >> +{ >> + if (idx >= ARRAY_SIZE(wave6_vpu_dec_fmt_list[type])) >> + return NULL; >> + >> + if (!wave6_vpu_dec_fmt_list[type][idx].v4l2_pix_fmt) >> + return NULL; >> + >> + return &wave6_vpu_dec_fmt_list[type][idx]; >> +} >> + >> +static void wave6_vpu_dec_release_fb(struct vpu_instance *inst) >> +{ >> + int i; >> + >> + for (i = 0; i < WAVE6_MAX_FBS; i++) { >> + wave6_free_dma(&inst->frame_vbuf[i]); >> + memset(&inst->frame_buf[i], 0, sizeof(struct >> frame_buffer)); >> + wave6_free_dma(&inst- >> >aux_vbuf[AUX_BUF_FBC_Y_TBL][i]); >> + wave6_free_dma(&inst- >> >aux_vbuf[AUX_BUF_FBC_C_TBL][i]); >> + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_MV_COL][i]); >> + } >> +} >> + >> +static void wave6_vpu_dec_destroy_instance(struct vpu_instance >> *inst) >> +{ >> + u32 fail_res; >> + int ret; >> + >> + dprintk(inst->dev->dev, "[%d] destroy instance\n", inst- >> >id); >> + wave6_vpu_remove_dbgfs_file(inst); >> + >> + ret = wave6_vpu_dec_close(inst, &fail_res); >> + if (ret) { >> + dev_err(inst->dev->dev, "failed destroy instance: %d >> (%d)\n", >> + ret, fail_res); >> + } >> + >> + wave6_vpu_dec_release_fb(inst); >> + >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_NONE); >> + >> + if (!pm_runtime_suspended(inst->dev->dev)) >> + pm_runtime_put_sync(inst->dev->dev); >> +} >> + >> +static void wave6_handle_bitstream_buffer(struct vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *src_buf; >> + u32 src_size = 0; >> + int ret; >> + >> + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); >> + if (src_buf) { >> + struct vpu_buffer *vpu_buf = >> wave6_to_vpu_buf(src_buf); >> + dma_addr_t rd_ptr = wave6_get_dma_addr(src_buf, 0); >> + >> + if (vpu_buf->consumed) { >> + dev_dbg(inst->dev->dev, "%s: Already >> consumed buffer\n", >> + __func__); >> + return; >> + } >> + >> + vpu_buf->ts_start = ktime_get_raw(); >> + vpu_buf->consumed = true; >> + wave6_vpu_dec_set_rd_ptr(inst, rd_ptr, true); >> + >> + src_size = vb2_get_plane_payload(&src_buf->vb2_buf, >> 0); >> + } >> + >> + if (!src_size) { >> + dma_addr_t rd = 0, wr = 0; >> + >> + wave6_vpu_dec_get_bitstream_buffer(inst, &rd, &wr); >> + wave6_vpu_dec_set_rd_ptr(inst, wr, true); >> + } >> + >> + trace_dec_pic(inst, src_buf ? src_buf->vb2_buf.index : -1, >> src_size); >> + >> + ret = wave6_vpu_dec_update_bitstream_buffer(inst, src_size); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "%s: Update bitstream buffer >> fail %d\n", >> + __func__, ret); >> + return; >> + } >> +} >> + >> +static void wave6_update_pix_fmt_cap(struct v4l2_pix_format_mplane >> *pix_mp, >> + unsigned int width, >> + unsigned int height, >> + bool new_resolution) >> +{ >> + unsigned int aligned_width; >> + >> + if (new_resolution) >> + pix_mp->plane_fmt[0].bytesperline = 0; >> + >> + aligned_width = round_up(width, 32); >> + wave6_update_pix_fmt(pix_mp, aligned_width, height); >> +} >> + >> +static int wave6_allocate_aux_buffer(struct vpu_instance *inst, >> + enum aux_buffer_type type, >> + int num) >> +{ >> + struct aux_buffer buf[WAVE6_MAX_FBS]; >> + struct aux_buffer_info buf_info; >> + struct dec_aux_buffer_size_info size_info; >> + unsigned int size; >> + int i, ret; >> + >> + memset(buf, 0, sizeof(buf)); >> + >> + size_info.width = inst->src_fmt.width; >> + size_info.height = inst->src_fmt.height; >> + size_info.type = type; >> + >> + ret = wave6_vpu_dec_get_aux_buffer_size(inst, size_info, >> &size); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "%s: Get size fail (type >> %d)\n", __func__, type); >> + return ret; >> + } >> + >> + num = min_t(u32, num, WAVE6_MAX_FBS); >> + for (i = 0; i < num; i++) { >> + inst->aux_vbuf[type][i].size = size; >> + ret = wave6_alloc_dma(inst->dev->dev, &inst- >> >aux_vbuf[type][i]); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "%s: Alloc fail >> (type %d)\n", __func__, type); >> + return ret; >> + } >> + >> + buf[i].index = i; >> + buf[i].addr = inst->aux_vbuf[type][i].daddr; >> + buf[i].size = inst->aux_vbuf[type][i].size; >> + } >> + >> + buf_info.type = type; >> + buf_info.num = num; >> + buf_info.buf_array = buf; >> + >> + ret = wave6_vpu_dec_register_aux_buffer(inst, buf_info); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "%s: Register fail (type >> %d)\n", __func__, type); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static void wave6_vpu_dec_handle_dst_buffer(struct vpu_instance >> *inst) >> +{ >> + struct vb2_v4l2_buffer *dst_buf; >> + struct v4l2_m2m_buffer *v4l2_m2m_buf; >> + struct vpu_buffer *vpu_buf; >> + dma_addr_t buf_addr_y, buf_addr_cb, buf_addr_cr; >> + u32 buf_size; >> + u32 fb_stride = inst->dst_fmt.plane_fmt[0].bytesperline; >> + u32 luma_size = fb_stride * inst->dst_fmt.height; >> + u32 chroma_size = (fb_stride / 2) * (inst->dst_fmt.height / >> 2); >> + struct frame_buffer disp_buffer = {0}; >> + struct dec_initial_info initial_info = {0}; >> + int consumed_num = wave6_vpu_get_consumed_fb_num(inst); >> + int ret; >> + >> + wave6_vpu_dec_give_command(inst, DEC_GET_SEQ_INFO, >> &initial_info); >> + >> + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, >> v4l2_m2m_buf) { >> + dst_buf = &v4l2_m2m_buf->vb; >> + vpu_buf = wave6_to_vpu_buf(dst_buf); >> + >> + if (vpu_buf->consumed) >> + continue; >> + >> + if (consumed_num >= WAVE6_MAX_FBS) >> + break; >> + >> + if (inst->dst_fmt.num_planes == 1) { >> + buf_size = vb2_plane_size(&dst_buf->vb2_buf, >> 0); >> + buf_addr_y = wave6_get_dma_addr(dst_buf, 0); >> + buf_addr_cb = buf_addr_y + luma_size; >> + buf_addr_cr = buf_addr_cb + chroma_size; >> + } else if (inst->dst_fmt.num_planes == 2) { >> + buf_size = vb2_plane_size(&dst_buf->vb2_buf, >> 0) + >> + vb2_plane_size(&dst_buf->vb2_buf, >> 1); >> + buf_addr_y = wave6_get_dma_addr(dst_buf, 0); >> + buf_addr_cb = wave6_get_dma_addr(dst_buf, >> 1); >> + buf_addr_cr = buf_addr_cb + chroma_size; >> + } else if (inst->dst_fmt.num_planes == 3) { >> + buf_size = vb2_plane_size(&dst_buf->vb2_buf, >> 0) + >> + vb2_plane_size(&dst_buf->vb2_buf, >> 1) + >> + vb2_plane_size(&dst_buf->vb2_buf, >> 2); >> + buf_addr_y = wave6_get_dma_addr(dst_buf, 0); >> + buf_addr_cb = wave6_get_dma_addr(dst_buf, >> 1); >> + buf_addr_cr = wave6_get_dma_addr(dst_buf, >> 2); >> + } >> + disp_buffer.buf_y = buf_addr_y; >> + disp_buffer.buf_cb = buf_addr_cb; >> + disp_buffer.buf_cr = buf_addr_cr; >> + disp_buffer.width = inst->src_fmt.width; >> + disp_buffer.height = inst->src_fmt.height; >> + disp_buffer.stride = fb_stride; >> + disp_buffer.map_type = LINEAR_FRAME_MAP; >> + disp_buffer.luma_bitdepth = >> initial_info.luma_bitdepth; >> + disp_buffer.chroma_bitdepth = >> initial_info.chroma_bitdepth; >> + disp_buffer.chroma_format_idc = >> initial_info.chroma_format_idc; >> + >> + ret = wave6_vpu_dec_register_display_buffer_ex(inst, >> disp_buffer); >> + if (ret) { >> + dev_err(inst->dev->dev, "fail register >> display buffer %d", ret); >> + break; >> + } >> + >> + vpu_buf->consumed = true; >> + consumed_num++; >> + } >> +} >> + >> +static enum v4l2_quantization to_v4l2_quantization(u32 >> video_full_range_flag) >> +{ >> + switch (video_full_range_flag) { >> + case 0: >> + return V4L2_QUANTIZATION_LIM_RANGE; >> + case 1: >> + return V4L2_QUANTIZATION_FULL_RANGE; >> + default: >> + return V4L2_QUANTIZATION_DEFAULT; >> + } >> +} >> + >> +static enum v4l2_colorspace to_v4l2_colorspace(u32 colour_primaries) >> +{ >> + switch (colour_primaries) { >> + case 1: >> + return V4L2_COLORSPACE_REC709; >> + case 4: >> + return V4L2_COLORSPACE_470_SYSTEM_M; >> + case 5: >> + return V4L2_COLORSPACE_470_SYSTEM_BG; >> + case 6: >> + return V4L2_COLORSPACE_SMPTE170M; >> + case 7: >> + return V4L2_COLORSPACE_SMPTE240M; >> + case 9: >> + return V4L2_COLORSPACE_BT2020; >> + case 11: >> + return V4L2_COLORSPACE_DCI_P3; >> + default: >> + return V4L2_COLORSPACE_DEFAULT; >> + } >> +} >> + >> +static enum v4l2_xfer_func to_v4l2_xfer_func(u32 >> transfer_characteristics) >> +{ >> + switch (transfer_characteristics) { >> + case 1: >> + return V4L2_XFER_FUNC_709; >> + case 6: >> + return V4L2_XFER_FUNC_709; >> + case 7: >> + return V4L2_XFER_FUNC_SMPTE240M; >> + case 8: >> + return V4L2_XFER_FUNC_NONE; >> + case 13: >> + return V4L2_XFER_FUNC_SRGB; >> + case 14: >> + return V4L2_XFER_FUNC_709; >> + case 16: >> + return V4L2_XFER_FUNC_SMPTE2084; >> + default: >> + return V4L2_XFER_FUNC_DEFAULT; >> + } >> +} >> + >> +static enum v4l2_ycbcr_encoding to_v4l2_ycbcr_encoding(u32 >> matrix_coeffs) >> +{ >> + switch (matrix_coeffs) { >> + case 1: >> + return V4L2_YCBCR_ENC_709; >> + case 5: >> + return V4L2_YCBCR_ENC_601; >> + case 6: >> + return V4L2_YCBCR_ENC_601; >> + case 7: >> + return V4L2_YCBCR_ENC_SMPTE240M; >> + case 9: >> + return V4L2_YCBCR_ENC_BT2020; >> + case 10: >> + return V4L2_YCBCR_ENC_BT2020_CONST_LUM; >> + default: >> + return V4L2_YCBCR_ENC_DEFAULT; >> + } >> +} >> + >> +static void wave6_update_color_info(struct vpu_instance *inst, >> + struct dec_initial_info >> *initial_info) >> +{ >> + struct color_param *color = &initial_info->color; >> + >> + if (!color->video_signal_type_present) >> + goto set_default_all; >> + >> + inst->quantization = to_v4l2_quantization(color- >> >color_range); >> + >> + if (!color->color_description_present) >> + goto set_default_color; >> + >> + inst->colorspace = to_v4l2_colorspace(color- >> >color_primaries); >> + inst->xfer_func = to_v4l2_xfer_func(color- >> >transfer_characteristics); >> + inst->ycbcr_enc = to_v4l2_ycbcr_encoding(color- >> >matrix_coefficients); >> + >> + return; >> + >> +set_default_all: >> + inst->quantization = V4L2_QUANTIZATION_DEFAULT; >> +set_default_color: >> + inst->colorspace = V4L2_COLORSPACE_DEFAULT; >> + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; >> + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; >> +} >> + >> +static enum v4l2_mpeg_video_hevc_profile to_v4l2_hevc_profile(u32 >> profile) >> +{ >> + switch (profile) { >> + case HEVC_PROFILE_MAIN: >> + return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN; >> + default: >> + return V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN; >> + } >> +} >> + >> +static enum v4l2_mpeg_video_h264_profile to_v4l2_h264_profile(u32 >> profile) >> +{ >> + switch (profile) { >> + case H264_PROFILE_BP: >> + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; >> + case H264_PROFILE_MP: >> + return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; >> + case H264_PROFILE_EXTENDED: >> + return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED; >> + case H264_PROFILE_HP: >> + return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; >> + default: >> + return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; >> + } >> +} >> + >> +static void wave6_update_v4l2_ctrls(struct vpu_instance *inst, >> + struct dec_initial_info *info) >> +{ >> + struct v4l2_ctrl *ctrl; >> + u32 min_disp_cnt; >> + >> + min_disp_cnt = info->frame_buf_delay + 1; >> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, >> + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); >> + if (ctrl) >> + v4l2_ctrl_s_ctrl(ctrl, min_disp_cnt); >> + >> + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_HEVC) { >> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, >> + >> V4L2_CID_MPEG_VIDEO_HEVC_PROFILE); >> + if (ctrl) >> + v4l2_ctrl_s_ctrl(ctrl, >> to_v4l2_hevc_profile(info->profile)); >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_H264) { >> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, >> + >> V4L2_CID_MPEG_VIDEO_H264_PROFILE); >> + if (ctrl) >> + v4l2_ctrl_s_ctrl(ctrl, >> to_v4l2_h264_profile(info->profile)); >> + } >> +} >> + >> +static int wave6_vpu_dec_start_decode(struct vpu_instance *inst) >> +{ >> + struct dec_param pic_param; >> + int ret; >> + u32 fail_res = 0; >> + >> + memset(&pic_param, 0, sizeof(struct dec_param)); >> + >> + wave6_handle_bitstream_buffer(inst); >> + if (inst->state == VPU_INST_STATE_OPEN) { >> + ret = wave6_vpu_dec_seek_header(inst); >> + if (ret) { >> + vb2_queue_error(v4l2_m2m_get_src_vq(inst- >> >v4l2_fh.m2m_ctx)); >> + vb2_queue_error(v4l2_m2m_get_dst_vq(inst- >> >v4l2_fh.m2m_ctx)); >> + } >> + return -EAGAIN; >> + } >> + >> + wave6_vpu_dec_handle_dst_buffer(inst); >> + >> + ret = wave6_vpu_dec_start_one_frame(inst, &pic_param, >> &fail_res); >> + if (ret) { >> + struct vb2_v4l2_buffer *src_buf = NULL; >> + >> + dev_err(inst->dev->dev, "[%d] %s: fail %d\n", inst- >> >id, __func__, ret); >> + wave6_vpu_set_instance_state(inst, >> VPU_INST_STATE_STOP); >> + >> + src_buf = v4l2_m2m_src_buf_remove(inst- >> >v4l2_fh.m2m_ctx); >> + if (src_buf) { >> + v4l2_m2m_buf_done(src_buf, >> VB2_BUF_STATE_ERROR); >> + inst->sequence++; >> + inst->processed_buf_num++; >> + inst->error_buf_num++; >> + } >> + } >> + >> + return ret; >> +} >> + >> +static void wave6_handle_decoded_frame(struct vpu_instance *inst, >> + struct dec_output_info *info) >> +{ >> + struct vb2_v4l2_buffer *src_buf; >> + struct vb2_v4l2_buffer *dst_buf; >> + struct vpu_buffer *vpu_buf; >> + enum vb2_buffer_state state; >> + >> + state = info->decoding_success ? VB2_BUF_STATE_DONE : >> VB2_BUF_STATE_ERROR; >> + >> + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); >> + if (!src_buf) { >> + dev_err(inst->dev->dev, "[%d] decoder can't find src >> buffer\n", inst->id); >> + return; >> + } >> + >> + vpu_buf = wave6_to_vpu_buf(src_buf); >> + if (!vpu_buf || !vpu_buf->consumed) { >> + dev_err(inst->dev->dev, "[%d] src buffer is not >> consumed\n", inst->id); >> + return; >> + } >> + >> + dst_buf = wave6_get_dst_buf_by_addr(inst, info- >> >frame_decoded_addr); >> + if (dst_buf) { >> + struct vpu_buffer *dst_vpu_buf = >> wave6_to_vpu_buf(dst_buf); >> + >> + if (wave6_to_vpu_buf(dst_buf)->used) { >> + dev_warn(inst->dev->dev, "[%d] duplication >> frame buffer\n", inst->id); >> + inst->sequence++; >> + } >> + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); >> + dst_vpu_buf->used = true; >> + if (state == VB2_BUF_STATE_ERROR) >> + dst_vpu_buf->error = true; >> + dst_vpu_buf->ts_input = vpu_buf->ts_input; >> + dst_vpu_buf->ts_start = vpu_buf->ts_start; >> + dst_vpu_buf->ts_finish = ktime_get_raw(); >> + dst_vpu_buf->hw_time = wave6_vpu_cycle_to_ns(inst- >> >dev, info->cycle.frame_cycle); >> + } >> + >> + v4l2_m2m_src_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, >> src_buf); >> + if (state == VB2_BUF_STATE_ERROR) { >> + dprintk(inst->dev->dev, "[%d] error frame %d\n", >> inst->id, inst->sequence); >> + inst->error_buf_num++; >> + } >> + v4l2_m2m_buf_done(src_buf, state); >> + inst->processed_buf_num++; >> +} >> + >> +static void wave6_handle_skipped_frame(struct vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *src_buf; >> + struct vpu_buffer *vpu_buf; >> + >> + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); >> + if (!src_buf) >> + return; >> + >> + vpu_buf = wave6_to_vpu_buf(src_buf); >> + if (!vpu_buf || !vpu_buf->consumed) >> + return; >> + >> + dprintk(inst->dev->dev, "[%d] skip frame %d\n", inst->id, >> inst->sequence); >> + >> + inst->sequence++; >> + inst->processed_buf_num++; >> + inst->error_buf_num++; >> + v4l2_m2m_src_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, >> src_buf); >> + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); >> +} >> + >> +static void wave6_handle_display_frame(struct vpu_instance *inst, >> + dma_addr_t addr, enum >> vb2_buffer_state state) >> +{ >> + struct vb2_v4l2_buffer *dst_buf; >> + struct vpu_buffer *vpu_buf; >> + >> + dst_buf = wave6_get_dst_buf_by_addr(inst, addr); >> + if (!dst_buf) >> + return; >> + >> + vpu_buf = wave6_to_vpu_buf(dst_buf); >> + if (!vpu_buf->used) { >> + dprintk(inst->dev->dev, "[%d] recycle display >> buffer\n", inst->id); >> + vpu_buf->consumed = false; >> + return; >> + } >> + >> + if (inst->dst_fmt.num_planes == 1) { >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, >> + inst- >> >dst_fmt.plane_fmt[0].sizeimage); >> + } else if (inst->dst_fmt.num_planes == 2) { >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, >> + inst- >> >dst_fmt.plane_fmt[0].sizeimage); >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, >> + inst- >> >dst_fmt.plane_fmt[1].sizeimage); >> + } else if (inst->dst_fmt.num_planes == 3) { >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, >> + inst- >> >dst_fmt.plane_fmt[0].sizeimage); >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, >> + inst- >> >dst_fmt.plane_fmt[1].sizeimage); >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 2, >> + inst- >> >dst_fmt.plane_fmt[2].sizeimage); >> + } >> + >> + vpu_buf->ts_output = ktime_get_raw(); >> + wave6_vpu_handle_performance(inst, vpu_buf); >> + >> + if (vpu_buf->error) >> + state = VB2_BUF_STATE_ERROR; >> + dst_buf->sequence = inst->sequence++; >> + dst_buf->field = V4L2_FIELD_NONE; >> + if (state == VB2_BUF_STATE_ERROR) >> + dprintk(inst->dev->dev, "[%d] discard frame %d\n", >> inst->id, dst_buf->sequence); >> + v4l2_m2m_dst_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, >> dst_buf); >> + v4l2_m2m_buf_done(dst_buf, state); >> +} >> + >> +static void wave6_handle_display_frames(struct vpu_instance *inst, >> + struct dec_output_info >> *info) >> +{ >> + int i; >> + >> + for (i = 0; i < info->disp_frame_num; i++) >> + wave6_handle_display_frame(inst, >> + info->disp_frame_addr[i], >> + VB2_BUF_STATE_DONE); >> +} >> + >> +static void wave6_handle_discard_frames(struct vpu_instance *inst, >> + struct dec_output_info >> *info) >> +{ >> + int i; >> + >> + for (i = 0; i < info->release_disp_frame_num; i++) >> + wave6_handle_display_frame(inst, >> + info- >> >release_disp_frame_addr[i], >> + VB2_BUF_STATE_ERROR); >> +} >> + >> +static void wave6_handle_last_frame(struct vpu_instance *inst, >> + struct vb2_v4l2_buffer *dst_buf) >> +{ >> + if (!dst_buf) { >> + dst_buf = v4l2_m2m_dst_buf_remove(inst- >> >v4l2_fh.m2m_ctx); >> + if (!dst_buf) { >> + inst->next_buf_last = true; >> + return; >> + } >> + } >> + >> + if (inst->dst_fmt.num_planes == 1) { >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); >> + } else if (inst->dst_fmt.num_planes == 2) { >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); >> + } else if (inst->dst_fmt.num_planes == 3) { >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 2, 0); >> + } >> + >> + dst_buf->flags |= V4L2_BUF_FLAG_LAST; >> + dst_buf->field = V4L2_FIELD_NONE; >> + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); >> + >> + if (inst->state != VPU_INST_STATE_INIT_SEQ) { >> + dprintk(inst->dev->dev, "[%d] eos\n", inst->id); >> + inst->eos = true; >> + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, >> false); >> + } >> +} >> + >> +static void wave6_vpu_dec_retry_one_frame(struct vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *src_buf; >> + struct vpu_buffer *vpu_buf; >> + >> + src_buf = v4l2_m2m_next_src_buf(inst->v4l2_fh.m2m_ctx); >> + if (!src_buf) >> + return; >> + >> + vpu_buf = wave6_to_vpu_buf(src_buf); >> + vpu_buf->consumed = false; >> +} >> + >> +static void wave6_vpu_dec_handle_source_change(struct vpu_instance >> *inst, >> + struct >> dec_initial_info *info) >> +{ >> + static const struct v4l2_event vpu_event_src_ch = { >> + .type = V4L2_EVENT_SOURCE_CHANGE, >> + .u.src_change.changes = >> V4L2_EVENT_SRC_CH_RESOLUTION, >> + }; >> + >> + dprintk(inst->dev->dev, "pic size %dx%d profile %d, >> min_fb_cnt : %d | min_disp_cnt : %d\n", >> + info->pic_width, info->pic_height, >> + info->profile, info->min_frame_buffer_count, info- >> >frame_buf_delay); >> + >> + wave6_vpu_dec_retry_one_frame(inst); >> + wave6_vpu_dec_give_command(inst, DEC_RESET_FRAMEBUF_INFO, >> NULL); >> + >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); >> + >> + inst->crop.left = info->pic_crop_rect.left; >> + inst->crop.top = info->pic_crop_rect.top; >> + inst->crop.width = info->pic_crop_rect.right - inst- >> >crop.left; >> + inst->crop.height = info->pic_crop_rect.bottom - inst- >> >crop.top; >> + >> + wave6_update_v4l2_ctrls(inst, info); >> + wave6_update_color_info(inst, info); >> + wave6_update_pix_fmt(&inst->src_fmt, info->pic_width, info- >> >pic_height); >> + wave6_update_pix_fmt_cap(&inst->dst_fmt, >> + info->pic_width, info->pic_height, >> + true); >> + >> + trace_source_change(inst, info); >> + >> + v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_src_ch); >> +} >> + >> +static void wave6_vpu_dec_handle_decoding_warn_error(struct >> vpu_instance *inst, >> + struct >> dec_output_info *info) >> +{ >> + if (info->warn_info) >> + dev_dbg(inst->dev->dev, "[%d] decoding %d warning >> 0x%x\n", >> + inst->id, inst->processed_buf_num, info- >> >warn_info); >> + >> + if (info->error_reason) >> + dev_err(inst->dev->dev, "[%d] decoding %d error >> 0x%x\n", >> + inst->id, inst->processed_buf_num, info- >> >error_reason); >> +} >> + >> +static void wave6_vpu_dec_finish_decode(struct vpu_instance *inst, >> bool error) >> +{ >> + struct dec_output_info info; >> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; >> + int ret; >> + >> + ret = wave6_vpu_dec_get_output_info(inst, &info); >> + if (ret) >> + goto finish_decode; >> + >> + trace_dec_done(inst, &info); >> + >> + dev_dbg(inst->dev->dev, "dec %d dis %d noti_flag %d >> stream_end %d\n", >> + info.frame_decoded, info.frame_display, >> + info.notification_flags, info.stream_end); >> + >> + if (info.notification_flags & DEC_NOTI_FLAG_NO_FB) { >> + wave6_vpu_dec_retry_one_frame(inst); >> + goto finish_decode; >> + } >> + >> + if (info.notification_flags & DEC_NOTI_FLAG_SEQ_CHANGE) { >> + struct dec_initial_info initial_info = {0}; >> + >> + v4l2_m2m_mark_stopped(m2m_ctx); >> + >> + if (info.frame_display) >> + wave6_handle_display_frames(inst, &info); >> + >> + if (info.release_disp_frame_num) >> + wave6_handle_discard_frames(inst, &info); >> + >> + wave6_vpu_dec_give_command(inst, DEC_GET_SEQ_INFO, >> &initial_info); >> + wave6_vpu_dec_handle_source_change(inst, >> &initial_info); >> + >> + wave6_handle_last_frame(inst, NULL); >> + >> + goto finish_decode; >> + } >> + >> + wave6_vpu_dec_handle_decoding_warn_error(inst, &info); >> + >> + if (info.frame_decoded) >> + wave6_handle_decoded_frame(inst, &info); >> + else >> + wave6_handle_skipped_frame(inst); >> + >> + if (info.frame_display) >> + wave6_handle_display_frames(inst, &info); >> + >> + if (info.release_disp_frame_num) >> + wave6_handle_discard_frames(inst, &info); >> + >> + if (info.stream_end && !inst->eos) >> + wave6_handle_last_frame(inst, NULL); >> + >> +finish_decode: >> + wave6_vpu_finish_job(inst); >> +} >> + >> +static int wave6_vpu_dec_querycap(struct file *file, void *fh, >> struct v4l2_capability *cap) >> +{ >> + strscpy(cap->driver, VPU_DEC_DRV_NAME, sizeof(cap->driver)); >> + strscpy(cap->card, VPU_DEC_DRV_NAME, sizeof(cap->card)); >> + strscpy(cap->bus_info, "platform:" VPU_DEC_DRV_NAME, >> sizeof(cap->bus_info)); >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_enum_framesizes(struct file *f, void *fh, >> struct v4l2_frmsizeenum *fsize) >> +{ >> + const struct vpu_format *vpu_fmt; >> + >> + if (fsize->index) >> + return -EINVAL; >> + >> + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, >> VPU_FMT_TYPE_CODEC); >> + if (!vpu_fmt) { >> + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, >> VPU_FMT_TYPE_RAW); >> + if (!vpu_fmt) >> + return -EINVAL; >> + } >> + >> + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; >> + fsize->stepwise.min_width = vpu_fmt->min_width; >> + fsize->stepwise.max_width = vpu_fmt->max_width; >> + fsize->stepwise.step_width = W6_DEC_PIC_SIZE_STEP; >> + fsize->stepwise.min_height = vpu_fmt->min_height; >> + fsize->stepwise.max_height = vpu_fmt->max_height; >> + fsize->stepwise.step_height = W6_DEC_PIC_SIZE_STEP; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_enum_fmt_cap(struct file *file, void *fh, >> struct v4l2_fmtdesc *f) >> +{ >> + const struct vpu_format *vpu_fmt; >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, >> VPU_FMT_TYPE_RAW); >> + if (!vpu_fmt) >> + return -EINVAL; >> + >> + f->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + f->flags = 0; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_try_fmt_cap(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + const struct vpu_format *vpu_fmt; >> + int width, height; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + if (!V4L2_TYPE_IS_CAPTURE(f->type)) >> + return -EINVAL; >> + >> + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, >> VPU_FMT_TYPE_RAW); >> + if (!vpu_fmt) { >> + width = inst->dst_fmt.width; >> + height = inst->dst_fmt.height; >> + pix_mp->pixelformat = inst->dst_fmt.pixelformat; >> + pix_mp->num_planes = inst->dst_fmt.num_planes; >> + } else { >> + width = clamp(pix_mp->width, >> + vpu_fmt->min_width, round_up(inst- >> >src_fmt.width, 32)); >> + height = clamp(pix_mp->height, >> + vpu_fmt->min_height, inst- >> >src_fmt.height); >> + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + pix_mp->num_planes = vpu_fmt->num_planes; >> + } >> + >> + if (inst->state >= VPU_INST_STATE_INIT_SEQ) { >> + width = inst->dst_fmt.width; >> + height = inst->dst_fmt.height; >> + } >> + >> + wave6_update_pix_fmt_cap(pix_mp, width, height, false); >> + pix_mp->colorspace = inst->colorspace; >> + pix_mp->ycbcr_enc = inst->ycbcr_enc; >> + pix_mp->quantization = inst->quantization; >> + pix_mp->xfer_func = inst->xfer_func; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_s_fmt_cap(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i, ret; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + ret = wave6_vpu_dec_try_fmt_cap(file, fh, f); >> + if (ret) >> + return ret; >> + >> + inst->dst_fmt.width = pix_mp->width; >> + inst->dst_fmt.height = pix_mp->height; >> + inst->dst_fmt.pixelformat = pix_mp->pixelformat; >> + inst->dst_fmt.field = pix_mp->field; >> + inst->dst_fmt.flags = pix_mp->flags; >> + inst->dst_fmt.num_planes = pix_mp->num_planes; >> + for (i = 0; i < inst->dst_fmt.num_planes; i++) { >> + inst->dst_fmt.plane_fmt[i].bytesperline = pix_mp- >> >plane_fmt[i].bytesperline; >> + inst->dst_fmt.plane_fmt[i].sizeimage = pix_mp- >> >plane_fmt[i].sizeimage; >> + } >> + >> + if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12 || >> + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV12M) { >> + inst->cbcr_interleave = true; >> + inst->nv21 = false; >> + } else if (inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21 || >> + inst->dst_fmt.pixelformat == V4L2_PIX_FMT_NV21M) >> { >> + inst->cbcr_interleave = true; >> + inst->nv21 = true; >> + } else { >> + inst->cbcr_interleave = false; >> + inst->nv21 = false; >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_g_fmt_cap(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i; >> + >> + pix_mp->width = inst->dst_fmt.width; >> + pix_mp->height = inst->dst_fmt.height; >> + pix_mp->pixelformat = inst->dst_fmt.pixelformat; >> + pix_mp->field = inst->dst_fmt.field; >> + pix_mp->flags = inst->dst_fmt.flags; >> + pix_mp->num_planes = inst->dst_fmt.num_planes; >> + for (i = 0; i < pix_mp->num_planes; i++) { >> + pix_mp->plane_fmt[i].bytesperline = inst- >> >dst_fmt.plane_fmt[i].bytesperline; >> + pix_mp->plane_fmt[i].sizeimage = inst- >> >dst_fmt.plane_fmt[i].sizeimage; >> + } >> + >> + pix_mp->colorspace = inst->colorspace; >> + pix_mp->ycbcr_enc = inst->ycbcr_enc; >> + pix_mp->quantization = inst->quantization; >> + pix_mp->xfer_func = inst->xfer_func; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_enum_fmt_out(struct file *file, void *fh, >> struct v4l2_fmtdesc *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + const struct vpu_format *vpu_fmt; >> + >> + dev_dbg(inst->dev->dev, "%s: index %d\n", __func__, f- >> >index); >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, >> VPU_FMT_TYPE_CODEC); >> + if (!vpu_fmt) >> + return -EINVAL; >> + >> + f->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + f->flags = 0; >> + f->flags = V4L2_FMT_FLAG_DYN_RESOLUTION | >> V4L2_FMT_FLAG_COMPRESSED; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_try_fmt_out(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + const struct vpu_format *vpu_fmt; >> + int width, height; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + if (!V4L2_TYPE_IS_OUTPUT(f->type)) >> + return -EINVAL; >> + >> + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, >> VPU_FMT_TYPE_CODEC); >> + if (!vpu_fmt) { >> + width = inst->src_fmt.width; >> + height = inst->src_fmt.height; >> + pix_mp->pixelformat = inst->src_fmt.pixelformat; >> + pix_mp->num_planes = inst->src_fmt.num_planes; >> + } else { >> + width = pix_mp->width; >> + height = pix_mp->height; >> + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + pix_mp->num_planes = vpu_fmt->num_planes; >> + } >> + >> + wave6_update_pix_fmt(pix_mp, width, height); >> + pix_mp->colorspace = inst->colorspace; >> + pix_mp->ycbcr_enc = inst->ycbcr_enc; >> + pix_mp->quantization = inst->quantization; >> + pix_mp->xfer_func = inst->xfer_func; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_s_fmt_out(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane in_pix_mp = f->fmt.pix_mp; >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i, ret; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + ret = wave6_vpu_dec_try_fmt_out(file, fh, f); >> + if (ret) >> + return ret; >> + >> + pix_mp->colorspace = in_pix_mp.colorspace; >> + pix_mp->ycbcr_enc = in_pix_mp.ycbcr_enc; >> + pix_mp->quantization = in_pix_mp.quantization; >> + pix_mp->xfer_func = in_pix_mp.xfer_func; >> + >> + inst->src_fmt.width = pix_mp->width; >> + inst->src_fmt.height = pix_mp->height; >> + inst->src_fmt.pixelformat = pix_mp->pixelformat; >> + inst->src_fmt.field = pix_mp->field; >> + inst->src_fmt.flags = pix_mp->flags; >> + inst->src_fmt.num_planes = pix_mp->num_planes; >> + for (i = 0; i < inst->src_fmt.num_planes; i++) { >> + inst->src_fmt.plane_fmt[i].bytesperline = pix_mp- >> >plane_fmt[i].bytesperline; >> + inst->src_fmt.plane_fmt[i].sizeimage = pix_mp- >> >plane_fmt[i].sizeimage; >> + } >> + >> + inst->colorspace = pix_mp->colorspace; >> + inst->ycbcr_enc = pix_mp->ycbcr_enc; >> + inst->quantization = pix_mp->quantization; >> + inst->xfer_func = pix_mp->xfer_func; >> + >> + wave6_update_pix_fmt_cap(&inst->dst_fmt, >> + pix_mp->width, pix_mp->height, >> + true); >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_g_fmt_out(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i; >> + >> + pix_mp->width = inst->src_fmt.width; >> + pix_mp->height = inst->src_fmt.height; >> + pix_mp->pixelformat = inst->src_fmt.pixelformat; >> + pix_mp->field = inst->src_fmt.field; >> + pix_mp->flags = inst->src_fmt.flags; >> + pix_mp->num_planes = inst->src_fmt.num_planes; >> + for (i = 0; i < pix_mp->num_planes; i++) { >> + pix_mp->plane_fmt[i].bytesperline = inst- >> >src_fmt.plane_fmt[i].bytesperline; >> + pix_mp->plane_fmt[i].sizeimage = inst- >> >src_fmt.plane_fmt[i].sizeimage; >> + } >> + >> + pix_mp->colorspace = inst->colorspace; >> + pix_mp->ycbcr_enc = inst->ycbcr_enc; >> + pix_mp->quantization = inst->quantization; >> + pix_mp->xfer_func = inst->xfer_func; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_g_selection(struct file *file, void *fh, >> struct v4l2_selection *s) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + >> + dev_dbg(inst->dev->dev, "%s: type %d target %d\n", >> + __func__, s->type, s->target); >> + >> + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && >> + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) >> + return -EINVAL; >> + >> + switch (s->target) { >> + case V4L2_SEL_TGT_COMPOSE_BOUNDS: >> + s->r.left = 0; >> + s->r.top = 0; >> + s->r.width = inst->dst_fmt.width; >> + s->r.height = inst->dst_fmt.height; >> + break; >> + case V4L2_SEL_TGT_COMPOSE_PADDED: >> + case V4L2_SEL_TGT_COMPOSE: >> + s->r.left = 0; >> + s->r.top = 0; >> + if (inst->scaler_info.enable) { >> + s->r.width = inst->scaler_info.width; >> + s->r.height = inst->scaler_info.height; >> + } else if (inst->crop.width && inst->crop.height) { >> + s->r = inst->crop; >> + } else { >> + s->r.width = inst->src_fmt.width; >> + s->r.height = inst->src_fmt.height; >> + } >> + break; >> + case V4L2_SEL_TGT_CROP: >> + case V4L2_SEL_TGT_CROP_DEFAULT: >> + case V4L2_SEL_TGT_CROP_BOUNDS: >> + case V4L2_SEL_TGT_COMPOSE_DEFAULT: >> + s->r.left = 0; >> + s->r.top = 0; >> + s->r.width = inst->src_fmt.width; >> + s->r.height = inst->src_fmt.height; >> + if (inst->crop.width && inst->crop.height) >> + s->r = inst->crop; >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_s_selection(struct file *file, void *fh, >> struct v4l2_selection *s) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + int step = 4; >> + int scale_width, scale_height; >> + int min_scale_width, min_scale_height; >> + >> + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && >> + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) >> + return -EINVAL; >> + >> + if (s->target != V4L2_SEL_TGT_COMPOSE) >> + return -EINVAL; >> + >> + if (!(s->flags & (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE))) >> + s->flags |= V4L2_SEL_FLAG_LE; >> + >> + scale_width = clamp(s->r.width, W6_MIN_DEC_PIC_WIDTH, >> + round_up(inst->src_fmt.width, 32)); >> + scale_height = clamp(s->r.height, W6_MIN_DEC_PIC_HEIGHT, >> + inst->src_fmt.height); >> + if (s->flags & V4L2_SEL_FLAG_GE) { >> + scale_width = round_up(scale_width, step); >> + scale_height = round_up(scale_height, step); >> + } >> + if (s->flags & V4L2_SEL_FLAG_LE) { >> + scale_width = round_down(scale_width, step); >> + scale_height = round_down(scale_height, step); >> + } >> + >> + if (scale_width < inst->src_fmt.width || >> + scale_height < inst->src_fmt.height) >> + inst->scaler_info.enable = true; >> + >> + if (inst->scaler_info.enable) { >> + min_scale_width = ALIGN((inst->src_fmt.width / 8), >> step); >> + min_scale_height = ALIGN((inst->src_fmt.height / 8), >> step); >> + >> + if (scale_width < W6_MIN_DEC_PIC_WIDTH) >> + scale_width = W6_MIN_DEC_PIC_WIDTH; >> + if (scale_width < min_scale_width) >> + scale_width = min_scale_width; >> + if (scale_height < W6_MIN_DEC_PIC_HEIGHT) >> + scale_height = W6_MIN_DEC_PIC_HEIGHT; >> + if (scale_height < min_scale_height) >> + scale_height = min_scale_height; >> + >> + inst->scaler_info.width = scale_width; >> + inst->scaler_info.height = scale_height; >> + } >> + >> + s->r.left = 0; >> + s->r.top = 0; >> + s->r.width = scale_width; >> + s->r.height = scale_height; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_decoder_cmd(struct file *file, void *fh, >> struct v4l2_decoder_cmd *dc) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + int ret; >> + >> + dev_dbg(inst->dev->dev, "%s: cmd %d\n", __func__, dc->cmd); >> + >> + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); >> + if (ret) >> + return ret; >> + >> + switch (dc->cmd) { >> + case V4L2_DEC_CMD_STOP: >> + dprintk(inst->dev->dev, "[%d] drain\n", inst->id); >> + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, >> true); >> + v4l2_m2m_try_schedule(inst->v4l2_fh.m2m_ctx); >> + break; >> + case V4L2_DEC_CMD_START: >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static const struct v4l2_ioctl_ops wave6_vpu_dec_ioctl_ops = { >> + .vidioc_querycap = wave6_vpu_dec_querycap, >> + .vidioc_enum_framesizes = wave6_vpu_dec_enum_framesizes, >> + >> + .vidioc_enum_fmt_vid_cap = wave6_vpu_dec_enum_fmt_cap, >> + .vidioc_s_fmt_vid_cap_mplane = wave6_vpu_dec_s_fmt_cap, >> + .vidioc_g_fmt_vid_cap_mplane = wave6_vpu_dec_g_fmt_cap, >> + .vidioc_try_fmt_vid_cap_mplane = wave6_vpu_dec_try_fmt_cap, >> + >> + .vidioc_enum_fmt_vid_out = wave6_vpu_dec_enum_fmt_out, >> + .vidioc_s_fmt_vid_out_mplane = wave6_vpu_dec_s_fmt_out, >> + .vidioc_g_fmt_vid_out_mplane = wave6_vpu_dec_g_fmt_out, >> + .vidioc_try_fmt_vid_out_mplane = wave6_vpu_dec_try_fmt_out, >> + >> + .vidioc_g_selection = wave6_vpu_dec_g_selection, >> + .vidioc_s_selection = wave6_vpu_dec_s_selection, >> + >> + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, >> + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, >> + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, >> + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, >> + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, >> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, >> + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, >> + .vidioc_streamon = v4l2_m2m_ioctl_streamon, >> + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, >> + >> + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, >> + .vidioc_decoder_cmd = wave6_vpu_dec_decoder_cmd, >> + >> + .vidioc_subscribe_event = wave6_vpu_subscribe_event, >> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, >> +}; >> + >> +static int wave6_vpu_dec_s_ctrl(struct v4l2_ctrl *ctrl) >> +{ >> + struct vpu_instance *inst = wave6_ctrl_to_vpu_inst(ctrl); >> + >> + trace_s_ctrl(inst, ctrl); >> + >> + dev_dbg(inst->dev->dev, "%s: name %s value %d\n", >> + __func__, ctrl->name, ctrl->val); >> + >> + switch (ctrl->id) { >> + case V4L2_CID_VPU_THUMBNAIL_MODE: >> + inst->thumbnail_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: >> + inst->disp_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: >> + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: >> + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: >> + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static const struct v4l2_ctrl_ops wave6_vpu_dec_ctrl_ops = { >> + .s_ctrl = wave6_vpu_dec_s_ctrl, >> +}; >> + >> +static const struct v4l2_ctrl_config wave6_vpu_thumbnail_mode = { >> + .ops = &wave6_vpu_dec_ctrl_ops, >> + .id = V4L2_CID_VPU_THUMBNAIL_MODE, >> + .name = "thumbnail mode", >> + .type = V4L2_CTRL_TYPE_BOOLEAN, >> + .def = 0, >> + .min = 0, >> + .max = 1, >> + .step = 1, >> + .flags = V4L2_CTRL_FLAG_WRITE_ONLY, >> +}; >> + >> +static void wave6_set_dec_openparam(struct dec_open_param >> *open_param, >> + struct vpu_instance *inst) >> +{ >> + open_param->inst_buffer.temp_base = inst->dev- >> >temp_vbuf.daddr; >> + open_param->inst_buffer.temp_size = inst->dev- >> >temp_vbuf.size; >> + open_param->bs_mode = BS_MODE_PIC_END; >> + open_param->stream_endian = VPU_STREAM_ENDIAN; >> + open_param->frame_endian = VPU_FRAME_ENDIAN; >> + open_param->disp_mode = inst->disp_mode; >> +} >> + >> +static int wave6_vpu_dec_create_instance(struct vpu_instance *inst) >> +{ >> + int ret; >> + struct dec_open_param open_param; >> + >> + memset(&open_param, 0, sizeof(struct dec_open_param)); >> + >> + wave6_vpu_activate(inst->dev); >> + ret = pm_runtime_resume_and_get(inst->dev->dev); >> + if (ret) { >> + dev_err(inst->dev->dev, "runtime_resume failed >> %d\n", ret); >> + return ret; >> + } >> + >> + wave6_vpu_wait_activated(inst->dev); >> + >> + inst->std = wave6_to_codec_std(inst->type, inst- >> >src_fmt.pixelformat); >> + if (inst->std == STD_UNKNOWN) { >> + dev_err(inst->dev->dev, "unsupported pixelformat: >> %.4s\n", >> + (char *)&inst->src_fmt.pixelformat); >> + ret = -EINVAL; >> + goto error_pm; >> + } >> + >> + wave6_set_dec_openparam(&open_param, inst); >> + >> + ret = wave6_vpu_dec_open(inst, &open_param); >> + if (ret) { >> + dev_err(inst->dev->dev, "failed create instance : >> %d\n", ret); >> + goto error_pm; >> + } >> + >> + dprintk(inst->dev->dev, "[%d] decoder\n", inst->id); >> + >> + if (inst->thumbnail_mode) >> + wave6_vpu_dec_give_command(inst, >> ENABLE_DEC_THUMBNAIL_MODE, NULL); >> + >> + wave6_vpu_create_dbgfs_file(inst); >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_OPEN); >> + inst->v4l2_fh.m2m_ctx->ignore_cap_streaming = true; >> + v4l2_m2m_set_dst_buffered(inst->v4l2_fh.m2m_ctx, true); >> + >> + return 0; >> + >> +error_pm: >> + pm_runtime_put_sync(inst->dev->dev); >> + >> + return ret; >> +} >> + >> +static int wave6_vpu_dec_prepare_fb(struct vpu_instance *inst) >> +{ >> + int ret; >> + unsigned int i; >> + unsigned int fb_num; >> + unsigned int mv_num; >> + unsigned int fb_stride; >> + unsigned int fb_height; >> + struct dec_info *p_dec_info = &inst->codec_info->dec_info; >> + >> + fb_num = p_dec_info->initial_info.min_frame_buffer_count; >> + mv_num = p_dec_info->initial_info.req_mv_buffer_count; >> + >> + fb_stride = ALIGN(inst->src_fmt.width, 32); >> + fb_height = ALIGN(inst->src_fmt.height, 32); >> + >> + for (i = 0; i < fb_num; i++) { >> + struct frame_buffer *frame = &inst->frame_buf[i]; >> + struct vpu_buf *vframe = &inst->frame_vbuf[i]; >> + unsigned int l_size = fb_stride * fb_height; >> + unsigned int ch_size = ALIGN(fb_stride / 2, 32) * >> fb_height; >> + >> + vframe->size = l_size + ch_size; >> + ret = wave6_alloc_dma(inst->dev->dev, vframe); >> + if (ret) { >> + dev_err(inst->dev->dev, "alloc FBC buffer >> fail : %zu\n", >> + vframe->size); >> + goto error; >> + } >> + >> + frame->buf_y = vframe->daddr; >> + frame->buf_cb = vframe->daddr + l_size; >> + frame->buf_cr = (dma_addr_t)-1; >> + frame->width = inst->src_fmt.width; >> + frame->stride = fb_stride; >> + frame->height = fb_height; >> + frame->map_type = COMPRESSED_FRAME_MAP; >> + } >> + >> + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_Y_TBL, >> fb_num); >> + if (ret) >> + goto error; >> + >> + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_C_TBL, >> fb_num); >> + if (ret) >> + goto error; >> + >> + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_MV_COL, >> mv_num); >> + if (ret) >> + goto error; >> + >> + ret = wave6_vpu_dec_register_frame_buffer_ex(inst, fb_num, >> fb_stride, >> + fb_height, >> + >> COMPRESSED_FRAME_MAP); >> + if (ret) { >> + dev_err(inst->dev->dev, "register frame buffer fail >> %d\n", ret); >> + goto error; >> + } >> + >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_PIC_RUN); >> + >> + return 0; >> + >> +error: >> + wave6_vpu_dec_release_fb(inst); >> + return ret; >> +} >> + >> +static int wave6_vpu_dec_queue_setup(struct vb2_queue *q, unsigned >> int *num_buffers, >> + unsigned int *num_planes, >> unsigned int sizes[], >> + struct device *alloc_devs[]) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(q); >> + struct v4l2_pix_format_mplane inst_format = >> + (V4L2_TYPE_IS_OUTPUT(q->type)) ? inst->src_fmt : >> inst->dst_fmt; >> + unsigned int i; >> + >> + dev_dbg(inst->dev->dev, "%s: num_buffers %d num_planes %d >> type %d\n", >> + __func__, *num_buffers, *num_planes, q->type); >> + >> + if (*num_planes) { >> + if (inst_format.num_planes != *num_planes) >> + return -EINVAL; >> + >> + for (i = 0; i < *num_planes; i++) { >> + if (sizes[i] < >> inst_format.plane_fmt[i].sizeimage) >> + return -EINVAL; >> + } >> + } else { >> + *num_planes = inst_format.num_planes; >> + for (i = 0; i < *num_planes; i++) { >> + sizes[i] = >> inst_format.plane_fmt[i].sizeimage; >> + dev_dbg(inst->dev->dev, "size[%d] : %d\n", >> i, sizes[i]); >> + } >> + >> + if (V4L2_TYPE_IS_CAPTURE(q->type)) { >> + struct v4l2_ctrl *ctrl; >> + unsigned int min_disp_cnt = 0; >> + >> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, >> + >> V4L2_CID_MIN_BUFFERS_FOR_CAPTURE); >> + if (ctrl) >> + min_disp_cnt = >> v4l2_ctrl_g_ctrl(ctrl); >> + >> + *num_buffers = max(*num_buffers, >> min_disp_cnt); >> + >> + if (*num_buffers > WAVE6_MAX_FBS) >> + *num_buffers = min_disp_cnt; >> + } >> + } >> + >> + if (V4L2_TYPE_IS_OUTPUT(q->type) && >> + inst->state == VPU_INST_STATE_SEEK) { >> + wave6_vpu_pause(inst->dev->dev, 0); >> + wave6_vpu_dec_destroy_instance(inst); >> + wave6_vpu_pause(inst->dev->dev, 1); >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_dec_seek_header(struct vpu_instance *inst) >> +{ >> + struct dec_initial_info initial_info; >> + int ret; >> + >> + memset(&initial_info, 0, sizeof(struct dec_initial_info)); >> + >> + ret = wave6_vpu_dec_issue_seq_init(inst); >> + if (ret) { >> + dev_err(inst->dev->dev, "failed >> wave6_vpu_dec_issue_seq_init %d\n", ret); >> + return ret; >> + } >> + >> + if (wave6_vpu_wait_interrupt(inst, W6_VPU_TIMEOUT) < 0) >> + dev_err(inst->dev->dev, "failed to call >> vpu_wait_interrupt()\n"); >> + >> + ret = wave6_vpu_dec_complete_seq_init(inst, &initial_info); >> + if (ret) { >> + dev_err(inst->dev->dev, "vpu_dec_complete_seq_init: >> %d, reason : 0x%x\n", >> + ret, initial_info.err_reason); >> + if ((initial_info.err_reason & >> WAVE6_SYSERR_NOT_SUPPORT) || >> + (initial_info.err_reason & >> WAVE6_SYSERR_NOT_SUPPORT_PROFILE)) { >> + ret = -EINVAL; >> + } else if ((initial_info.err_reason & >> HEVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND) || >> + (initial_info.err_reason & >> AVC_ETCERR_INIT_SEQ_SPS_NOT_FOUND)) { >> + wave6_handle_skipped_frame(inst); >> + ret = 0; >> + } >> + } else { >> + wave6_vpu_dec_handle_source_change(inst, >> &initial_info); >> + inst->v4l2_fh.m2m_ctx->ignore_cap_streaming = false; >> + v4l2_m2m_set_dst_buffered(inst->v4l2_fh.m2m_ctx, >> false); >> + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(inst- >> >v4l2_fh.m2m_ctx))) >> + wave6_handle_last_frame(inst, NULL); >> + } >> + >> + return ret; >> +} >> + >> +static void wave6_vpu_dec_buf_queue_src(struct vb2_buffer *vb) >> +{ >> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); >> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); >> + >> + dev_dbg(inst->dev->dev, "type %4d index %4d size[0] %4ld >> size[1] : %4ld | size[2] : %4ld\n", >> + vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, >> 0), >> + vb2_plane_size(&vbuf->vb2_buf, 1), >> vb2_plane_size(&vbuf->vb2_buf, 2)); >> + >> + vbuf->sequence = inst->queued_src_buf_num++; >> + vpu_buf->ts_input = ktime_get_raw(); >> + >> + v4l2_m2m_buf_queue(inst->v4l2_fh.m2m_ctx, vbuf); >> +} >> + >> +static void wave6_vpu_dec_buf_queue_dst(struct vb2_buffer *vb) >> +{ >> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); >> + >> + dev_dbg(inst->dev->dev, "type %4d index %4d size[0] %4ld >> size[1] : %4ld | size[2] : %4ld\n", >> + vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, >> 0), >> + vb2_plane_size(&vbuf->vb2_buf, 1), >> vb2_plane_size(&vbuf->vb2_buf, 2)); >> + >> + inst->queued_dst_buf_num++; >> + if (inst->next_buf_last) { >> + wave6_handle_last_frame(inst, vbuf); >> + inst->next_buf_last = false; >> + } else { >> + v4l2_m2m_buf_queue(inst->v4l2_fh.m2m_ctx, vbuf); >> + } >> +} >> + >> +static void wave6_vpu_dec_buf_queue(struct vb2_buffer *vb) >> +{ >> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); >> + >> + vpu_buf->consumed = false; >> + vpu_buf->used = false; >> + vpu_buf->error = false; >> + if (V4L2_TYPE_IS_OUTPUT(vb->type)) >> + wave6_vpu_dec_buf_queue_src(vb); >> + else >> + wave6_vpu_dec_buf_queue_dst(vb); >> +} >> + >> +static int wave6_vpu_dec_start_streaming(struct vb2_queue *q, >> unsigned int count) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(q); >> + struct v4l2_pix_format_mplane *fmt; >> + int ret = 0; >> + >> + trace_start_streaming(inst, q->type); >> + >> + wave6_vpu_pause(inst->dev->dev, 0); >> + >> + if (V4L2_TYPE_IS_OUTPUT(q->type)) { >> + fmt = &inst->src_fmt; >> + if (inst->state == VPU_INST_STATE_NONE) { >> + ret = wave6_vpu_dec_create_instance(inst); >> + if (ret) >> + goto exit; >> + } >> + >> + if (inst->state == VPU_INST_STATE_SEEK) >> + wave6_vpu_set_instance_state(inst, inst- >> >state_in_seek); >> + } else { >> + fmt = &inst->dst_fmt; >> + if (inst->state == VPU_INST_STATE_INIT_SEQ) { >> + ret = wave6_vpu_dec_prepare_fb(inst); >> + if (ret) >> + goto exit; >> + } >> + } >> + >> +exit: >> + wave6_vpu_pause(inst->dev->dev, 1); >> + if (ret) >> + wave6_vpu_return_buffers(inst, q->type, >> VB2_BUF_STATE_QUEUED); >> + >> + dprintk(inst->dev->dev, "[%d] %s %c%c%c%c %dx%d, %d buffers, >> ret = %d\n", >> + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : >> "capture", >> + fmt->pixelformat, >> + fmt->pixelformat >> 8, >> + fmt->pixelformat >> 16, >> + fmt->pixelformat >> 24, >> + fmt->width, fmt->height, vb2_get_num_buffers(q), >> ret); >> + >> + return ret; >> +} >> + >> +static void wave6_vpu_dec_stop_streaming(struct vb2_queue *q) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(q); >> + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; >> + >> + trace_stop_streaming(inst, q->type); >> + >> + dprintk(inst->dev->dev, "[%d] %s, input %d, decode %d error >> %d\n", >> + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : >> "capture", >> + inst->queued_src_buf_num, inst->processed_buf_num, >> inst->error_buf_num); >> + >> + if (inst->state == VPU_INST_STATE_NONE) >> + goto exit; >> + >> + wave6_vpu_pause(inst->dev->dev, 0); >> + >> + if (V4L2_TYPE_IS_OUTPUT(q->type)) { >> + wave6_vpu_reset_performance(inst); >> + inst->queued_src_buf_num = 0; >> + inst->processed_buf_num = 0; >> + inst->error_buf_num = 0; >> + inst->state_in_seek = inst->state; >> + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, >> false); >> + wave6_vpu_set_instance_state(inst, >> VPU_INST_STATE_SEEK); >> + inst->sequence = 0; >> + } else { >> + if (v4l2_m2m_has_stopped(m2m_ctx)) >> + v4l2_m2m_clear_state(m2m_ctx); >> + >> + inst->eos = false; >> + inst->queued_dst_buf_num = 0; >> + inst->sequence = 0; >> + wave6_vpu_dec_flush_instance(inst); >> + } >> + >> + wave6_vpu_pause(inst->dev->dev, 1); >> + >> +exit: >> + wave6_vpu_return_buffers(inst, q->type, >> VB2_BUF_STATE_ERROR); >> +} >> + >> +static int wave6_vpu_dec_buf_init(struct vb2_buffer *vb) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); >> + struct dec_initial_info initial_info = {0}; >> + int i; >> + >> + if (V4L2_TYPE_IS_OUTPUT(vb->type)) >> + return 0; >> + >> + wave6_vpu_dec_give_command(inst, DEC_GET_SEQ_INFO, >> &initial_info); >> + if (initial_info.chroma_format_idc != YUV400) >> + return 0; >> + >> + for (i = 0; i < inst->dst_fmt.num_planes; i++) { >> + void *vaddr = vb2_plane_vaddr(vb, i); >> + >> + if (vaddr) >> + memset(vaddr, 0x80, vb2_plane_size(vb, i)); >> + } >> + >> + return 0; >> +} >> + >> +static const struct vb2_ops wave6_vpu_dec_vb2_ops = { >> + .queue_setup = wave6_vpu_dec_queue_setup, >> + .wait_prepare = vb2_ops_wait_prepare, >> + .wait_finish = vb2_ops_wait_finish, >> + .buf_queue = wave6_vpu_dec_buf_queue, >> + .start_streaming = wave6_vpu_dec_start_streaming, >> + .stop_streaming = wave6_vpu_dec_stop_streaming, >> + .buf_init = wave6_vpu_dec_buf_init, >> +}; >> + >> +static void wave6_set_default_format(struct v4l2_pix_format_mplane >> *src_fmt, >> + struct v4l2_pix_format_mplane >> *dst_fmt) >> +{ >> + const struct vpu_format *vpu_fmt; >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_CODEC); >> + if (vpu_fmt) { >> + src_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + src_fmt->num_planes = vpu_fmt->num_planes; >> + wave6_update_pix_fmt(src_fmt, >> + W6_DEF_DEC_PIC_WIDTH, >> W6_DEF_DEC_PIC_HEIGHT); >> + } >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_RAW); >> + if (vpu_fmt) { >> + dst_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + dst_fmt->num_planes = vpu_fmt->num_planes; >> + wave6_update_pix_fmt_cap(dst_fmt, >> + W6_DEF_DEC_PIC_WIDTH, >> W6_DEF_DEC_PIC_HEIGHT, >> + true); >> + } >> +} >> + >> +static int wave6_vpu_dec_queue_init(void *priv, struct vb2_queue >> *src_vq, struct vb2_queue *dst_vq) >> +{ >> + struct vpu_instance *inst = priv; >> + int ret; >> + >> + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; >> + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; >> + src_vq->mem_ops = &vb2_dma_contig_memops; >> + src_vq->ops = &wave6_vpu_dec_vb2_ops; >> + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; >> + src_vq->buf_struct_size = sizeof(struct vpu_buffer); >> + src_vq->min_queued_buffers = 1; >> + src_vq->drv_priv = inst; >> + src_vq->lock = &inst->dev->dev_lock; >> + src_vq->dev = inst->dev->v4l2_dev.dev; >> + ret = vb2_queue_init(src_vq); >> + if (ret) >> + return ret; >> + >> + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; >> + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; >> + dst_vq->mem_ops = &vb2_dma_contig_memops; >> + dst_vq->ops = &wave6_vpu_dec_vb2_ops; >> + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; >> + dst_vq->buf_struct_size = sizeof(struct vpu_buffer); >> + dst_vq->min_queued_buffers = 1; >> + dst_vq->drv_priv = inst; >> + dst_vq->lock = &inst->dev->dev_lock; >> + dst_vq->dev = inst->dev->v4l2_dev.dev; >> + ret = vb2_queue_init(dst_vq); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> + >> +static const struct vpu_instance_ops wave6_vpu_dec_inst_ops = { >> + .start_process = wave6_vpu_dec_start_decode, >> + .finish_process = wave6_vpu_dec_finish_decode, >> +}; >> + >> +static int wave6_vpu_open_dec(struct file *filp) >> +{ >> + struct video_device *vdev = video_devdata(filp); >> + struct vpu_device *dev = video_drvdata(filp); >> + struct vpu_instance *inst = NULL; >> + int ret; >> + >> + inst = kzalloc(sizeof(*inst), GFP_KERNEL); >> + if (!inst) >> + return -ENOMEM; >> + >> + inst->dev = dev; >> + inst->type = VPU_INST_TYPE_DEC; >> + inst->ops = &wave6_vpu_dec_inst_ops; >> + >> + v4l2_fh_init(&inst->v4l2_fh, vdev); >> + filp->private_data = &inst->v4l2_fh; >> + v4l2_fh_add(&inst->v4l2_fh); >> + >> + inst->v4l2_fh.m2m_ctx = >> + v4l2_m2m_ctx_init(dev->m2m_dev, inst, >> wave6_vpu_dec_queue_init); >> + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { >> + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); >> + goto free_inst; >> + } >> + >> + v4l2_ctrl_handler_init(&inst->v4l2_ctrl_hdl, 10); >> + v4l2_ctrl_new_custom(&inst->v4l2_ctrl_hdl, >> &wave6_vpu_thumbnail_mode, NULL); >> + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, >> &wave6_vpu_dec_ctrl_ops, >> + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, >> 1, 1); >> + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, >> &wave6_vpu_dec_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY, >> + 0, 0, 1, 0); >> + v4l2_ctrl_new_std(&inst->v4l2_ctrl_hdl, >> &wave6_vpu_dec_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE, >> + 0, 1, 1, 0); >> + v4l2_ctrl_new_std_menu(&inst->v4l2_ctrl_hdl, >> &wave6_vpu_dec_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, >> + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0, >> + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); >> + v4l2_ctrl_new_std_menu(&inst->v4l2_ctrl_hdl, >> &wave6_vpu_dec_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_PROFILE, >> + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, 0, >> + >> V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); >> + >> + if (inst->v4l2_ctrl_hdl.error) { >> + ret = -ENODEV; >> + goto err_m2m_release; >> + } >> + >> + inst->v4l2_fh.ctrl_handler = &inst->v4l2_ctrl_hdl; >> + v4l2_ctrl_handler_setup(&inst->v4l2_ctrl_hdl); >> + >> + wave6_set_default_format(&inst->src_fmt, &inst->dst_fmt); >> + inst->colorspace = V4L2_COLORSPACE_DEFAULT; >> + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; >> + inst->quantization = V4L2_QUANTIZATION_DEFAULT; >> + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; >> + >> + return 0; >> + >> +err_m2m_release: >> + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); >> +free_inst: >> + kfree(inst); >> + return ret; >> +} >> + >> +static int wave6_vpu_dec_release(struct file *filp) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(filp- >> >private_data); >> + >> + dprintk(inst->dev->dev, "[%d] release\n", inst->id); >> + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); >> + >> + mutex_lock(&inst->dev->dev_lock); >> + if (inst->state != VPU_INST_STATE_NONE) { >> + wave6_vpu_pause(inst->dev->dev, 0); >> + wave6_vpu_dec_destroy_instance(inst); >> + wave6_vpu_pause(inst->dev->dev, 1); >> + } >> + mutex_unlock(&inst->dev->dev_lock); >> + >> + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl); >> + v4l2_fh_del(&inst->v4l2_fh); >> + v4l2_fh_exit(&inst->v4l2_fh); >> + kfree(inst); >> + >> + return 0; >> +} >> + >> +static const struct v4l2_file_operations wave6_vpu_dec_fops = { >> + .owner = THIS_MODULE, >> + .open = wave6_vpu_open_dec, >> + .release = wave6_vpu_dec_release, >> + .unlocked_ioctl = video_ioctl2, >> + .poll = v4l2_m2m_fop_poll, >> + .mmap = v4l2_m2m_fop_mmap, >> +}; >> + >> +int wave6_vpu_dec_register_device(struct vpu_device *dev) >> +{ >> + struct video_device *vdev_dec; >> + int ret; >> + >> + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, >> sizeof(*vdev_dec), GFP_KERNEL); >> + if (!vdev_dec) >> + return -ENOMEM; >> + >> + dev->video_dev_dec = vdev_dec; >> + >> + strscpy(vdev_dec->name, VPU_DEC_DEV_NAME, sizeof(vdev_dec- >> >name)); >> + vdev_dec->fops = &wave6_vpu_dec_fops; >> + vdev_dec->ioctl_ops = &wave6_vpu_dec_ioctl_ops; >> + vdev_dec->release = video_device_release_empty; >> + vdev_dec->v4l2_dev = &dev->v4l2_dev; >> + vdev_dec->vfl_dir = VFL_DIR_M2M; >> + vdev_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | >> V4L2_CAP_STREAMING; >> + vdev_dec->lock = &dev->dev_lock; >> + video_set_drvdata(vdev_dec, dev); >> + >> + ret = video_register_device(vdev_dec, VFL_TYPE_VIDEO, -1); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> + >> +void wave6_vpu_dec_unregister_device(struct vpu_device *dev) >> +{ >> + video_unregister_device(dev->video_dev_dec); >> +} >> diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c >> b/drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c >> new file mode 100644 >> index 000000000000..36417a7fef99 >> --- /dev/null >> +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-enc.c >> @@ -0,0 +1,2698 @@ >> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) >> +/* >> + * Wave6 series multi-standard codec IP - v4l2 stateful encoder >> interface >> + * >> + * Copyright (C) 2025 CHIPS&MEDIA INC >> + */ >> + >> +#include <linux/pm_runtime.h> >> +#include "wave6-vpu.h" >> +#include "wave6-vpu-dbg.h" >> +#include "wave6-trace.h" >> + >> +#define VPU_ENC_DEV_NAME "C&M Wave6 VPU encoder" >> +#define VPU_ENC_DRV_NAME "wave6-enc" >> + >> +static const struct vpu_format wave6_vpu_enc_fmt_list[2][23] = { >> + [VPU_FMT_TYPE_CODEC] = { >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_HEVC, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_H264, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + }, >> + [VPU_FMT_TYPE_RAW] = { >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUYV, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV24, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV24, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV42, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 3, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV12M, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 2, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV21M, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 2, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 3, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 2, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 2, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_RGB24, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_P010, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_ARGB32, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_XRGB32, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_RGBA32, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_RGBX32, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + { >> + .v4l2_pix_fmt = V4L2_PIX_FMT_ARGB2101010, >> + .max_width = W6_MAX_ENC_PIC_WIDTH, >> + .min_width = W6_MIN_ENC_PIC_WIDTH, >> + .max_height = W6_MAX_ENC_PIC_HEIGHT, >> + .min_height = W6_MIN_ENC_PIC_HEIGHT, >> + .num_planes = 1, >> + }, >> + } >> +}; >> + >> +static const struct vpu_format *wave6_find_vpu_fmt(unsigned int >> v4l2_pix_fmt, >> + enum vpu_fmt_type >> type) >> +{ >> + unsigned int index; >> + >> + for (index = 0; index < >> ARRAY_SIZE(wave6_vpu_enc_fmt_list[type]); index++) { >> + if (wave6_vpu_enc_fmt_list[type][index].v4l2_pix_fmt >> == v4l2_pix_fmt) >> + return &wave6_vpu_enc_fmt_list[type][index]; >> + } >> + >> + return NULL; >> +} >> + >> +static const struct vpu_format *wave6_find_vpu_fmt_by_idx(unsigned >> int idx, >> + enum >> vpu_fmt_type type) >> +{ >> + if (idx >= ARRAY_SIZE(wave6_vpu_enc_fmt_list[type])) >> + return NULL; >> + >> + if (!wave6_vpu_enc_fmt_list[type][idx].v4l2_pix_fmt) >> + return NULL; >> + >> + return &wave6_vpu_enc_fmt_list[type][idx]; >> +} >> + >> +static u32 wave6_cpb_size_msec(u32 cpb_size_kb, u32 bitrate) >> +{ >> + u64 cpb_size_bit; >> + u64 cpb_size_msec; >> + >> + cpb_size_bit = (u64)cpb_size_kb * 1000 * BITS_PER_BYTE; >> + cpb_size_msec = (cpb_size_bit * 1000) / bitrate; >> + >> + if (cpb_size_msec < 10 || cpb_size_msec > 100000) >> + cpb_size_msec = 10000; >> + >> + return cpb_size_msec; >> +} >> + >> +static void wave6_vpu_enc_release_fb(struct vpu_instance *inst) >> +{ >> + int i; >> + >> + for (i = 0; i < WAVE6_MAX_FBS; i++) { >> + wave6_free_dma(&inst->frame_vbuf[i]); >> + memset(&inst->frame_buf[i], 0, sizeof(struct >> frame_buffer)); >> + wave6_free_dma(&inst- >> >aux_vbuf[AUX_BUF_FBC_Y_TBL][i]); >> + wave6_free_dma(&inst- >> >aux_vbuf[AUX_BUF_FBC_C_TBL][i]); >> + wave6_free_dma(&inst->aux_vbuf[AUX_BUF_MV_COL][i]); >> + wave6_free_dma(&inst- >> >aux_vbuf[AUX_BUF_SUB_SAMPLE][i]); >> + } >> +} >> + >> +static void wave6_vpu_enc_destroy_instance(struct vpu_instance >> *inst) >> +{ >> + u32 fail_res; >> + int ret; >> + >> + dprintk(inst->dev->dev, "[%d] destroy instance\n", inst- >> >id); >> + wave6_vpu_remove_dbgfs_file(inst); >> + >> + ret = wave6_vpu_enc_close(inst, &fail_res); >> + if (ret) { >> + dev_err(inst->dev->dev, "failed destroy instance: %d >> (%d)\n", >> + ret, fail_res); >> + } >> + >> + wave6_vpu_enc_release_fb(inst); >> + wave6_free_dma(&inst->ar_vbuf); >> + >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_NONE); >> + >> + if (!pm_runtime_suspended(inst->dev->dev)) >> + pm_runtime_put_sync(inst->dev->dev); >> +} >> + >> +static struct vb2_v4l2_buffer *wave6_get_valid_src_buf(struct >> vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *vb2_v4l2_buf; >> + struct v4l2_m2m_buffer *v4l2_m2m_buf; >> + struct vpu_buffer *vpu_buf = NULL; >> + >> + v4l2_m2m_for_each_src_buf(inst->v4l2_fh.m2m_ctx, >> v4l2_m2m_buf) { >> + vb2_v4l2_buf = &v4l2_m2m_buf->vb; >> + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); >> + >> + if (!vpu_buf->consumed) { >> + dev_dbg(inst->dev->dev, "no consumed src idx >> : %d\n", >> + vb2_v4l2_buf->vb2_buf.index); >> + return vb2_v4l2_buf; >> + } >> + } >> + >> + return NULL; >> +} >> + >> +static struct vb2_v4l2_buffer *wave6_get_valid_dst_buf(struct >> vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *vb2_v4l2_buf; >> + struct v4l2_m2m_buffer *v4l2_m2m_buf; >> + struct vpu_buffer *vpu_buf; >> + >> + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, >> v4l2_m2m_buf) { >> + vb2_v4l2_buf = &v4l2_m2m_buf->vb; >> + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); >> + >> + if (!vpu_buf->consumed) { >> + dev_dbg(inst->dev->dev, "no consumed dst idx >> : %d\n", >> + vb2_v4l2_buf->vb2_buf.index); >> + return vb2_v4l2_buf; >> + } >> + } >> + >> + return NULL; >> +} >> + >> +static void wave6_set_csc(struct vpu_instance *inst, struct >> enc_param *pic_param) >> +{ >> + bool is_10bit = false; >> + >> + if (!(inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGB24) && >> + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB32) && >> + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_XRGB32) && >> + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32) && >> + !(inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32) && >> + !(inst->src_fmt.pixelformat == >> V4L2_PIX_FMT_ARGB2101010)) >> + return; >> + >> + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB2101010) >> + is_10bit = true; >> + >> + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32) >> + pic_param->csc.format_order = 8; >> + >> + if (inst->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT || >> + inst->ycbcr_enc == V4L2_YCBCR_ENC_601) { >> + if (inst->quantization == >> V4L2_QUANTIZATION_FULL_RANGE) { >> + /* >> + * Y 0.299(R) 0.587(G) 0.114(B) >> + * Cb -0.16874(R) -0.33126(G) 0.5(B) >> + * Cr 0.5(R) -0.41869(G) -0.08131(B) >> + */ >> + pic_param->csc.coef_ry = 0x099; >> + pic_param->csc.coef_gy = 0x12d; >> + pic_param->csc.coef_by = 0x03a; >> + pic_param->csc.coef_rcb = 0xffffffaa; >> + pic_param->csc.coef_gcb = 0xffffff56; >> + pic_param->csc.coef_bcb = 0x100; >> + pic_param->csc.coef_rcr = 0x100; >> + pic_param->csc.coef_gcr = 0xffffff2a; >> + pic_param->csc.coef_bcr = 0xffffffd6; >> + pic_param->csc.offset_y = 0x0; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } else { >> + /* >> + * Y 0.258(R) 0.504(G) 0.098(B) >> + * Cb -0.1484(R) -0.2891(G) 0.4375(B) >> + * Cr 0.4375(R) -0.3672(G) -0.0703(B) >> + */ >> + pic_param->csc.coef_ry = 0x084; >> + pic_param->csc.coef_gy = 0x102; >> + pic_param->csc.coef_by = 0x032; >> + pic_param->csc.coef_rcb = 0xffffffb4; >> + pic_param->csc.coef_gcb = 0xffffff6c; >> + pic_param->csc.coef_bcb = 0x0e0; >> + pic_param->csc.coef_rcr = 0x0e0; >> + pic_param->csc.coef_gcr = 0xffffff44; >> + pic_param->csc.coef_bcr = 0xffffffdc; >> + pic_param->csc.offset_y = (is_10bit) ? 0x40 >> : 0x10; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } >> + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_709) { >> + if (inst->quantization == >> V4L2_QUANTIZATION_FULL_RANGE) { >> + /* >> + * Y 0.2126(R) 0.7152(G) 0.0722(B) >> + * Cb -0.11457(R) -0.38543(G) 0.5(B) >> + * Cr 0.5(R) -0.45415(G) -0.04585(B) >> + */ >> + pic_param->csc.coef_ry = 0x06d; >> + pic_param->csc.coef_gy = 0x16e; >> + pic_param->csc.coef_by = 0x025; >> + pic_param->csc.coef_rcb = 0xffffffc5; >> + pic_param->csc.coef_gcb = 0xffffff3b; >> + pic_param->csc.coef_bcb = 0x100; >> + pic_param->csc.coef_rcr = 0x100; >> + pic_param->csc.coef_gcr = 0xffffff17; >> + pic_param->csc.coef_bcr = 0xffffffe9; >> + pic_param->csc.offset_y = 0x0; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } else { >> + pic_param->csc.coef_ry = 0x05e; >> + pic_param->csc.coef_gy = 0x13b; >> + pic_param->csc.coef_by = 0x020; >> + pic_param->csc.coef_rcb = 0xffffffcc; >> + pic_param->csc.coef_gcb = 0xffffff53; >> + pic_param->csc.coef_bcb = 0x0e1; >> + pic_param->csc.coef_rcr = 0x0e1; >> + pic_param->csc.coef_gcr = 0xffffff34; >> + pic_param->csc.coef_bcr = 0xffffffeb; >> + pic_param->csc.offset_y = (is_10bit) ? 0x40 >> : 0x10; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } >> + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_BT2020) { >> + if (inst->quantization == >> V4L2_QUANTIZATION_FULL_RANGE) { >> + /* >> + * Y 0.2627(R) 0.678(G) 0.0593(B) >> + * Cb -0.13963(R) -0.36037(G) 0.5(B) >> + * Cr 0.5(R) -0.45979(G) -0.04021(B) >> + */ >> + pic_param->csc.coef_ry = 0x087; >> + pic_param->csc.coef_gy = 0x15b; >> + pic_param->csc.coef_by = 0x01e; >> + pic_param->csc.coef_rcb = 0xffffffb9; >> + pic_param->csc.coef_gcb = 0xffffff47; >> + pic_param->csc.coef_bcb = 0x100; >> + pic_param->csc.coef_rcr = 0x100; >> + pic_param->csc.coef_gcr = 0xffffff15; >> + pic_param->csc.coef_bcr = 0xffffffeb; >> + pic_param->csc.offset_y = 0x0; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } else { >> + pic_param->csc.coef_ry = 0x074; >> + pic_param->csc.coef_gy = 0x12a; >> + pic_param->csc.coef_by = 0x01a; >> + pic_param->csc.coef_rcb = 0xffffffc1; >> + pic_param->csc.coef_gcb = 0xffffff5e; >> + pic_param->csc.coef_bcb = 0x0e1; >> + pic_param->csc.coef_rcr = 0x0e1; >> + pic_param->csc.coef_gcr = 0xffffff31; >> + pic_param->csc.coef_bcr = 0xffffffee; >> + pic_param->csc.offset_y = (is_10bit) ? 0x40 >> : 0x10; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } >> + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_SMPTE240M) { >> + if (inst->quantization == >> V4L2_QUANTIZATION_FULL_RANGE) { >> + /* >> + * Y 0.2122(R) 0.7013(G) 0.0865(B) >> + * Cb -0.1161(R) -0.3839(G) 0.5(B) >> + * Cr 0.5(R) -0.4451(G) -0.0549(B) >> + */ >> + pic_param->csc.coef_ry = 0x06d; >> + pic_param->csc.coef_gy = 0x167; >> + pic_param->csc.coef_by = 0x02c; >> + pic_param->csc.coef_rcb = 0xffffffc5; >> + pic_param->csc.coef_gcb = 0xffffff3b; >> + pic_param->csc.coef_bcb = 0x100; >> + pic_param->csc.coef_rcr = 0x100; >> + pic_param->csc.coef_gcr = 0xffffff1c; >> + pic_param->csc.coef_bcr = 0xffffffe4; >> + pic_param->csc.offset_y = 0x0; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } else { >> + pic_param->csc.coef_ry = 0x05d; >> + pic_param->csc.coef_gy = 0x134; >> + pic_param->csc.coef_by = 0x026; >> + pic_param->csc.coef_rcb = 0xffffffcc; >> + pic_param->csc.coef_gcb = 0xffffff53; >> + pic_param->csc.coef_bcb = 0x0e1; >> + pic_param->csc.coef_rcr = 0x0e1; >> + pic_param->csc.coef_gcr = 0xffffff38; >> + pic_param->csc.coef_bcr = 0xffffffe7; >> + pic_param->csc.offset_y = (is_10bit) ? 0x40 >> : 0x10; >> + pic_param->csc.offset_cb = (is_10bit) ? >> 0x200 : 0x80; >> + pic_param->csc.offset_cr = (is_10bit) ? >> 0x200 : 0x80; >> + } >> + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_XV601) { >> + if (inst->quantization == >> V4L2_QUANTIZATION_LIM_RANGE) { >> + /* >> + * Y 0.2558(R) 0.5021(G) 0.0975(B) >> + * Cb -0.1476(R) -0.2899(G) 0.4375(B) >> + * Cr 0.4375(R) -0.3664(G) -0.0711(B) >> + */ >> + pic_param->csc.coef_ry = 0x083; >> + pic_param->csc.coef_gy = 0x101; >> + pic_param->csc.coef_by = 0x032; >> + pic_param->csc.coef_rcb = 0xffffffb4; >> + pic_param->csc.coef_gcb = 0xffffff6c; >> + pic_param->csc.coef_bcb = 0x0e0; >> + pic_param->csc.coef_rcr = 0x0e0; >> + pic_param->csc.coef_gcr = 0xffffff44; >> + pic_param->csc.coef_bcr = 0xffffffdc; >> + pic_param->csc.offset_y = (is_10bit) ? 0x40 >> : 0x10; >> + pic_param->csc.offset_cb = 0x0; >> + pic_param->csc.offset_cr = 0x0; >> + } >> + } else if (inst->ycbcr_enc == V4L2_YCBCR_ENC_XV709) { >> + if (inst->quantization == >> V4L2_QUANTIZATION_LIM_RANGE) { >> + /* >> + * Y 0.1819(R) 0.6118(G) 0.0618(B) >> + * Cb -0.1003(R) -0.3372(G) 0.4375(B) >> + * Cr 0.4375(R) -0.3974(G) -0.0401(B) >> + */ >> + pic_param->csc.coef_ry = 0x05d; >> + pic_param->csc.coef_gy = 0x139; >> + pic_param->csc.coef_by = 0x020; >> + pic_param->csc.coef_rcb = 0xffffffcd; >> + pic_param->csc.coef_gcb = 0xffffff53; >> + pic_param->csc.coef_bcb = 0x0e0; >> + pic_param->csc.coef_rcr = 0x0e0; >> + pic_param->csc.coef_gcr = 0xffffff35; >> + pic_param->csc.coef_bcr = 0xffffffeb; >> + pic_param->csc.offset_y = (is_10bit) ? 0x40 >> : 0x10; >> + pic_param->csc.offset_cb = 0x0; >> + pic_param->csc.offset_cr = 0x0; >> + } >> + } >> +} >> + >> +static void wave6_update_crop_info(struct vpu_instance *inst, >> + u32 left, u32 top, u32 width, u32 >> height) >> +{ >> + u32 enc_pic_width, enc_pic_height; >> + >> + inst->crop.left = left; >> + inst->crop.top = top; >> + inst->crop.width = width; >> + inst->crop.height = height; >> + >> + inst->codec_rect.left = round_down(left, >> W6_ENC_CROP_X_POS_STEP); >> + inst->codec_rect.top = round_down(top, >> W6_ENC_CROP_Y_POS_STEP); >> + >> + enc_pic_width = width + left - inst->codec_rect.left; >> + inst->codec_rect.width = round_up(enc_pic_width, >> W6_ENC_PIC_SIZE_STEP); >> + >> + enc_pic_height = height + top - inst->codec_rect.top; >> + inst->codec_rect.height = round_up(enc_pic_height, >> W6_ENC_PIC_SIZE_STEP); >> +} >> + >> +static int wave6_allocate_aux_buffer(struct vpu_instance *inst, >> + enum aux_buffer_type type, >> + int num) >> +{ >> + struct aux_buffer buf[WAVE6_MAX_FBS]; >> + struct aux_buffer_info buf_info; >> + struct enc_aux_buffer_size_info size_info; >> + unsigned int size; >> + int i, ret; >> + >> + memset(buf, 0, sizeof(buf)); >> + >> + size_info.width = inst->codec_rect.width; >> + size_info.height = inst->codec_rect.height; >> + size_info.type = type; >> + size_info.mirror_direction = inst- >> >enc_ctrls.mirror_direction; >> + size_info.rotation_angle = inst->enc_ctrls.rot_angle; >> + >> + ret = wave6_vpu_enc_get_aux_buffer_size(inst, size_info, >> &size); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "%s: Get size fail (type >> %d)\n", __func__, type); >> + return ret; >> + } >> + >> + for (i = 0; i < num; i++) { >> + inst->aux_vbuf[type][i].size = size; >> + ret = wave6_alloc_dma(inst->dev->dev, &inst- >> >aux_vbuf[type][i]); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "%s: Alloc fail >> (type %d)\n", __func__, type); >> + return ret; >> + } >> + >> + buf[i].index = i; >> + buf[i].addr = inst->aux_vbuf[type][i].daddr; >> + buf[i].size = inst->aux_vbuf[type][i].size; >> + } >> + >> + buf_info.type = type; >> + buf_info.num = num; >> + buf_info.buf_array = buf; >> + >> + ret = wave6_vpu_enc_register_aux_buffer(inst, buf_info); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "%s: Register fail (type >> %d)\n", __func__, type); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static void wave6_update_frame_buf_addr(struct vpu_instance *inst, >> + struct frame_buffer >> *frame_buf) >> +{ >> + const struct v4l2_format_info *fmt_info; >> + u32 stride = inst->src_fmt.plane_fmt[0].bytesperline; >> + u32 offset; >> + >> + fmt_info = v4l2_format_info(inst->src_fmt.pixelformat); >> + if (!fmt_info) >> + return; >> + >> + offset = inst->codec_rect.top * stride + inst- >> >codec_rect.left * fmt_info->bpp[0]; >> + frame_buf->buf_y += offset; >> + >> + stride = DIV_ROUND_UP(stride, fmt_info->bpp[0]) * fmt_info- >> >bpp[1]; >> + offset = inst->codec_rect.top * stride / fmt_info->vdiv / >> fmt_info->hdiv >> + + inst->codec_rect.left * fmt_info->bpp[1] / >> fmt_info->hdiv; >> + frame_buf->buf_cb += offset; >> + frame_buf->buf_cr += offset; >> +} >> + >> +static int wave6_update_seq_param(struct vpu_instance *inst) >> +{ >> + struct enc_initial_info initial_info; >> + bool changed = false; >> + int ret; >> + >> + ret = wave6_vpu_enc_issue_seq_change(inst, &changed); >> + if (ret) { >> + dev_err(inst->dev->dev, "seq change fail %d\n", >> ret); >> + return ret; >> + } >> + >> + if (!changed) >> + return 0; >> + >> + if (wave6_vpu_wait_interrupt(inst, W6_VPU_TIMEOUT) < 0) { >> + dev_err(inst->dev->dev, "seq change timeout\n"); >> + return ret; >> + } >> + >> + wave6_vpu_enc_complete_seq_init(inst, &initial_info); >> + if (ret) { >> + dev_err(inst->dev->dev, "seq change error\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_start_encode(struct vpu_instance *inst) >> +{ >> + int ret = -EINVAL; >> + struct vb2_v4l2_buffer *src_buf = NULL; >> + struct vb2_v4l2_buffer *dst_buf = NULL; >> + struct vpu_buffer *src_vbuf = NULL; >> + struct vpu_buffer *dst_vbuf = NULL; >> + struct frame_buffer frame_buf; >> + struct enc_param pic_param; >> + u32 stride = inst->src_fmt.plane_fmt[0].bytesperline; >> + u32 luma_size = (stride * inst->src_fmt.height); >> + u32 chroma_size; >> + u32 fail_res; >> + >> + memset(&pic_param, 0, sizeof(struct enc_param)); >> + memset(&frame_buf, 0, sizeof(struct frame_buffer)); >> + >> + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420M) >> + chroma_size = ((stride / 2) * (inst->src_fmt.height >> / 2)); >> + else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422P >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422M) >> + chroma_size = ((stride) * (inst->src_fmt.height / >> 2)); >> + else >> + chroma_size = 0; >> + >> + ret = wave6_update_seq_param(inst); >> + if (ret) >> + goto exit; >> + >> + src_buf = wave6_get_valid_src_buf(inst); >> + dst_buf = wave6_get_valid_dst_buf(inst); >> + >> + if (!dst_buf) { >> + dev_dbg(inst->dev->dev, "no valid dst buf\n"); >> + goto exit; >> + } >> + >> + dst_vbuf = wave6_to_vpu_buf(dst_buf); >> + pic_param.pic_stream_buffer_addr = >> wave6_get_dma_addr(dst_buf, 0); >> + pic_param.pic_stream_buffer_size = vb2_plane_size(&dst_buf- >> >vb2_buf, 0); >> + if (!src_buf) { >> + dev_dbg(inst->dev->dev, "no valid src buf\n"); >> + if (inst->state == VPU_INST_STATE_STOP) >> + pic_param.src_end = true; >> + else >> + goto exit; >> + } else { >> + src_vbuf = wave6_to_vpu_buf(src_buf); >> + if (inst->src_fmt.num_planes == 1) { >> + frame_buf.buf_y = >> wave6_get_dma_addr(src_buf, 0); >> + frame_buf.buf_cb = frame_buf.buf_y + >> luma_size; >> + frame_buf.buf_cr = frame_buf.buf_cb + >> chroma_size; >> + } else if (inst->src_fmt.num_planes == 2) { >> + frame_buf.buf_y = >> wave6_get_dma_addr(src_buf, 0); >> + frame_buf.buf_cb = >> wave6_get_dma_addr(src_buf, 1); >> + frame_buf.buf_cr = frame_buf.buf_cb + >> chroma_size; >> + } else if (inst->src_fmt.num_planes == 3) { >> + frame_buf.buf_y = >> wave6_get_dma_addr(src_buf, 0); >> + frame_buf.buf_cb = >> wave6_get_dma_addr(src_buf, 1); >> + frame_buf.buf_cr = >> wave6_get_dma_addr(src_buf, 2); >> + } >> + wave6_update_frame_buf_addr(inst, &frame_buf); >> + frame_buf.stride = stride; >> + pic_param.src_idx = src_buf->vb2_buf.index; >> + if (src_vbuf->force_key_frame || inst- >> >error_recovery) { >> + pic_param.force_pic_type_enable = true; >> + pic_param.force_pic_type = >> ENC_FORCE_PIC_TYPE_IDR; >> + inst->error_recovery = false; >> + } >> + if (src_vbuf->force_frame_qp) { >> + pic_param.force_pic_qp_enable = true; >> + pic_param.force_pic_qp_i = src_vbuf- >> >force_i_frame_qp; >> + pic_param.force_pic_qp_p = src_vbuf- >> >force_p_frame_qp; >> + pic_param.force_pic_qp_b = src_vbuf- >> >force_b_frame_qp; >> + } >> + src_vbuf->ts_start = ktime_get_raw(); >> + } >> + >> + pic_param.source_frame = &frame_buf; >> + wave6_set_csc(inst, &pic_param); >> + >> + if (src_vbuf) >> + src_vbuf->consumed = true; >> + if (dst_vbuf) { >> + dst_vbuf->consumed = true; >> + dst_vbuf->used = true; >> + } >> + >> + trace_enc_pic(inst, &pic_param); >> + >> + ret = wave6_vpu_enc_start_one_frame(inst, &pic_param, >> &fail_res); >> + if (ret) { >> + dev_err(inst->dev->dev, "[%d] %s: fail %d\n", inst- >> >id, __func__, ret); >> + wave6_vpu_set_instance_state(inst, >> VPU_INST_STATE_STOP); >> + >> + dst_buf = v4l2_m2m_dst_buf_remove(inst- >> >v4l2_fh.m2m_ctx); >> + if (dst_buf) { >> + dst_buf->sequence = inst->sequence; >> + v4l2_m2m_buf_done(dst_buf, >> VB2_BUF_STATE_ERROR); >> + } >> + >> + src_buf = v4l2_m2m_src_buf_remove(inst- >> >v4l2_fh.m2m_ctx); >> + if (src_buf) { >> + v4l2_m2m_buf_done(src_buf, >> VB2_BUF_STATE_ERROR); >> + inst->sequence++; >> + inst->processed_buf_num++; >> + inst->error_buf_num++; >> + } >> + } else { >> + dev_dbg(inst->dev->dev, "%s: success\n", __func__); >> + } >> + >> +exit: >> + return ret; >> +} >> + >> +static void wave6_handle_encoded_frame(struct vpu_instance *inst, >> + struct enc_output_info *info) >> +{ >> + struct vb2_v4l2_buffer *src_buf; >> + struct vb2_v4l2_buffer *dst_buf; >> + struct vpu_buffer *vpu_buf; >> + struct vpu_buffer *dst_vpu_buf; >> + enum vb2_buffer_state state; >> + >> + state = info->encoding_success ? VB2_BUF_STATE_DONE : >> VB2_BUF_STATE_ERROR; >> + >> + src_buf = v4l2_m2m_src_buf_remove_by_idx(inst- >> >v4l2_fh.m2m_ctx, >> + info->enc_src_idx); >> + if (!src_buf) { >> + dev_err(inst->dev->dev, "[%d] encoder can't find src >> buffer\n", inst->id); >> + return; >> + } >> + >> + vpu_buf = wave6_to_vpu_buf(src_buf); >> + if (!vpu_buf || !vpu_buf->consumed) { >> + dev_err(inst->dev->dev, "[%d] src buffer is not >> consumed\n", inst->id); >> + return; >> + } >> + >> + dst_buf = wave6_get_dst_buf_by_addr(inst, info- >> >bitstream_buffer); >> + if (!dst_buf) { >> + dev_err(inst->dev->dev, "[%d] encoder can't find dst >> buffer\n", inst->id); >> + return; >> + } >> + >> + dst_vpu_buf = wave6_to_vpu_buf(dst_buf); >> + >> + dst_vpu_buf->average_qp = info->avg_ctu_qp; >> + dst_vpu_buf->ts_input = vpu_buf->ts_input; >> + dst_vpu_buf->ts_start = vpu_buf->ts_start; >> + dst_vpu_buf->ts_finish = ktime_get_raw(); >> + dst_vpu_buf->hw_time = wave6_vpu_cycle_to_ns(inst->dev, >> info->cycle.frame_cycle); >> + dst_vpu_buf->ts_output = ktime_get_raw(); >> + wave6_vpu_handle_performance(inst, dst_vpu_buf); >> + >> + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true); >> + v4l2_m2m_buf_done(src_buf, state); >> + >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, info- >> >bitstream_size); >> + dst_buf->sequence = inst->sequence++; >> + dst_buf->field = V4L2_FIELD_NONE; >> + if (info->pic_type == PIC_TYPE_I) >> + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; >> + else if (info->pic_type == PIC_TYPE_P) >> + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; >> + else if (info->pic_type == PIC_TYPE_B) >> + dst_buf->flags |= V4L2_BUF_FLAG_BFRAME; >> + >> + v4l2_m2m_dst_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, >> dst_buf); >> + if (state == VB2_BUF_STATE_ERROR) { >> + dprintk(inst->dev->dev, "[%d] error frame %d\n", >> inst->id, inst->sequence); >> + inst->error_recovery = true; >> + inst->error_buf_num++; >> + } >> + v4l2_m2m_buf_done(dst_buf, state); >> + inst->processed_buf_num++; >> +} >> + >> +static void wave6_handle_last_frame(struct vpu_instance *inst, >> + dma_addr_t addr) >> +{ >> + struct vb2_v4l2_buffer *dst_buf; >> + >> + dst_buf = wave6_get_dst_buf_by_addr(inst, addr); >> + if (!dst_buf) >> + return; >> + >> + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); >> + dst_buf->field = V4L2_FIELD_NONE; >> + dst_buf->flags |= V4L2_BUF_FLAG_LAST; >> + v4l2_m2m_dst_buf_remove_by_buf(inst->v4l2_fh.m2m_ctx, >> dst_buf); >> + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); >> + >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_PIC_RUN); >> + >> + dprintk(inst->dev->dev, "[%d] eos\n", inst->id); >> + inst->eos = true; >> + >> + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, false); >> +} >> + >> +static void wave6_vpu_enc_finish_encode(struct vpu_instance *inst, >> bool error) >> +{ >> + int ret; >> + struct enc_output_info info; >> + >> + if (error) { >> + vb2_queue_error(v4l2_m2m_get_src_vq(inst- >> >v4l2_fh.m2m_ctx)); >> + vb2_queue_error(v4l2_m2m_get_dst_vq(inst- >> >v4l2_fh.m2m_ctx)); >> + >> + wave6_vpu_set_instance_state(inst, >> VPU_INST_STATE_STOP); >> + inst->eos = true; >> + >> + goto finish_encode; >> + } >> + >> + ret = wave6_vpu_enc_get_output_info(inst, &info); >> + if (ret) { >> + dev_dbg(inst->dev->dev, "vpu_enc_get_output_info >> fail %d reason: %d | info : %d\n", >> + ret, info.error_reason, info.warn_info); >> + goto finish_encode; >> + } >> + >> + trace_enc_done(inst, &info); >> + >> + if (info.enc_src_idx >= 0 && info.recon_frame_index >= 0) >> + wave6_handle_encoded_frame(inst, &info); >> + else if (info.recon_frame_index == RECON_IDX_FLAG_ENC_END) >> + wave6_handle_last_frame(inst, >> info.bitstream_buffer); >> + >> +finish_encode: >> + wave6_vpu_finish_job(inst); >> +} >> + >> +static int wave6_vpu_enc_querycap(struct file *file, void *fh, >> struct v4l2_capability *cap) >> +{ >> + strscpy(cap->driver, VPU_ENC_DRV_NAME, sizeof(cap->driver)); >> + strscpy(cap->card, VPU_ENC_DRV_NAME, sizeof(cap->card)); >> + strscpy(cap->bus_info, "platform:" VPU_ENC_DRV_NAME, >> sizeof(cap->bus_info)); >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_enum_framesizes(struct file *f, void *fh, >> struct v4l2_frmsizeenum *fsize) >> +{ >> + const struct vpu_format *vpu_fmt; >> + >> + if (fsize->index) >> + return -EINVAL; >> + >> + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, >> VPU_FMT_TYPE_CODEC); >> + if (!vpu_fmt) { >> + vpu_fmt = wave6_find_vpu_fmt(fsize->pixel_format, >> VPU_FMT_TYPE_RAW); >> + if (!vpu_fmt) >> + return -EINVAL; >> + } >> + >> + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; >> + fsize->stepwise.min_width = vpu_fmt->min_width; >> + fsize->stepwise.max_width = vpu_fmt->max_width; >> + fsize->stepwise.step_width = W6_ENC_PIC_SIZE_STEP; >> + fsize->stepwise.min_height = vpu_fmt->min_height; >> + fsize->stepwise.max_height = vpu_fmt->max_height; >> + fsize->stepwise.step_height = W6_ENC_PIC_SIZE_STEP; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_enum_fmt_cap(struct file *file, void *fh, >> struct v4l2_fmtdesc *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + const struct vpu_format *vpu_fmt; >> + >> + dev_dbg(inst->dev->dev, "index : %d\n", f->index); >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, >> VPU_FMT_TYPE_CODEC); >> + if (!vpu_fmt) >> + return -EINVAL; >> + >> + f->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + f->flags = 0; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_try_fmt_cap(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + const struct vpu_format *vpu_fmt; >> + int width, height; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + if (!V4L2_TYPE_IS_CAPTURE(f->type)) >> + return -EINVAL; >> + >> + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, >> VPU_FMT_TYPE_CODEC); >> + if (!vpu_fmt) { >> + width = inst->dst_fmt.width; >> + height = inst->dst_fmt.height; >> + pix_mp->pixelformat = inst->dst_fmt.pixelformat; >> + pix_mp->num_planes = inst->dst_fmt.num_planes; >> + } else { >> + width = pix_mp->width; >> + height = pix_mp->height; >> + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + pix_mp->num_planes = vpu_fmt->num_planes; >> + } >> + >> + wave6_update_pix_fmt(pix_mp, width, height); >> + pix_mp->colorspace = inst->colorspace; >> + pix_mp->ycbcr_enc = inst->ycbcr_enc; >> + pix_mp->quantization = inst->quantization; >> + pix_mp->xfer_func = inst->xfer_func; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_s_fmt_cap(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i, ret; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + ret = wave6_vpu_enc_try_fmt_cap(file, fh, f); >> + if (ret) >> + return ret; >> + >> + inst->std = wave6_to_codec_std(inst->type, pix_mp- >> >pixelformat); >> + if (inst->std == STD_UNKNOWN) { >> + dev_err(inst->dev->dev, "unsupported pixelformat: >> %.4s\n", >> + (char *)&pix_mp->pixelformat); >> + return -EINVAL; >> + } >> + >> + inst->dst_fmt.width = pix_mp->width; >> + inst->dst_fmt.height = pix_mp->height; >> + inst->dst_fmt.pixelformat = pix_mp->pixelformat; >> + inst->dst_fmt.field = pix_mp->field; >> + inst->dst_fmt.flags = pix_mp->flags; >> + inst->dst_fmt.num_planes = pix_mp->num_planes; >> + for (i = 0; i < inst->dst_fmt.num_planes; i++) { >> + inst->dst_fmt.plane_fmt[i].bytesperline = pix_mp- >> >plane_fmt[i].bytesperline; >> + inst->dst_fmt.plane_fmt[i].sizeimage = pix_mp- >> >plane_fmt[i].sizeimage; >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_g_fmt_cap(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i; >> + >> + pix_mp->width = inst->dst_fmt.width; >> + pix_mp->height = inst->dst_fmt.height; >> + pix_mp->pixelformat = inst->dst_fmt.pixelformat; >> + pix_mp->field = inst->dst_fmt.field; >> + pix_mp->flags = inst->dst_fmt.flags; >> + pix_mp->num_planes = inst->dst_fmt.num_planes; >> + for (i = 0; i < pix_mp->num_planes; i++) { >> + pix_mp->plane_fmt[i].bytesperline = inst- >> >dst_fmt.plane_fmt[i].bytesperline; >> + pix_mp->plane_fmt[i].sizeimage = inst- >> >dst_fmt.plane_fmt[i].sizeimage; >> + } >> + >> + pix_mp->colorspace = inst->colorspace; >> + pix_mp->ycbcr_enc = inst->ycbcr_enc; >> + pix_mp->quantization = inst->quantization; >> + pix_mp->xfer_func = inst->xfer_func; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_enum_fmt_out(struct file *file, void *fh, >> struct v4l2_fmtdesc *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + const struct vpu_format *vpu_fmt; >> + >> + dev_dbg(inst->dev->dev, "%s: index %d\n", __func__, f- >> >index); >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(f->index, >> VPU_FMT_TYPE_RAW); >> + if (!vpu_fmt) >> + return -EINVAL; >> + >> + f->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + f->flags = 0; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_try_fmt_out(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + const struct vpu_format *vpu_fmt; >> + int width, height; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + if (!V4L2_TYPE_IS_OUTPUT(f->type)) >> + return -EINVAL; >> + >> + vpu_fmt = wave6_find_vpu_fmt(pix_mp->pixelformat, >> VPU_FMT_TYPE_RAW); >> + if (!vpu_fmt) { >> + width = inst->src_fmt.width; >> + height = inst->src_fmt.height; >> + pix_mp->pixelformat = inst->src_fmt.pixelformat; >> + pix_mp->num_planes = inst->src_fmt.num_planes; >> + } else { >> + width = clamp(pix_mp->width, >> + vpu_fmt->min_width, vpu_fmt- >> >max_width); >> + height = clamp(pix_mp->height, >> + vpu_fmt->min_height, vpu_fmt- >> >max_height); >> + >> + pix_mp->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + pix_mp->num_planes = vpu_fmt->num_planes; >> + } >> + >> + wave6_update_pix_fmt(pix_mp, width, height); >> + >> + if (pix_mp->ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM) >> + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_BT2020; >> + if (pix_mp->ycbcr_enc == V4L2_YCBCR_ENC_XV601 || >> + pix_mp->ycbcr_enc == V4L2_YCBCR_ENC_XV709) { >> + if (pix_mp->quantization == >> V4L2_QUANTIZATION_FULL_RANGE) >> + pix_mp->quantization = >> V4L2_QUANTIZATION_LIM_RANGE; >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_s_fmt_out(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i, ret; >> + >> + dev_dbg(inst->dev->dev, "%s: 4cc %d w %d h %d plane %d >> colorspace %d\n", >> + __func__, pix_mp->pixelformat, pix_mp->width, >> pix_mp->height, >> + pix_mp->num_planes, pix_mp->colorspace); >> + >> + ret = wave6_vpu_enc_try_fmt_out(file, fh, f); >> + if (ret) >> + return ret; >> + >> + inst->src_fmt.width = pix_mp->width; >> + inst->src_fmt.height = pix_mp->height; >> + inst->src_fmt.pixelformat = pix_mp->pixelformat; >> + inst->src_fmt.field = pix_mp->field; >> + inst->src_fmt.flags = pix_mp->flags; >> + inst->src_fmt.num_planes = pix_mp->num_planes; >> + for (i = 0; i < inst->src_fmt.num_planes; i++) { >> + inst->src_fmt.plane_fmt[i].bytesperline = pix_mp- >> >plane_fmt[i].bytesperline; >> + inst->src_fmt.plane_fmt[i].sizeimage = pix_mp- >> >plane_fmt[i].sizeimage; >> + } >> + >> + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV24 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16M || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGB24 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV24 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_P010 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB32 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_XRGB32 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB2101010) { >> + inst->cbcr_interleave = true; >> + inst->nv21 = false; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV42 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61M) >> { >> + inst->cbcr_interleave = true; >> + inst->nv21 = true; >> + } else { >> + inst->cbcr_interleave = false; >> + inst->nv21 = false; >> + } >> + >> + inst->colorspace = pix_mp->colorspace; >> + inst->ycbcr_enc = pix_mp->ycbcr_enc; >> + inst->quantization = pix_mp->quantization; >> + inst->xfer_func = pix_mp->xfer_func; >> + >> + wave6_update_pix_fmt(&inst->dst_fmt, pix_mp->width, pix_mp- >> >height); >> + wave6_update_crop_info(inst, 0, 0, pix_mp->width, pix_mp- >> >height); >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_g_fmt_out(struct file *file, void *fh, >> struct v4l2_format *f) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; >> + int i; >> + >> + dev_dbg(inst->dev->dev, "\n"); >> + >> + pix_mp->width = inst->src_fmt.width; >> + pix_mp->height = inst->src_fmt.height; >> + pix_mp->pixelformat = inst->src_fmt.pixelformat; >> + pix_mp->field = inst->src_fmt.field; >> + pix_mp->flags = inst->src_fmt.flags; >> + pix_mp->num_planes = inst->src_fmt.num_planes; >> + for (i = 0; i < pix_mp->num_planes; i++) { >> + pix_mp->plane_fmt[i].bytesperline = inst- >> >src_fmt.plane_fmt[i].bytesperline; >> + pix_mp->plane_fmt[i].sizeimage = inst- >> >src_fmt.plane_fmt[i].sizeimage; >> + } >> + >> + pix_mp->colorspace = inst->colorspace; >> + pix_mp->ycbcr_enc = inst->ycbcr_enc; >> + pix_mp->quantization = inst->quantization; >> + pix_mp->xfer_func = inst->xfer_func; >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_g_selection(struct file *file, void *fh, >> struct v4l2_selection *s) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + >> + dev_dbg(inst->dev->dev, "%s: type %d target %d\n", >> + __func__, s->type, s->target); >> + >> + if (!V4L2_TYPE_IS_OUTPUT(s->type)) >> + return -EINVAL; >> + >> + switch (s->target) { >> + case V4L2_SEL_TGT_CROP_DEFAULT: >> + case V4L2_SEL_TGT_CROP_BOUNDS: >> + s->r.left = 0; >> + s->r.top = 0; >> + s->r.width = inst->src_fmt.width; >> + s->r.height = inst->src_fmt.height; >> + break; >> + case V4L2_SEL_TGT_CROP: >> + s->r = inst->crop; >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_s_selection(struct file *file, void *fh, >> struct v4l2_selection *s) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + u32 max_crop_w, max_crop_h; >> + >> + if (!V4L2_TYPE_IS_OUTPUT(s->type)) >> + return -EINVAL; >> + >> + if (s->target != V4L2_SEL_TGT_CROP) >> + return -EINVAL; >> + >> + if (!(s->flags & (V4L2_SEL_FLAG_GE | V4L2_SEL_FLAG_LE))) >> + s->flags |= V4L2_SEL_FLAG_LE; >> + >> + if (s->flags & V4L2_SEL_FLAG_GE) { >> + s->r.left = round_up(s->r.left, W6_ENC_CROP_STEP); >> + s->r.top = round_up(s->r.top, W6_ENC_CROP_STEP); >> + s->r.width = round_up(s->r.width, W6_ENC_CROP_STEP); >> + s->r.height = round_up(s->r.height, >> W6_ENC_CROP_STEP); >> + } >> + if (s->flags & V4L2_SEL_FLAG_LE) { >> + s->r.left = round_down(s->r.left, W6_ENC_CROP_STEP); >> + s->r.top = round_down(s->r.top, W6_ENC_CROP_STEP); >> + s->r.width = round_down(s->r.width, >> W6_ENC_CROP_STEP); >> + s->r.height = round_down(s->r.height, >> W6_ENC_CROP_STEP); >> + } >> + >> + max_crop_w = inst->src_fmt.width - s->r.left; >> + max_crop_h = inst->src_fmt.height - s->r.top; >> + >> + if (!s->r.width || !s->r.height) >> + return 0; >> + if (max_crop_w < W6_MIN_ENC_PIC_WIDTH) >> + return 0; >> + if (max_crop_h < W6_MIN_ENC_PIC_HEIGHT) >> + return 0; >> + >> + s->r.width = clamp(s->r.width, W6_MIN_ENC_PIC_WIDTH, >> max_crop_w); >> + s->r.height = clamp(s->r.height, W6_MIN_ENC_PIC_HEIGHT, >> max_crop_h); >> + >> + wave6_update_pix_fmt(&inst->dst_fmt, s->r.width, s- >> >r.height); >> + wave6_update_crop_info(inst, s->r.left, s->r.top, s- >> >r.width, s->r.height); >> + >> + dev_dbg(inst->dev->dev, "V4L2_SEL_TGT_CROP %dx%dx%dx%d\n", >> + s->r.left, s->r.top, s->r.width, s->r.height); >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_encoder_cmd(struct file *file, void *fh, >> struct v4l2_encoder_cmd *ec) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + int ret; >> + >> + dev_dbg(inst->dev->dev, "%s: cmd %d\n", __func__, ec->cmd); >> + >> + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); >> + if (ret) >> + return ret; >> + >> + if (!wave6_vpu_both_queues_are_streaming(inst)) >> + return 0; >> + >> + switch (ec->cmd) { >> + case V4L2_ENC_CMD_STOP: >> + wave6_vpu_set_instance_state(inst, >> VPU_INST_STATE_STOP); >> + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, >> true); >> + v4l2_m2m_try_schedule(inst->v4l2_fh.m2m_ctx); >> + break; >> + case V4L2_ENC_CMD_START: >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_g_parm(struct file *file, void *fh, struct >> v4l2_streamparm *a) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + >> + dev_dbg(inst->dev->dev, "%s: type %d\n", __func__, a->type); >> + >> + if (!V4L2_TYPE_IS_OUTPUT(a->type)) >> + return -EINVAL; >> + >> + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; >> + a->parm.output.timeperframe.numerator = 1; >> + a->parm.output.timeperframe.denominator = inst->frame_rate; >> + >> + dev_dbg(inst->dev->dev, "%s: numerator : %d | denominator : >> %d\n", >> + __func__, >> + a->parm.output.timeperframe.numerator, >> + a->parm.output.timeperframe.denominator); >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_s_parm(struct file *file, void *fh, struct >> v4l2_streamparm *a) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + >> + dev_dbg(inst->dev->dev, "%s: type %d\n", __func__, a->type); >> + >> + if (!V4L2_TYPE_IS_OUTPUT(a->type)) >> + return -EINVAL; >> + >> + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; >> + if (a->parm.output.timeperframe.denominator && a- >> >parm.output.timeperframe.numerator) { >> + inst->frame_rate = a- >> >parm.output.timeperframe.denominator / >> + a- >> >parm.output.timeperframe.numerator; >> + } else { >> + a->parm.output.timeperframe.numerator = 1; >> + a->parm.output.timeperframe.denominator = inst- >> >frame_rate; >> + } >> + >> + dev_dbg(inst->dev->dev, "%s: numerator : %d | denominator : >> %d\n", >> + __func__, >> + a->parm.output.timeperframe.numerator, >> + a->parm.output.timeperframe.denominator); >> + >> + return 0; >> +} >> + >> +static const struct v4l2_ioctl_ops wave6_vpu_enc_ioctl_ops = { >> + .vidioc_querycap = wave6_vpu_enc_querycap, >> + .vidioc_enum_framesizes = wave6_vpu_enc_enum_framesizes, >> + >> + .vidioc_enum_fmt_vid_cap = wave6_vpu_enc_enum_fmt_cap, >> + .vidioc_s_fmt_vid_cap_mplane = wave6_vpu_enc_s_fmt_cap, >> + .vidioc_g_fmt_vid_cap_mplane = wave6_vpu_enc_g_fmt_cap, >> + .vidioc_try_fmt_vid_cap_mplane = wave6_vpu_enc_try_fmt_cap, >> + >> + .vidioc_enum_fmt_vid_out = wave6_vpu_enc_enum_fmt_out, >> + .vidioc_s_fmt_vid_out_mplane = wave6_vpu_enc_s_fmt_out, >> + .vidioc_g_fmt_vid_out_mplane = wave6_vpu_enc_g_fmt_out, >> + .vidioc_try_fmt_vid_out_mplane = wave6_vpu_enc_try_fmt_out, >> + >> + .vidioc_g_selection = wave6_vpu_enc_g_selection, >> + .vidioc_s_selection = wave6_vpu_enc_s_selection, >> + >> + .vidioc_g_parm = wave6_vpu_enc_g_parm, >> + .vidioc_s_parm = wave6_vpu_enc_s_parm, >> + >> + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, >> + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, >> + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, >> + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, >> + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, >> + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, >> + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, >> + .vidioc_streamon = v4l2_m2m_ioctl_streamon, >> + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, >> + >> + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, >> + .vidioc_encoder_cmd = wave6_vpu_enc_encoder_cmd, >> + >> + .vidioc_subscribe_event = wave6_vpu_subscribe_event, >> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, >> +}; >> + >> +static int wave6_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl) >> +{ >> + struct vpu_instance *inst = wave6_ctrl_to_vpu_inst(ctrl); >> + struct enc_controls *p = &inst->enc_ctrls; >> + >> + trace_s_ctrl(inst, ctrl); >> + >> + dev_dbg(inst->dev->dev, "%s: name %s value %d\n", >> + __func__, ctrl->name, ctrl->val); >> + >> + switch (ctrl->id) { >> + case V4L2_CID_HFLIP: >> + p->mirror_direction |= (ctrl->val << 1); >> + break; >> + case V4L2_CID_VFLIP: >> + p->mirror_direction |= ctrl->val; >> + break; >> + case V4L2_CID_ROTATE: >> + p->rot_angle = ctrl->val; >> + break; >> + case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: >> + break; >> + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: >> + p->gop_size = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: >> + p->slice_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: >> + p->slice_max_mb = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: >> + p->bitrate_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_BITRATE: >> + p->bitrate = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: >> + p->frame_rc_enable = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: >> + p->mb_rc_enable = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: >> + p->force_key_frame = true; >> + break; >> + case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR: >> + p->prepend_spspps_to_idr = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE: >> + break; >> + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: >> + p->intra_refresh_period = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: >> + p->frame_skip_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: >> + p->hevc.profile = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: >> + p->hevc.level = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP: >> + p->hevc.min_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP: >> + p->hevc.max_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: >> + p->hevc.i_frame_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP: >> + p->hevc.p_frame_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: >> + p->hevc.b_frame_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: >> + p->hevc.loop_filter_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2: >> + p->hevc.lf_beta_offset_div2 = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2: >> + p->hevc.lf_tc_offset_div2 = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE: >> + p->hevc.refresh_type = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD: >> + p->hevc.refresh_period = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED: >> + p->hevc.const_intra_pred = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING: >> + p->hevc.strong_smoothing = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION: >> + p->hevc.tmv_prediction = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: >> + p->h264.profile = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: >> + p->h264.level = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: >> + p->h264.min_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: >> + p->h264.max_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: >> + p->h264.i_frame_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: >> + p->h264.p_frame_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: >> + p->h264.b_frame_qp = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: >> + p->h264.loop_filter_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: >> + p->h264.loop_filter_beta = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: >> + p->h264.loop_filter_alpha = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: >> + p->h264._8x8_transform = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION: >> + p->h264.constrained_intra_prediction = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET: >> + p->h264.chroma_qp_index_offset = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: >> + p->h264.entropy_mode = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: >> + p->h264.i_period = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: >> + p->h264.vui_sar_enable = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: >> + p->h264.vui_sar_idc = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: >> + p->h264.vui_ext_sar_width = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: >> + p->h264.vui_ext_sar_height = ctrl->val; >> + break; >> + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: >> + p->h264.cpb_size = ctrl->val; >> + break; >> + default: >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static const struct v4l2_ctrl_ops wave6_vpu_enc_ctrl_ops = { >> + .s_ctrl = wave6_vpu_enc_s_ctrl, >> +}; >> + >> +static u32 to_video_full_range_flag(enum v4l2_quantization >> quantization) >> +{ >> + switch (quantization) { >> + case V4L2_QUANTIZATION_FULL_RANGE: >> + return 1; >> + case V4L2_QUANTIZATION_LIM_RANGE: >> + default: >> + return 0; >> + } >> +} >> + >> +static u32 to_colour_primaries(enum v4l2_colorspace colorspace) >> +{ >> + switch (colorspace) { >> + case V4L2_COLORSPACE_SMPTE170M: >> + return 6; >> + case V4L2_COLORSPACE_REC709: >> + case V4L2_COLORSPACE_SRGB: >> + case V4L2_COLORSPACE_JPEG: >> + return 1; >> + case V4L2_COLORSPACE_BT2020: >> + return 9; >> + case V4L2_COLORSPACE_DCI_P3: >> + return 11; >> + case V4L2_COLORSPACE_SMPTE240M: >> + return 7; >> + case V4L2_COLORSPACE_470_SYSTEM_M: >> + return 4; >> + case V4L2_COLORSPACE_470_SYSTEM_BG: >> + return 5; >> + case V4L2_COLORSPACE_RAW: >> + default: >> + return 2; >> + } >> +} >> + >> +static u32 to_transfer_characteristics(enum v4l2_colorspace >> colorspace, >> + enum v4l2_xfer_func >> xfer_func) >> +{ >> + if (xfer_func == V4L2_XFER_FUNC_DEFAULT) >> + xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(colorspace); >> + >> + switch (xfer_func) { >> + case V4L2_XFER_FUNC_709: >> + if (colorspace == V4L2_COLORSPACE_SMPTE170M) >> + return 6; >> + else if (colorspace == V4L2_COLORSPACE_BT2020) >> + return 14; >> + else >> + return 1; >> + case V4L2_XFER_FUNC_SRGB: >> + return 13; >> + case V4L2_XFER_FUNC_SMPTE240M: >> + return 7; >> + case V4L2_XFER_FUNC_NONE: >> + return 8; >> + case V4L2_XFER_FUNC_SMPTE2084: >> + return 16; >> + case V4L2_XFER_FUNC_DCI_P3: >> + default: >> + return 2; >> + } >> +} >> + >> +static u32 to_matrix_coeffs(enum v4l2_colorspace colorspace, >> + enum v4l2_ycbcr_encoding ycbcr_enc) >> +{ >> + if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) >> + ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colorspace); >> + >> + switch (ycbcr_enc) { >> + case V4L2_YCBCR_ENC_601: >> + case V4L2_YCBCR_ENC_XV601: >> + if (colorspace == V4L2_COLORSPACE_SMPTE170M) >> + return 6; >> + else >> + return 5; >> + case V4L2_YCBCR_ENC_709: >> + case V4L2_YCBCR_ENC_XV709: >> + return 1; >> + case V4L2_YCBCR_ENC_BT2020: >> + return 9; >> + case V4L2_YCBCR_ENC_BT2020_CONST_LUM: >> + return 10; >> + case V4L2_YCBCR_ENC_SMPTE240M: >> + return 7; >> + default: >> + return 2; >> + } >> +} >> + >> +static void wave6_set_enc_h264_param(struct enc_codec_param *output, >> + struct h264_enc_controls >> *ctrls) >> +{ >> + switch (ctrls->profile) { >> + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: >> + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: >> + output->profile = H264_PROFILE_BP; >> + output->internal_bit_depth = 8; >> + break; >> + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: >> + output->profile = H264_PROFILE_MP; >> + output->internal_bit_depth = 8; >> + break; >> + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: >> + output->profile = H264_PROFILE_EXTENDED; >> + output->internal_bit_depth = 8; >> + break; >> + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: >> + output->profile = H264_PROFILE_HP; >> + output->internal_bit_depth = 8; >> + break; >> + default: >> + break; >> + } >> + switch (ctrls->level) { >> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: >> + output->level = 10; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: >> + output->level = 9; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: >> + output->level = 11; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: >> + output->level = 12; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: >> + output->level = 13; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: >> + output->level = 20; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: >> + output->level = 21; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: >> + output->level = 22; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: >> + output->level = 30; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: >> + output->level = 31; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: >> + output->level = 32; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: >> + output->level = 40; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: >> + output->level = 41; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: >> + output->level = 42; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: >> + output->level = 50; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: >> + output->level = 51; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LEVEL_5_2: >> + output->level = 52; >> + break; >> + default: >> + break; >> + } >> + output->qp = ctrls->i_frame_qp; >> + output->min_qp_i = ctrls->min_qp; >> + output->max_qp_i = ctrls->max_qp; >> + output->min_qp_p = ctrls->min_qp; >> + output->max_qp_p = ctrls->max_qp; >> + output->min_qp_b = ctrls->min_qp; >> + output->max_qp_b = ctrls->max_qp; >> + switch (ctrls->loop_filter_mode) { >> + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED: >> + output->en_dbk = 0; >> + output->en_lf_cross_slice_boundary = 0; >> + break; >> + case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED: >> + output->en_dbk = 1; >> + output->en_lf_cross_slice_boundary = 1; >> + break; >> + case >> V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: >> + output->en_dbk = 1; >> + output->en_lf_cross_slice_boundary = 0; >> + break; >> + default: >> + break; >> + } >> + output->intra_period = ctrls->i_period; >> + output->beta_offset_div2 = ctrls->loop_filter_beta; >> + output->tc_offset_div2 = ctrls->loop_filter_alpha; >> + if (output->profile >= H264_PROFILE_HP) >> + output->en_transform8x8 = ctrls->_8x8_transform; >> + output->en_constrained_intra_pred = ctrls- >> >constrained_intra_prediction; >> + output->cb_qp_offset = ctrls->chroma_qp_index_offset; >> + output->cr_qp_offset = ctrls->chroma_qp_index_offset; >> + if (output->profile >= H264_PROFILE_MP) >> + output->en_cabac = ctrls->entropy_mode; >> + output->en_auto_level_adjusting = >> DEFAULT_EN_AUTO_LEVEL_ADJUSTING; >> +} >> + >> +static void wave6_set_enc_hevc_param(struct enc_codec_param *output, >> + struct hevc_enc_controls >> *ctrls) >> +{ >> + switch (ctrls->profile) { >> + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: >> + output->profile = HEVC_PROFILE_MAIN; >> + output->internal_bit_depth = 8; >> + break; >> + default: >> + break; >> + } >> + switch (ctrls->level) { >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: >> + output->level = 10 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: >> + output->level = 20 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: >> + output->level = 21 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: >> + output->level = 30 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: >> + output->level = 31 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: >> + output->level = 40 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: >> + output->level = 41 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: >> + output->level = 50 * 3; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: >> + output->level = 51 * 3; >> + break; >> + default: >> + break; >> + } >> + output->qp = ctrls->i_frame_qp; >> + output->min_qp_i = ctrls->min_qp; >> + output->max_qp_i = ctrls->max_qp; >> + output->min_qp_p = ctrls->min_qp; >> + output->max_qp_p = ctrls->max_qp; >> + output->min_qp_b = ctrls->min_qp; >> + output->max_qp_b = ctrls->max_qp; >> + switch (ctrls->loop_filter_mode) { >> + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED: >> + output->en_dbk = 0; >> + output->en_sao = 0; >> + output->en_lf_cross_slice_boundary = 0; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED: >> + output->en_dbk = 1; >> + output->en_sao = 1; >> + output->en_lf_cross_slice_boundary = 1; >> + break; >> + case >> V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: >> + output->en_dbk = 1; >> + output->en_sao = 1; >> + output->en_lf_cross_slice_boundary = 0; >> + break; >> + default: >> + break; >> + } >> + switch (ctrls->refresh_type) { >> + case V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE: >> + output->decoding_refresh_type = >> DEC_REFRESH_TYPE_NON_IRAP; >> + break; >> + case V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR: >> + output->decoding_refresh_type = >> DEC_REFRESH_TYPE_IDR; >> + break; >> + default: >> + break; >> + } >> + output->intra_period = ctrls->refresh_period; >> + if (output->idr_period) { >> + output->decoding_refresh_type = >> DEC_REFRESH_TYPE_IDR; >> + output->intra_period = output->idr_period; >> + output->idr_period = 0; >> + } >> + output->beta_offset_div2 = ctrls->lf_beta_offset_div2; >> + output->tc_offset_div2 = ctrls->lf_tc_offset_div2; >> + output->en_constrained_intra_pred = ctrls->const_intra_pred; >> + output->en_strong_intra_smoothing = ctrls->strong_smoothing; >> + output->en_temporal_mvp = ctrls->tmv_prediction; >> + output->num_ticks_poc_diff_one = DEFAULT_NUM_TICKS_POC_DIFF; >> + output->en_auto_level_adjusting = >> DEFAULT_EN_AUTO_LEVEL_ADJUSTING; >> + output->en_intra_trans_skip = DEFAULT_EN_INTRA_TRANS_SKIP; >> + output->en_me_center = DEFAULT_EN_ME_CENTER; >> + output->intra_4x4 = DEFAULT_INTRA_4X4; >> +} >> + >> +static void wave6_set_enc_open_param(struct enc_open_param >> *open_param, >> + struct vpu_instance *inst) >> +{ >> + struct enc_controls *ctrls = &inst->enc_ctrls; >> + struct enc_codec_param *output = &open_param->codec_param; >> + u32 ctu_size = (inst->std == W_AVC_ENC) ? 16 : 64; >> + u32 num_ctu_row = ALIGN(inst->src_fmt.height, ctu_size) / >> ctu_size; >> + >> + open_param->source_endian = VPU_SOURCE_ENDIAN; >> + if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV420M || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) { >> + open_param->src_format = FORMAT_420; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422P >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV422M >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV16M >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV61M) >> { >> + open_param->src_format = FORMAT_422; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV24 || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV42) { >> + open_param->src_format = FORMAT_YUV444_24BIT; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUV24) >> { >> + open_param->src_format = FORMAT_YUV444_24BIT_PACKED; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_YUYV) { >> + open_param->src_format = FORMAT_YUYV; >> + open_param->packed_format = PACKED_YUYV; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGB24) >> { >> + open_param->src_format = FORMAT_RGB_24BIT_PACKED; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_P010) { >> + open_param->src_format = FORMAT_420_P10_16BIT_MSB; >> + open_param->source_endian = VDI_128BIT_LE_BYTE_SWAP; >> + } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_ARGB32 >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_XRGB32 >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBA32 >> || >> + inst->src_fmt.pixelformat == V4L2_PIX_FMT_RGBX32) >> { >> + open_param->src_format = FORMAT_RGB_32BIT_PACKED; >> + } else if (inst->src_fmt.pixelformat == >> V4L2_PIX_FMT_ARGB2101010) { >> + open_param->src_format = >> FORMAT_RGB_P10_32BIT_PACKED; >> + open_param->source_endian = >> VDI_128BIT_LE_WORD_BYTE_SWAP; >> + } >> + open_param->line_buf_int_en = true; >> + open_param->stream_endian = VPU_STREAM_ENDIAN; >> + open_param->inst_buffer.temp_base = inst->dev- >> >temp_vbuf.daddr; >> + open_param->inst_buffer.temp_size = inst->dev- >> >temp_vbuf.size; >> + open_param->inst_buffer.ar_base = inst->ar_vbuf.daddr; >> + open_param->pic_width = inst->codec_rect.width; >> + open_param->pic_height = inst->codec_rect.height; >> + >> + output->custom_map_endian = VPU_USER_DATA_ENDIAN; >> + output->gop_preset_idx = PRESET_IDX_IPP_SINGLE; >> + output->temp_layer_cnt = DEFAULT_TEMP_LAYER_CNT; >> + output->rc_initial_level = DEFAULT_RC_INITIAL_LEVEL; >> + output->pic_rc_max_dqp = DEFAULT_PIC_RC_MAX_DQP; >> + output->rc_initial_qp = DEFAULT_RC_INITIAL_QP; >> + output->en_adaptive_round = DEFAULT_EN_ADAPTIVE_ROUND; >> + output->q_round_inter = DEFAULT_Q_ROUND_INTER; >> + output->q_round_intra = DEFAULT_Q_ROUND_INTRA; >> + >> + output->frame_rate = inst->frame_rate; >> + output->idr_period = ctrls->gop_size; >> + output->rc_mode = ctrls->bitrate_mode; >> + output->rc_update_speed = (ctrls->bitrate_mode) ? >> DEFAULT_RC_UPDATE_SPEED_CBR : >> + >> DEFAULT_RC_UPDATE_SPEED_VBR; >> + output->en_rate_control = ctrls->frame_rc_enable; >> + output->en_cu_level_rate_control = ctrls->mb_rc_enable; >> + output->max_intra_pic_bit = inst- >> >dst_fmt.plane_fmt[0].sizeimage * 8; >> + output->max_inter_pic_bit = inst- >> >dst_fmt.plane_fmt[0].sizeimage * 8; >> + output->bitrate = ctrls->bitrate; >> + output->cpb_size = wave6_cpb_size_msec(ctrls->h264.cpb_size, >> ctrls->bitrate); >> + output->slice_mode = ctrls->slice_mode; >> + output->slice_arg = ctrls->slice_max_mb; >> + output->forced_idr_header = ctrls->prepend_spspps_to_idr; >> + output->en_vbv_overflow_drop_frame = (ctrls- >> >frame_skip_mode) ? 1 : 0; >> + if (ctrls->intra_refresh_period) { >> + output->intra_refresh_mode = INTRA_REFRESH_ROW; >> + if (ctrls->intra_refresh_period < num_ctu_row) { >> + output->intra_refresh_arg = (num_ctu_row + >> ctrls->intra_refresh_period - 1) >> + / ctrls- >> >intra_refresh_period; >> + } else { >> + output->intra_refresh_arg = 1; >> + } >> + } >> + output->sar.enable = ctrls->h264.vui_sar_enable; >> + output->sar.idc = ctrls->h264.vui_sar_idc; >> + if (output->sar.idc == >> V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED) >> + output->sar.idc = H264_VUI_SAR_IDC_EXTENDED; >> + output->sar.width = ctrls->h264.vui_ext_sar_width; >> + output->sar.height = ctrls->h264.vui_ext_sar_height; >> + output->color.video_signal_type_present = >> DEFAULT_VUI_VIDEO_SIGNAL_TYPE_PRESENT_FLAG; >> + output->color.color_range = to_video_full_range_flag(inst- >> >quantization); >> + output->color.color_description_present = >> DEFAULT_VUI_COLOR_DESCRIPTION_PRESENT_FLAG; >> + output->color.color_primaries = to_colour_primaries(inst- >> >colorspace); >> + output->color.transfer_characteristics = >> to_transfer_characteristics(inst->colorspace, >> + >> inst->xfer_func); >> + output->color.matrix_coefficients = to_matrix_coeffs(inst- >> >colorspace, inst->ycbcr_enc); >> + output->conf_win.left = inst->crop.left - inst- >> >codec_rect.left; >> + output->conf_win.top = inst->crop.top - inst- >> >codec_rect.top; >> + output->conf_win.right = inst->codec_rect.width >> + - inst->crop.width - output- >> >conf_win.left; >> + output->conf_win.bottom = inst->codec_rect.height >> + - inst->crop.height - >> output->conf_win.top; >> + >> + switch (inst->std) { >> + case W_AVC_ENC: >> + wave6_set_enc_h264_param(output, &ctrls->h264); >> + break; >> + case W_HEVC_ENC: >> + wave6_set_enc_hevc_param(output, &ctrls->hevc); >> + break; >> + default: >> + break; >> + } >> +} >> + >> +static int wave6_vpu_enc_create_instance(struct vpu_instance *inst) >> +{ >> + int ret; >> + struct enc_open_param open_param; >> + >> + memset(&open_param, 0, sizeof(struct enc_open_param)); >> + >> + wave6_vpu_activate(inst->dev); >> + ret = pm_runtime_resume_and_get(inst->dev->dev); >> + if (ret) { >> + dev_err(inst->dev->dev, "runtime_resume failed >> %d\n", ret); >> + return ret; >> + } >> + >> + wave6_vpu_wait_activated(inst->dev); >> + >> + inst->ar_vbuf.size = ALIGN(WAVE6_ARBUF_SIZE, 4096); >> + ret = wave6_alloc_dma(inst->dev->dev, &inst->ar_vbuf); >> + if (ret) { >> + dev_err(inst->dev->dev, "alloc ar of size %zu >> failed\n", >> + inst->ar_vbuf.size); >> + goto error_pm; >> + } >> + >> + wave6_set_enc_open_param(&open_param, inst); >> + >> + ret = wave6_vpu_enc_open(inst, &open_param); >> + if (ret) { >> + dev_err(inst->dev->dev, "failed create instance : >> %d\n", ret); >> + goto error_open; >> + } >> + >> + dprintk(inst->dev->dev, "[%d] encoder\n", inst->id); >> + wave6_vpu_create_dbgfs_file(inst); >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_OPEN); >> + >> + return 0; >> + >> +error_open: >> + wave6_free_dma(&inst->ar_vbuf); >> +error_pm: >> + pm_runtime_put_sync(inst->dev->dev); >> + return ret; >> +} >> + >> +static int wave6_vpu_enc_initialize_instance(struct vpu_instance >> *inst) >> +{ >> + int ret; >> + struct enc_initial_info initial_info; >> + struct v4l2_ctrl *ctrl; >> + >> + if (inst->enc_ctrls.mirror_direction) { >> + wave6_vpu_enc_give_command(inst, ENABLE_MIRRORING, >> NULL); >> + wave6_vpu_enc_give_command(inst, >> SET_MIRROR_DIRECTION, >> + &inst- >> >enc_ctrls.mirror_direction); >> + } >> + if (inst->enc_ctrls.rot_angle) { >> + wave6_vpu_enc_give_command(inst, ENABLE_ROTATION, >> NULL); >> + wave6_vpu_enc_give_command(inst, SET_ROTATION_ANGLE, >> + &inst- >> >enc_ctrls.rot_angle); >> + } >> + >> + ret = wave6_vpu_enc_issue_seq_init(inst); >> + if (ret) { >> + dev_err(inst->dev->dev, "seq init fail %d\n", ret); >> + return ret; >> + } >> + >> + if (wave6_vpu_wait_interrupt(inst, W6_VPU_TIMEOUT) < 0) { >> + dev_err(inst->dev->dev, "seq init timeout\n"); >> + return ret; >> + } >> + >> + ret = wave6_vpu_enc_complete_seq_init(inst, &initial_info); >> + if (ret) { >> + dev_err(inst->dev->dev, "seq init error\n"); >> + return ret; >> + } >> + >> + dev_dbg(inst->dev->dev, "min_fb_cnt : %d | min_src_cnt : >> %d\n", >> + initial_info.min_frame_buffer_count, >> + initial_info.min_src_frame_count); >> + >> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, >> + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); >> + if (ctrl) >> + v4l2_ctrl_s_ctrl(ctrl, >> initial_info.min_src_frame_count); >> + >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); >> + >> + return 0; >> +} >> + >> +static int wave6_vpu_enc_prepare_fb(struct vpu_instance *inst) >> +{ >> + int ret; >> + unsigned int i; >> + unsigned int fb_num; >> + unsigned int mv_num; >> + unsigned int fb_stride; >> + unsigned int fb_height; >> + struct enc_info *p_enc_info = &inst->codec_info->enc_info; >> + >> + fb_num = p_enc_info->initial_info.min_frame_buffer_count; >> + mv_num = p_enc_info->initial_info.req_mv_buffer_count; >> + >> + fb_stride = ALIGN(inst->codec_rect.width, 32); >> + fb_height = ALIGN(inst->codec_rect.height, 32); >> + >> + for (i = 0; i < fb_num; i++) { >> + struct frame_buffer *frame = &inst->frame_buf[i]; >> + struct vpu_buf *vframe = &inst->frame_vbuf[i]; >> + unsigned int l_size = fb_stride * fb_height; >> + unsigned int ch_size = ALIGN(fb_stride / 2, 32) * >> fb_height; >> + >> + vframe->size = l_size + ch_size; >> + ret = wave6_alloc_dma(inst->dev->dev, vframe); >> + if (ret) { >> + dev_err(inst->dev->dev, "alloc FBC buffer >> fail : %zu\n", >> + vframe->size); >> + goto error; >> + } >> + >> + frame->buf_y = vframe->daddr; >> + frame->buf_cb = vframe->daddr + l_size; >> + frame->buf_cr = (dma_addr_t)-1; >> + frame->stride = fb_stride; >> + frame->height = fb_height; >> + frame->map_type = COMPRESSED_FRAME_MAP; >> + } >> + >> + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_Y_TBL, >> fb_num); >> + if (ret) >> + goto error; >> + >> + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_FBC_C_TBL, >> fb_num); >> + if (ret) >> + goto error; >> + >> + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_MV_COL, >> mv_num); >> + if (ret) >> + goto error; >> + >> + ret = wave6_allocate_aux_buffer(inst, AUX_BUF_SUB_SAMPLE, >> fb_num); >> + if (ret) >> + goto error; >> + >> + ret = wave6_vpu_enc_register_frame_buffer_ex(inst, fb_num, >> fb_stride, >> + fb_height, >> + >> COMPRESSED_FRAME_MAP); >> + if (ret) { >> + dev_err(inst->dev->dev, "register frame buffer fail >> %d\n", ret); >> + goto error; >> + } >> + >> + wave6_vpu_set_instance_state(inst, VPU_INST_STATE_PIC_RUN); >> + >> + return 0; >> + >> +error: >> + wave6_vpu_enc_release_fb(inst); >> + return ret; >> +} >> + >> +static int wave6_vpu_enc_queue_setup(struct vb2_queue *q, unsigned >> int *num_buffers, >> + unsigned int *num_planes, >> unsigned int sizes[], >> + struct device *alloc_devs[]) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(q); >> + struct v4l2_pix_format_mplane inst_format = >> + (V4L2_TYPE_IS_OUTPUT(q->type)) ? inst->src_fmt : >> inst->dst_fmt; >> + unsigned int i; >> + >> + dev_dbg(inst->dev->dev, "%s: num_buffers %d num_planes %d >> type %d\n", >> + __func__, *num_buffers, *num_planes, q->type); >> + >> + if (*num_planes) { >> + if (inst_format.num_planes != *num_planes) >> + return -EINVAL; >> + >> + for (i = 0; i < *num_planes; i++) { >> + if (sizes[i] < >> inst_format.plane_fmt[i].sizeimage) >> + return -EINVAL; >> + } >> + } else { >> + *num_planes = inst_format.num_planes; >> + for (i = 0; i < *num_planes; i++) { >> + sizes[i] = >> inst_format.plane_fmt[i].sizeimage; >> + dev_dbg(inst->dev->dev, "size[%d] : %d\n", >> i, sizes[i]); >> + } >> + >> + if (V4L2_TYPE_IS_OUTPUT(q->type)) { >> + struct v4l2_ctrl *ctrl; >> + unsigned int min_src_frame_count = 0; >> + >> + ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, >> + >> V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); >> + if (ctrl) >> + min_src_frame_count = >> v4l2_ctrl_g_ctrl(ctrl); >> + >> + *num_buffers = max(*num_buffers, >> min_src_frame_count); >> + } >> + } >> + >> + return 0; >> +} >> + >> +static void wave6_vpu_enc_buf_queue(struct vb2_buffer *vb) >> +{ >> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); >> + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); >> + >> + dev_dbg(inst->dev->dev, "type %4d index %4d size[0] %4ld >> size[1] : %4ld | size[2] : %4ld\n", >> + vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, >> 0), >> + vb2_plane_size(&vbuf->vb2_buf, 1), >> vb2_plane_size(&vbuf->vb2_buf, 2)); >> + >> + if (V4L2_TYPE_IS_OUTPUT(vb->type)) { >> + vbuf->sequence = inst->queued_src_buf_num++; >> + >> + vpu_buf->ts_input = ktime_get_raw(); >> + vpu_buf->force_key_frame = inst- >> >enc_ctrls.force_key_frame; >> + inst->enc_ctrls.force_key_frame = false; >> + vpu_buf->force_frame_qp = (!inst- >> >enc_ctrls.frame_rc_enable) ? true : false; >> + if (vpu_buf->force_frame_qp) { >> + if (inst->std == W_AVC_ENC) { >> + vpu_buf->force_i_frame_qp = inst- >> >enc_ctrls.h264.i_frame_qp; >> + vpu_buf->force_p_frame_qp = inst- >> >enc_ctrls.h264.p_frame_qp; >> + vpu_buf->force_b_frame_qp = inst- >> >enc_ctrls.h264.b_frame_qp; >> + } else if (inst->std == W_HEVC_ENC) { >> + vpu_buf->force_i_frame_qp = inst- >> >enc_ctrls.hevc.i_frame_qp; >> + vpu_buf->force_p_frame_qp = inst- >> >enc_ctrls.hevc.p_frame_qp; >> + vpu_buf->force_b_frame_qp = inst- >> >enc_ctrls.hevc.b_frame_qp; >> + } >> + } >> + } else { >> + inst->queued_dst_buf_num++; >> + } >> + >> + vpu_buf->consumed = false; >> + vpu_buf->used = false; >> + v4l2_m2m_buf_queue(inst->v4l2_fh.m2m_ctx, vbuf); >> +} >> + >> +static void wave6_vpu_enc_buf_finish(struct vb2_buffer *vb) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); >> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); >> + struct vpu_buffer *vpu_buf = wave6_to_vpu_buf(vbuf); >> + struct v4l2_ctrl *ctrl; >> + >> + if (V4L2_TYPE_IS_OUTPUT(vb->type)) >> + return; >> + >> + ctrl = v4l2_ctrl_find(inst->v4l2_fh.ctrl_handler, >> V4L2_CID_MPEG_VIDEO_AVERAGE_QP); >> + if (ctrl) >> + v4l2_ctrl_s_ctrl(ctrl, vpu_buf->average_qp); >> +} >> + >> +static int wave6_vpu_enc_start_streaming(struct vb2_queue *q, >> unsigned int count) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(q); >> + struct v4l2_pix_format_mplane *fmt; >> + struct vb2_queue *vq_peer; >> + int ret = 0; >> + >> + trace_start_streaming(inst, q->type); >> + >> + if (V4L2_TYPE_IS_OUTPUT(q->type)) { >> + fmt = &inst->src_fmt; >> + vq_peer = v4l2_m2m_get_dst_vq(inst- >> >v4l2_fh.m2m_ctx); >> + } else { >> + fmt = &inst->dst_fmt; >> + vq_peer = v4l2_m2m_get_src_vq(inst- >> >v4l2_fh.m2m_ctx); >> + } >> + >> + dprintk(inst->dev->dev, "[%d] %s %c%c%c%c %dx%d, %d >> buffers\n", >> + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : >> "capture", >> + fmt->pixelformat, >> + fmt->pixelformat >> 8, >> + fmt->pixelformat >> 16, >> + fmt->pixelformat >> 24, >> + fmt->width, fmt->height, vb2_get_num_buffers(q)); >> + >> + if (!vb2_is_streaming(vq_peer)) >> + return 0; >> + >> + wave6_vpu_pause(inst->dev->dev, 0); >> + >> + if (inst->state == VPU_INST_STATE_NONE) { >> + ret = wave6_vpu_enc_create_instance(inst); >> + if (ret) >> + goto exit; >> + } >> + >> + if (inst->state == VPU_INST_STATE_OPEN) { >> + ret = wave6_vpu_enc_initialize_instance(inst); >> + if (ret) { >> + wave6_vpu_enc_destroy_instance(inst); >> + goto exit; >> + } >> + } >> + >> + if (inst->state == VPU_INST_STATE_INIT_SEQ) { >> + ret = wave6_vpu_enc_prepare_fb(inst); >> + if (ret) { >> + wave6_vpu_enc_destroy_instance(inst); >> + goto exit; >> + } >> + } >> + >> +exit: >> + wave6_vpu_pause(inst->dev->dev, 1); >> + if (ret) >> + wave6_vpu_return_buffers(inst, q->type, >> VB2_BUF_STATE_QUEUED); >> + >> + return ret; >> +} >> + >> +static void wave6_vpu_enc_stop_streaming(struct vb2_queue *q) >> +{ >> + struct vpu_instance *inst = vb2_get_drv_priv(q); >> + struct vb2_queue *vq_peer; >> + >> + trace_stop_streaming(inst, q->type); >> + >> + dprintk(inst->dev->dev, "[%d] %s, input %d, decode %d\n", >> + inst->id, V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : >> "capture", >> + inst->queued_src_buf_num, inst->sequence); >> + >> + if (inst->state == VPU_INST_STATE_NONE) >> + goto exit; >> + >> + if (wave6_vpu_both_queues_are_streaming(inst)) >> + wave6_vpu_set_instance_state(inst, >> VPU_INST_STATE_STOP); >> + >> + wave6_vpu_pause(inst->dev->dev, 0); >> + >> + if (V4L2_TYPE_IS_OUTPUT(q->type)) { >> + wave6_vpu_reset_performance(inst); >> + inst->queued_src_buf_num = 0; >> + inst->processed_buf_num = 0; >> + inst->error_buf_num = 0; >> + inst->sequence = 0; >> + v4l2_m2m_set_src_buffered(inst->v4l2_fh.m2m_ctx, >> false); >> + } else { >> + inst->eos = false; >> + inst->queued_dst_buf_num = 0; >> + } >> + >> + if (V4L2_TYPE_IS_OUTPUT(q->type)) >> + vq_peer = v4l2_m2m_get_dst_vq(inst- >> >v4l2_fh.m2m_ctx); >> + else >> + vq_peer = v4l2_m2m_get_src_vq(inst- >> >v4l2_fh.m2m_ctx); >> + >> + if (!vb2_is_streaming(vq_peer) && inst->state != >> VPU_INST_STATE_NONE) >> + wave6_vpu_enc_destroy_instance(inst); >> + >> + wave6_vpu_pause(inst->dev->dev, 1); >> + >> +exit: >> + wave6_vpu_return_buffers(inst, q->type, >> VB2_BUF_STATE_ERROR); >> +} >> + >> +static const struct vb2_ops wave6_vpu_enc_vb2_ops = { >> + .queue_setup = wave6_vpu_enc_queue_setup, >> + .wait_prepare = vb2_ops_wait_prepare, >> + .wait_finish = vb2_ops_wait_finish, >> + .buf_queue = wave6_vpu_enc_buf_queue, >> + .buf_finish = wave6_vpu_enc_buf_finish, >> + .start_streaming = wave6_vpu_enc_start_streaming, >> + .stop_streaming = wave6_vpu_enc_stop_streaming, >> +}; >> + >> +static void wave6_set_default_format(struct v4l2_pix_format_mplane >> *src_fmt, >> + struct v4l2_pix_format_mplane >> *dst_fmt) >> +{ >> + const struct vpu_format *vpu_fmt; >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_RAW); >> + if (vpu_fmt) { >> + src_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + src_fmt->num_planes = vpu_fmt->num_planes; >> + wave6_update_pix_fmt(src_fmt, >> + W6_DEF_ENC_PIC_WIDTH, >> W6_DEF_ENC_PIC_HEIGHT); >> + } >> + >> + vpu_fmt = wave6_find_vpu_fmt_by_idx(0, VPU_FMT_TYPE_CODEC); >> + if (vpu_fmt) { >> + dst_fmt->pixelformat = vpu_fmt->v4l2_pix_fmt; >> + dst_fmt->num_planes = vpu_fmt->num_planes; >> + wave6_update_pix_fmt(dst_fmt, >> + W6_DEF_ENC_PIC_WIDTH, >> W6_DEF_ENC_PIC_HEIGHT); >> + } >> +} >> + >> +static int wave6_vpu_enc_queue_init(void *priv, struct vb2_queue >> *src_vq, struct vb2_queue *dst_vq) >> +{ >> + struct vpu_instance *inst = priv; >> + int ret; >> + >> + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; >> + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; >> + src_vq->mem_ops = &vb2_dma_contig_memops; >> + src_vq->ops = &wave6_vpu_enc_vb2_ops; >> + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; >> + src_vq->buf_struct_size = sizeof(struct vpu_buffer); >> + src_vq->drv_priv = inst; >> + src_vq->lock = &inst->dev->dev_lock; >> + src_vq->dev = inst->dev->v4l2_dev.dev; >> + ret = vb2_queue_init(src_vq); >> + if (ret) >> + return ret; >> + >> + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; >> + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; >> + dst_vq->mem_ops = &vb2_dma_contig_memops; >> + dst_vq->ops = &wave6_vpu_enc_vb2_ops; >> + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; >> + dst_vq->buf_struct_size = sizeof(struct vpu_buffer); >> + dst_vq->drv_priv = inst; >> + dst_vq->lock = &inst->dev->dev_lock; >> + dst_vq->dev = inst->dev->v4l2_dev.dev; >> + ret = vb2_queue_init(dst_vq); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> + >> +static const struct vpu_instance_ops wave6_vpu_enc_inst_ops = { >> + .start_process = wave6_vpu_enc_start_encode, >> + .finish_process = wave6_vpu_enc_finish_encode, >> +}; >> + >> +static int wave6_vpu_open_enc(struct file *filp) >> +{ >> + struct video_device *vdev = video_devdata(filp); >> + struct vpu_device *dev = video_drvdata(filp); >> + struct vpu_instance *inst = NULL; >> + struct v4l2_ctrl_handler *v4l2_ctrl_hdl; >> + int ret; >> + >> + inst = kzalloc(sizeof(*inst), GFP_KERNEL); >> + if (!inst) >> + return -ENOMEM; >> + v4l2_ctrl_hdl = &inst->v4l2_ctrl_hdl; >> + >> + inst->dev = dev; >> + inst->type = VPU_INST_TYPE_ENC; >> + inst->ops = &wave6_vpu_enc_inst_ops; >> + >> + v4l2_fh_init(&inst->v4l2_fh, vdev); >> + filp->private_data = &inst->v4l2_fh; >> + v4l2_fh_add(&inst->v4l2_fh); >> + >> + inst->v4l2_fh.m2m_ctx = >> + v4l2_m2m_ctx_init(dev->m2m_dev, inst, >> wave6_vpu_enc_queue_init); >> + if (IS_ERR(inst->v4l2_fh.m2m_ctx)) { >> + ret = PTR_ERR(inst->v4l2_fh.m2m_ctx); >> + goto free_inst; >> + } >> + >> + v4l2_ctrl_handler_init(v4l2_ctrl_hdl, 50); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, >> + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, 0, >> + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, >> + V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, 0, >> + V4L2_MPEG_VIDEO_HEVC_LEVEL_5); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP, >> + 0, 51, 1, 8); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP, >> + 0, 51, 1, 51); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, >> + 0, 51, 1, 30); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP, >> + 0, 51, 1, 30); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP, >> + 0, 51, 1, 30); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE, >> + >> V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, >> + >> V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2, >> + -6, 6, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2, >> + -6, 6, 1, 0); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE, >> + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR, >> + >> BIT(V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA), >> + V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD, >> + 0, 2047, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED, >> + 0, 1, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING, >> + 0, 1, 1, 1); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION, >> + 0, 1, 1, 1); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_PROFILE, >> + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, 0, >> + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_LEVEL, >> + V4L2_MPEG_VIDEO_H264_LEVEL_5_2, 0, >> + V4L2_MPEG_VIDEO_H264_LEVEL_5_0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, >> + 0, 51, 1, 8); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, >> + 0, 51, 1, 51); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, >> + 0, 51, 1, 30); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, >> + 0, 51, 1, 30); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, >> + 0, 51, 1, 30); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE, >> + >> V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY, 0, >> + >> V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA, >> + -6, 6, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA, >> + -6, 6, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, >> + 0, 1, 1, 1); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, >> + 0, 1, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET, >> + -12, 12, 1, 0); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, >> + >> V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, 0, >> + >> V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, >> + 0, 2047, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, >> 0, 1, 1, 0); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC, >> + >> V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED, 0, >> + >> V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH, >> + 0, 0xFFFF, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT, >> + 0, 0xFFFF, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_HFLIP, >> + 0, 1, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_VFLIP, >> + 0, 1, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_ROTATE, >> + 0, 270, 90, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, >> + 0, 18750000, 1, 0); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, >> + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, >> + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_BITRATE, >> + 1, 1500000000, 1, 2097152); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, >> + 0, 1, 1, 1); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, >> + 0, 1, 1, 1); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_GOP_SIZE, >> + 0, 2047, 1, 30); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, >> + >> V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB, 0, >> + >> V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, >> + 0, 0x3FFFF, 1, 1); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, >> + 0, 1, 1, 0); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR, >> + 0, 1, 1, 1); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + >> V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE, >> + >> V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC, >> + >> BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM), >> + >> V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD, >> + 0, 2160, 1, 0); >> + v4l2_ctrl_new_std_menu(v4l2_ctrl_hdl, >> &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE, >> + >> V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT, >> + >> BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT), >> + >> V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave6_vpu_enc_ctrl_ops, >> + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, >> 1); >> + v4l2_ctrl_new_std(v4l2_ctrl_hdl, NULL, >> + V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, >> 0); >> + >> + if (v4l2_ctrl_hdl->error) { >> + ret = -ENODEV; >> + goto err_m2m_release; >> + } >> + >> + inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl; >> + v4l2_ctrl_handler_setup(v4l2_ctrl_hdl); >> + >> + wave6_set_default_format(&inst->src_fmt, &inst->dst_fmt); >> + wave6_update_crop_info(inst, 0, 0, inst->dst_fmt.width, >> inst->dst_fmt.height); >> + inst->colorspace = V4L2_COLORSPACE_DEFAULT; >> + inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; >> + inst->quantization = V4L2_QUANTIZATION_DEFAULT; >> + inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; >> + inst->frame_rate = 30; >> + >> + return 0; >> + >> +err_m2m_release: >> + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); >> +free_inst: >> + kfree(inst); >> + return ret; >> +} >> + >> +static int wave6_vpu_enc_release(struct file *filp) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(filp- >> >private_data); >> + >> + dprintk(inst->dev->dev, "[%d] release\n", inst->id); >> + v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); >> + >> + mutex_lock(&inst->dev->dev_lock); >> + if (inst->state != VPU_INST_STATE_NONE) { >> + wave6_vpu_pause(inst->dev->dev, 0); >> + wave6_vpu_enc_destroy_instance(inst); >> + wave6_vpu_pause(inst->dev->dev, 1); >> + } >> + mutex_unlock(&inst->dev->dev_lock); >> + >> + v4l2_ctrl_handler_free(&inst->v4l2_ctrl_hdl); >> + v4l2_fh_del(&inst->v4l2_fh); >> + v4l2_fh_exit(&inst->v4l2_fh); >> + kfree(inst); >> + >> + return 0; >> +} >> + >> +static const struct v4l2_file_operations wave6_vpu_enc_fops = { >> + .owner = THIS_MODULE, >> + .open = wave6_vpu_open_enc, >> + .release = wave6_vpu_enc_release, >> + .unlocked_ioctl = video_ioctl2, >> + .poll = v4l2_m2m_fop_poll, >> + .mmap = v4l2_m2m_fop_mmap, >> +}; >> + >> +int wave6_vpu_enc_register_device(struct vpu_device *dev) >> +{ >> + struct video_device *vdev_enc; >> + int ret; >> + >> + vdev_enc = devm_kzalloc(dev->v4l2_dev.dev, >> sizeof(*vdev_enc), GFP_KERNEL); >> + if (!vdev_enc) >> + return -ENOMEM; >> + >> + dev->video_dev_enc = vdev_enc; >> + >> + strscpy(vdev_enc->name, VPU_ENC_DEV_NAME, sizeof(vdev_enc- >> >name)); >> + vdev_enc->fops = &wave6_vpu_enc_fops; >> + vdev_enc->ioctl_ops = &wave6_vpu_enc_ioctl_ops; >> + vdev_enc->release = video_device_release_empty; >> + vdev_enc->v4l2_dev = &dev->v4l2_dev; >> + vdev_enc->vfl_dir = VFL_DIR_M2M; >> + vdev_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | >> V4L2_CAP_STREAMING; >> + vdev_enc->lock = &dev->dev_lock; >> + video_set_drvdata(vdev_enc, dev); >> + >> + ret = video_register_device(vdev_enc, VFL_TYPE_VIDEO, -1); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> + >> +void wave6_vpu_enc_unregister_device(struct vpu_device *dev) >> +{ >> + video_unregister_device(dev->video_dev_enc); >> +} >> diff --git a/drivers/media/platform/chips-media/wave6/wave6-vpu- >> v4l2.c b/drivers/media/platform/chips-media/wave6/wave6-vpu-v4l2.c >> new file mode 100644 >> index 000000000000..e614eda01a5a >> --- /dev/null >> +++ b/drivers/media/platform/chips-media/wave6/wave6-vpu-v4l2.c >> @@ -0,0 +1,381 @@ >> +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) >> +/* >> + * Wave6 series multi-standard codec IP - v4l2 driver helper >> interface >> + * >> + * Copyright (C) 2025 CHIPS&MEDIA INC >> + */ >> + >> +#include <linux/clk.h> >> +#include <linux/math64.h> >> +#include "wave6-vpu.h" >> +#include "wave6-vpu-dbg.h" >> +#include "wave6-trace.h" >> + >> +void wave6_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, >> + unsigned int width, >> + unsigned int height) >> +{ >> + const struct v4l2_format_info *fmt_info; >> + unsigned int stride_y; >> + int i; >> + >> + pix_mp->width = width; >> + pix_mp->height = height; >> + pix_mp->flags = 0; >> + pix_mp->field = V4L2_FIELD_NONE; >> + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); >> + >> + fmt_info = v4l2_format_info(pix_mp->pixelformat); >> + if (!fmt_info) { >> + pix_mp->plane_fmt[0].bytesperline = 0; >> + if (!pix_mp->plane_fmt[0].sizeimage) >> + pix_mp->plane_fmt[0].sizeimage = width * >> height; >> + >> + return; >> + } >> + >> + stride_y = width * fmt_info->bpp[0]; >> + if (pix_mp->plane_fmt[0].bytesperline <= W6_MAX_PIC_STRIDE) >> + stride_y = max(stride_y, pix_mp- >> >plane_fmt[0].bytesperline); >> + stride_y = round_up(stride_y, 32); >> + pix_mp->plane_fmt[0].bytesperline = stride_y; >> + pix_mp->plane_fmt[0].sizeimage = stride_y * height; >> + >> + stride_y = DIV_ROUND_UP(stride_y, fmt_info->bpp[0]); >> + >> + for (i = 1; i < fmt_info->comp_planes; i++) { >> + unsigned int stride_c, sizeimage_c; >> + >> + stride_c = DIV_ROUND_UP(stride_y, fmt_info->hdiv) * >> + fmt_info->bpp[i]; >> + sizeimage_c = stride_c * DIV_ROUND_UP(height, >> fmt_info->vdiv); >> + >> + if (fmt_info->mem_planes == 1) { >> + pix_mp->plane_fmt[0].sizeimage += >> sizeimage_c; >> + } else { >> + pix_mp->plane_fmt[i].bytesperline = >> stride_c; >> + pix_mp->plane_fmt[i].sizeimage = >> sizeimage_c; >> + } >> + } >> +} >> + >> +dma_addr_t wave6_get_dma_addr(struct vb2_v4l2_buffer *buf, unsigned >> int plane_no) >> +{ >> + return vb2_dma_contig_plane_dma_addr(&buf->vb2_buf, >> plane_no) + >> + buf->planes[plane_no].data_offset; >> +} >> + >> +struct vb2_v4l2_buffer *wave6_get_dst_buf_by_addr(struct >> vpu_instance *inst, >> + dma_addr_t addr) >> +{ >> + struct vb2_v4l2_buffer *vb2_v4l2_buf; >> + struct v4l2_m2m_buffer *v4l2_m2m_buf; >> + struct vb2_v4l2_buffer *dst_buf = NULL; >> + >> + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, >> v4l2_m2m_buf) { >> + vb2_v4l2_buf = &v4l2_m2m_buf->vb; >> + if (addr == wave6_get_dma_addr(vb2_v4l2_buf, 0)) { >> + dst_buf = vb2_v4l2_buf; >> + break; >> + } >> + } >> + >> + return dst_buf; >> +} >> + >> +enum codec_std wave6_to_codec_std(enum vpu_instance_type type, >> unsigned int v4l2_pix_fmt) >> +{ >> + enum codec_std std = STD_UNKNOWN; >> + >> + if (v4l2_pix_fmt == V4L2_PIX_FMT_H264) >> + std = (type == VPU_INST_TYPE_DEC) ? W_AVC_DEC : >> W_AVC_ENC; >> + else if (v4l2_pix_fmt == V4L2_PIX_FMT_HEVC) >> + std = (type == VPU_INST_TYPE_DEC) ? W_HEVC_DEC : >> W_HEVC_ENC; >> + >> + return std; >> +} >> + >> +const char *wave6_vpu_instance_state_name(u32 state) >> +{ >> + switch (state) { >> + case VPU_INST_STATE_NONE: return "none"; >> + case VPU_INST_STATE_OPEN: return "open"; >> + case VPU_INST_STATE_INIT_SEQ: return "init_seq"; >> + case VPU_INST_STATE_PIC_RUN: return "pic_run"; >> + case VPU_INST_STATE_SEEK: return "seek"; >> + case VPU_INST_STATE_STOP: return "stop"; >> + } >> + return "unknown"; >> +} >> + >> +void wave6_vpu_set_instance_state(struct vpu_instance *inst, u32 >> state) >> +{ >> + trace_set_state(inst, state); >> + >> + dprintk(inst->dev->dev, "[%d] %s -> %s\n", >> + inst->id, >> + wave6_vpu_instance_state_name(inst->state), >> + wave6_vpu_instance_state_name(state)); >> + >> + inst->state = state; >> + if (state == VPU_INST_STATE_PIC_RUN && !inst- >> >performance.ts_first) >> + inst->performance.ts_first = ktime_get_raw(); >> +} >> + >> +u64 wave6_vpu_cycle_to_ns(struct vpu_device *vpu_dev, u64 cycle) >> +{ >> + if (!vpu_dev || !vpu_dev->clk_vpu || !clk_get_rate(vpu_dev- >> >clk_vpu)) >> + return 0; >> + >> + return (cycle * NSEC_PER_SEC) / clk_get_rate(vpu_dev- >> >clk_vpu); >> +} >> + >> +int wave6_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int >> timeout) >> +{ >> + int ret; >> + >> + ret = wait_for_completion_timeout(&inst->dev->irq_done, >> + >> msecs_to_jiffies(timeout)); >> + if (!ret) >> + return -ETIMEDOUT; >> + >> + reinit_completion(&inst->dev->irq_done); >> + >> + return 0; >> +} >> + >> +int wave6_vpu_subscribe_event(struct v4l2_fh *fh, >> + const struct v4l2_event_subscription >> *sub) >> +{ >> + struct vpu_instance *inst = wave6_to_vpu_inst(fh); >> + bool is_decoder = (inst->type == VPU_INST_TYPE_DEC) ? true : >> false; >> + >> + dev_dbg(inst->dev->dev, "%s: [%s] type: %d id: %d | flags: >> %d\n", >> + __func__, is_decoder ? "decoder" : "encoder", sub- >> >type, >> + sub->id, sub->flags); >> + >> + switch (sub->type) { >> + case V4L2_EVENT_SOURCE_CHANGE: >> + if (is_decoder) >> + return v4l2_src_change_event_subscribe(fh, >> sub); >> + return -EINVAL; >> + case V4L2_EVENT_CTRL: >> + return v4l2_ctrl_subscribe_event(fh, sub); >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +void wave6_vpu_return_buffers(struct vpu_instance *inst, >> + unsigned int type, enum >> vb2_buffer_state state) >> +{ >> + struct vb2_v4l2_buffer *buf; >> + int i; >> + >> + if (V4L2_TYPE_IS_OUTPUT(type)) { >> + while ((buf = v4l2_m2m_src_buf_remove(inst- >> >v4l2_fh.m2m_ctx))) >> + v4l2_m2m_buf_done(buf, state); >> + } else { >> + while ((buf = v4l2_m2m_dst_buf_remove(inst- >> >v4l2_fh.m2m_ctx))) { >> + for (i = 0; i < inst->dst_fmt.num_planes; >> i++) >> + vb2_set_plane_payload(&buf->vb2_buf, >> i, 0); >> + v4l2_m2m_buf_done(buf, state); >> + } >> + } >> +} >> + >> +u32 wave6_vpu_get_consumed_fb_num(struct vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *vb2_v4l2_buf; >> + struct v4l2_m2m_buffer *v4l2_m2m_buf; >> + struct vpu_buffer *vpu_buf; >> + u32 num = 0; >> + >> + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, >> v4l2_m2m_buf) { >> + vb2_v4l2_buf = &v4l2_m2m_buf->vb; >> + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); >> + if (vpu_buf->consumed) >> + num++; >> + } >> + >> + return num; >> +} >> + >> +u32 wave6_vpu_get_used_fb_num(struct vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *vb2_v4l2_buf; >> + struct v4l2_m2m_buffer *v4l2_m2m_buf; >> + struct vpu_buffer *vpu_buf; >> + u32 num = 0; >> + >> + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, >> v4l2_m2m_buf) { >> + vb2_v4l2_buf = &v4l2_m2m_buf->vb; >> + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); >> + if (vpu_buf->used) >> + num++; >> + } >> + >> + return num; >> +} >> + >> +static bool wave6_vpu_check_fb_available(struct vpu_instance *inst) >> +{ >> + struct vb2_v4l2_buffer *vb2_v4l2_buf; >> + struct v4l2_m2m_buffer *v4l2_m2m_buf; >> + struct vpu_buffer *vpu_buf; >> + >> + v4l2_m2m_for_each_dst_buf(inst->v4l2_fh.m2m_ctx, >> v4l2_m2m_buf) { >> + vb2_v4l2_buf = &v4l2_m2m_buf->vb; >> + vpu_buf = wave6_to_vpu_buf(vb2_v4l2_buf); >> + >> + if (!vpu_buf->used) >> + return true; >> + } >> + >> + return false; >> +} >> + >> +static int wave6_vpu_job_ready(void *priv) >> +{ >> + struct vpu_instance *inst = priv; >> + >> + dev_dbg(inst->dev->dev, "[%d]%s: state %d\n", >> + inst->id, __func__, inst->state); >> + >> + if (inst->type == VPU_INST_TYPE_DEC && inst->state == >> VPU_INST_STATE_OPEN) >> + return 1; >> + if (inst->state < VPU_INST_STATE_PIC_RUN) >> + return 0; >> + if (inst->state == VPU_INST_STATE_STOP && inst->eos) >> + return 0; >> + if (!wave6_vpu_check_fb_available(inst)) >> + return 0; >> + >> + return 1; >> +} >> + >> +static void wave6_vpu_device_run_timeout(struct work_struct *work) >> +{ >> + struct delayed_work *dwork = to_delayed_work(work); >> + struct vpu_device *dev = container_of(dwork, struct >> vpu_device, task_timer); >> + struct vpu_instance *inst = v4l2_m2m_get_curr_priv(dev- >> >m2m_dev); >> + struct vb2_v4l2_buffer *src_buf = NULL; >> + struct vb2_v4l2_buffer *dst_buf = NULL; >> + >> + if (!inst) >> + return; >> + >> + dev_err(inst->dev->dev, "[%d] sequence %d timeout\n", inst- >> >id, inst->sequence); >> + src_buf = v4l2_m2m_src_buf_remove(inst->v4l2_fh.m2m_ctx); >> + if (src_buf) { >> + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); >> + if (inst->type == VPU_INST_TYPE_DEC) >> + inst->processed_buf_num++; >> + inst->error_buf_num++; >> + } >> + >> + dst_buf = v4l2_m2m_dst_buf_remove(inst->v4l2_fh.m2m_ctx); >> + if (dst_buf) >> + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); >> + >> + vb2_queue_error(v4l2_m2m_get_src_vq(inst->v4l2_fh.m2m_ctx)); >> + vb2_queue_error(v4l2_m2m_get_dst_vq(inst->v4l2_fh.m2m_ctx)); >> + >> + v4l2_m2m_job_finish(inst->dev->m2m_dev, inst- >> >v4l2_fh.m2m_ctx); >> +} >> + >> +static void wave6_vpu_device_run(void *priv) >> +{ >> + struct vpu_instance *inst = priv; >> + int ret; >> + >> + dev_dbg(inst->dev->dev, "[%d]%s: state %d\n", >> + inst->id, __func__, inst->state); >> + >> + ret = inst->ops->start_process(inst); >> + if (!ret) >> + schedule_delayed_work(&inst->dev->task_timer, >> msecs_to_jiffies(W6_VPU_TIMEOUT)); >> + else >> + v4l2_m2m_job_finish(inst->dev->m2m_dev, inst- >> >v4l2_fh.m2m_ctx); >> +} >> + >> +void wave6_vpu_finish_job(struct vpu_instance *inst) >> +{ >> + cancel_delayed_work(&inst->dev->task_timer); >> + v4l2_m2m_job_finish(inst->dev->m2m_dev, inst- >> >v4l2_fh.m2m_ctx); >> +} >> + >> +void wave6_vpu_handle_performance(struct vpu_instance *inst, struct >> vpu_buffer *vpu_buf) >> +{ >> + s64 latency, time_spent; >> + >> + if (!inst || !vpu_buf) >> + return; >> + >> + inst->performance.ts_last = vpu_buf->ts_output; >> + >> + latency = vpu_buf->ts_output - vpu_buf->ts_input; >> + time_spent = vpu_buf->ts_finish - vpu_buf->ts_start; >> + >> + if (!inst->performance.latency_first) >> + inst->performance.latency_first = latency; >> + inst->performance.latency_max = max_t(s64, latency, inst- >> >performance.latency_max); >> + >> + if (!inst->performance.min_process_time) >> + inst->performance.min_process_time = time_spent; >> + else if (inst->performance.min_process_time > time_spent) >> + inst->performance.min_process_time = time_spent; >> + >> + if (inst->performance.max_process_time < time_spent) >> + inst->performance.max_process_time = time_spent; >> + >> + inst->performance.total_sw_time += time_spent; >> + inst->performance.total_hw_time += vpu_buf->hw_time; >> +} >> + >> +void wave6_vpu_reset_performance(struct vpu_instance *inst) >> +{ >> + if (!inst) >> + return; >> + >> + if (inst->processed_buf_num) { >> + s64 tmp; >> + s64 fps_act, fps_sw, fps_hw; >> + struct vpu_performance_info *perf = &inst- >> >performance; >> + >> + tmp = MSEC_PER_SEC * inst->processed_buf_num; >> + fps_act = DIV_ROUND_CLOSEST(tmp, (perf->ts_last - >> perf->ts_first) / NSEC_PER_MSEC); >> + fps_sw = DIV_ROUND_CLOSEST(tmp, perf->total_sw_time >> / NSEC_PER_MSEC); >> + fps_hw = DIV_ROUND_CLOSEST(tmp, perf->total_hw_time >> / NSEC_PER_MSEC); >> + dprintk(inst->dev->dev, >> + "[%d] fps actual: %lld, sw: %lld, hw: %lld, >> latency(ms) %llu.%06llu\n", >> + inst->id, fps_act, fps_sw, fps_hw, >> + perf->latency_first / NSEC_PER_MSEC, >> + perf->latency_first % NSEC_PER_MSEC); >> + } >> + >> + memset(&inst->performance, 0, sizeof(inst->performance)); >> +} >> + >> +static const struct v4l2_m2m_ops wave6_vpu_m2m_ops = { >> + .device_run = wave6_vpu_device_run, >> + .job_ready = wave6_vpu_job_ready, >> +}; >> + >> +int wave6_vpu_init_m2m_dev(struct vpu_device *dev) >> +{ >> + dev->m2m_dev = v4l2_m2m_init(&wave6_vpu_m2m_ops); >> + if (IS_ERR(dev->m2m_dev)) { >> + dev_err(dev->dev, "v4l2_m2m_init fail: %ld\n", >> PTR_ERR(dev->m2m_dev)); >> + return PTR_ERR(dev->m2m_dev); >> + } >> + >> + INIT_DELAYED_WORK(&dev->task_timer, >> wave6_vpu_device_run_timeout); >> + >> + return 0; >> +} >> + >> +void wave6_vpu_release_m2m_dev(struct vpu_device *dev) >> +{ >> + v4l2_m2m_release(dev->m2m_dev); >> +}