diff options
Diffstat (limited to 'drivers/media/platform/qcom/venus')
18 files changed, 1782 insertions, 704 deletions
diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index bfd4edf7c83f..b44b11b03e12 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -2,7 +2,8 @@ # Makefile for Qualcomm Venus driver venus-core-objs += core.o helpers.o firmware.o \ - hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o + hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ + hfi_parser.o venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 41eef376eb2d..bb6add9d340e 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -152,6 +152,83 @@ static void venus_clks_disable(struct venus_core *core) clk_disable_unprepare(core->clks[i]); } +static u32 to_v4l2_codec_type(u32 codec) +{ + switch (codec) { + case HFI_VIDEO_CODEC_H264: + return V4L2_PIX_FMT_H264; + case HFI_VIDEO_CODEC_H263: + return V4L2_PIX_FMT_H263; + case HFI_VIDEO_CODEC_MPEG1: + return V4L2_PIX_FMT_MPEG1; + case HFI_VIDEO_CODEC_MPEG2: + return V4L2_PIX_FMT_MPEG2; + case HFI_VIDEO_CODEC_MPEG4: + return V4L2_PIX_FMT_MPEG4; + case HFI_VIDEO_CODEC_VC1: + return V4L2_PIX_FMT_VC1_ANNEX_G; + case HFI_VIDEO_CODEC_VP8: + return V4L2_PIX_FMT_VP8; + case HFI_VIDEO_CODEC_VP9: + return V4L2_PIX_FMT_VP9; + case HFI_VIDEO_CODEC_DIVX: + case HFI_VIDEO_CODEC_DIVX_311: + return V4L2_PIX_FMT_XVID; + default: + return 0; + } +} + +static int venus_enumerate_codecs(struct venus_core *core, u32 type) +{ + const struct hfi_inst_ops dummy_ops = {}; + struct venus_inst *inst; + u32 codec, codecs; + unsigned int i; + int ret; + + if (core->res->hfi_version != HFI_VERSION_1XX) + return 0; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + mutex_init(&inst->lock); + inst->core = core; + inst->session_type = type; + if (type == VIDC_SESSION_TYPE_DEC) + codecs = core->dec_codecs; + else + codecs = core->enc_codecs; + + ret = hfi_session_create(inst, &dummy_ops); + if (ret) + goto err; + + for (i = 0; i < MAX_CODEC_NUM; i++) { + codec = (1 << i) & codecs; + if (!codec) + continue; + + ret = hfi_session_init(inst, to_v4l2_codec_type(codec)); + if (ret) + goto done; + + ret = hfi_session_deinit(inst); + if (ret) + goto done; + } + +done: + hfi_session_destroy(inst); +err: + mutex_destroy(&inst->lock); + kfree(inst); + + return ret; +} + static int venus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -219,6 +296,14 @@ static int venus_probe(struct platform_device *pdev) if (ret) goto err_venus_shutdown; + ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC); + if (ret) + goto err_venus_shutdown; + + ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC); + if (ret) + goto err_venus_shutdown; + ret = v4l2_device_register(dev, &core->v4l2_dev); if (ret) goto err_core_deinit; @@ -365,9 +450,31 @@ static const struct venus_resources msm8996_res = { .fwname = "qcom/venus-4.2/venus.mdt", }; +static const struct freq_tbl sdm845_freq_table[] = { + { 1944000, 380000000 }, /* 4k UHD @ 60 */ + { 972000, 320000000 }, /* 4k UHD @ 30 */ + { 489600, 200000000 }, /* 1080p @ 60 */ + { 244800, 100000000 }, /* 1080p @ 30 */ +}; + +static const struct venus_resources sdm845_res = { + .freq_tbl = sdm845_freq_table, + .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), + .clks = {"core", "iface", "bus" }, + .clks_num = 3, + .max_load = 2563200, + .hfi_version = HFI_VERSION_4XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/venus-5.2/venus.mdt", +}; + static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, + { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 0360d295f4c8..2f02365f4818 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -57,6 +57,30 @@ struct venus_format { u32 type; }; +#define MAX_PLANES 4 +#define MAX_FMT_ENTRIES 32 +#define MAX_CAP_ENTRIES 32 +#define MAX_ALLOC_MODE_ENTRIES 16 +#define MAX_CODEC_NUM 32 + +struct raw_formats { + u32 buftype; + u32 fmt; +}; + +struct venus_caps { + u32 codec; + u32 domain; + bool cap_bufs_mode_dynamic; + unsigned int num_caps; + struct hfi_capability caps[MAX_CAP_ENTRIES]; + unsigned int num_pl; + struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; + unsigned int num_fmts; + struct raw_formats fmts[MAX_FMT_ENTRIES]; + bool valid; /* used only for Venus v1xx */ +}; + /** * struct venus_core - holds core parameters valid for all instances * @@ -65,6 +89,8 @@ struct venus_format { * @clks: an array of struct clk pointers * @core0_clk: a struct clk pointer for core0 * @core1_clk: a struct clk pointer for core1 + * @core0_bus_clk: a struct clk pointer for core0 bus clock + * @core1_bus_clk: a struct clk pointer for core1 bus clock * @vdev_dec: a reference to video device structure for decoder instances * @vdev_enc: a reference to video device structure for encoder instances * @v4l2_dev: a holder for v4l2 device structure @@ -94,6 +120,8 @@ struct venus_core { struct clk *clks[VIDC_CLKS_NUM_MAX]; struct clk *core0_clk; struct clk *core1_clk; + struct clk *core0_bus_clk; + struct clk *core1_bus_clk; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -109,8 +137,8 @@ struct venus_core { unsigned int error; bool sys_error; const struct hfi_core_ops *core_ops; - u32 enc_codecs; - u32 dec_codecs; + unsigned long enc_codecs; + unsigned long dec_codecs; unsigned int max_sessions_supported; #define ENC_ROTATION_CAPABILITY 0x1 #define ENC_SCALING_CAPABILITY 0x2 @@ -120,6 +148,8 @@ struct venus_core { void *priv; const struct hfi_ops *ops; struct delayed_work work; + struct venus_caps caps[MAX_CODEC_NUM]; + unsigned int codecs_count; }; struct vdec_controls { @@ -160,10 +190,12 @@ struct venc_controls { u32 mpeg4; u32 h264; u32 vpx; + u32 hevc; } profile; struct { u32 mpeg4; u32 h264; + u32 hevc; } level; }; @@ -185,6 +217,7 @@ struct venus_buffer { * @list: used for attach an instance to the core * @lock: instance lock * @core: a reference to the core struct + * @dpbbufs: a list of decoded picture buffers * @internalbufs: a list of internal bufferes * @registeredbufs: a list of registered capture bufferes * @delayed_process a list of delayed buffers @@ -209,9 +242,15 @@ struct venus_buffer { * @num_output_bufs: holds number of output buffers * @input_buf_size holds input buffer size * @output_buf_size: holds output buffer size + * @output2_buf_size: holds secondary decoder output buffer size + * @dpb_buftype: decoded picture buffer type + * @dpb_fmt: decoded picture buffer raw format + * @opb_buftype: output picture buffer type + * @opb_fmt: output picture buffer raw format * @reconfig: a flag raised by decoder when the stream resolution changed * @reconfig_width: holds the new width * @reconfig_height: holds the new height + * @hfi_codec: current codec for this instance in HFI space * @sequence_cap: a sequence counter for capture queue * @sequence_out: a sequence counter for output queue * @m2m_dev: a reference to m2m device structure @@ -224,27 +263,12 @@ struct venus_buffer { * @priv: a private for HFI operations callbacks * @session_type: the type of the session (decoder or encoder) * @hprop: a union used as a holder by get property - * @cap_width: width capability - * @cap_height: height capability - * @cap_mbs_per_frame: macroblocks per frame capability - * @cap_mbs_per_sec: macroblocks per second capability - * @cap_framerate: framerate capability - * @cap_scale_x: horizontal scaling capability - * @cap_scale_y: vertical scaling capability - * @cap_bitrate: bitrate capability - * @cap_hier_p: hier capability - * @cap_ltr_count: LTR count capability - * @cap_secure_output2_threshold: secure OUTPUT2 threshold capability - * @cap_bufs_mode_static: buffers allocation mode capability - * @cap_bufs_mode_dynamic: buffers allocation mode capability - * @pl_count: count of supported profiles/levels - * @pl: supported profiles/levels - * @bufreq: holds buffer requirements */ struct venus_inst { struct list_head list; struct mutex lock; struct venus_core *core; + struct list_head dpbbufs; struct list_head internalbufs; struct list_head registeredbufs; struct list_head delayed_process; @@ -273,9 +297,15 @@ struct venus_inst { unsigned int num_output_bufs; unsigned int input_buf_size; unsigned int output_buf_size; + unsigned int output2_buf_size; + u32 dpb_buftype; + u32 dpb_fmt; + u32 opb_buftype; + u32 opb_fmt; bool reconfig; u32 reconfig_width; u32 reconfig_height; + u32 hfi_codec; u32 sequence_cap; u32 sequence_out; struct v4l2_m2m_dev *m2m_dev; @@ -287,24 +317,12 @@ struct venus_inst { const struct hfi_inst_ops *ops; u32 session_type; union hfi_get_property hprop; - struct hfi_capability cap_width; - struct hfi_capability cap_height; - struct hfi_capability cap_mbs_per_frame; - struct hfi_capability cap_mbs_per_sec; - struct hfi_capability cap_framerate; - struct hfi_capability cap_scale_x; - struct hfi_capability cap_scale_y; - struct hfi_capability cap_bitrate; - struct hfi_capability cap_hier_p; - struct hfi_capability cap_ltr_count; - struct hfi_capability cap_secure_output2_threshold; - bool cap_bufs_mode_static; - bool cap_bufs_mode_dynamic; - unsigned int pl_count; - struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; - struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX]; }; +#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) +#define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX) +#define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX) + #define ctrl_to_inst(ctrl) \ container_of((ctrl)->handler, struct venus_inst, ctrl_handler) @@ -318,4 +336,18 @@ static inline void *to_hfi_priv(struct venus_core *core) return core->priv; } +static inline struct venus_caps * +venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain) +{ + unsigned int c; + + for (c = 0; c < core->codecs_count; c++) { + if (core->caps[c].codec == codec && + core->caps[c].domain == domain) + return &core->caps[c]; + } + + return NULL; +} + #endif diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 0ce9559a2924..cd3b96e6f24b 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -13,6 +13,7 @@ * */ #include <linux/clk.h> +#include <linux/iopoll.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> @@ -24,6 +25,7 @@ #include "core.h" #include "helpers.h" #include "hfi_helper.h" +#include "hfi_venus_io.h" struct intbuf { struct list_head list; @@ -69,6 +71,9 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt) case V4L2_PIX_FMT_XVID: codec = HFI_VIDEO_CODEC_DIVX; break; + case V4L2_PIX_FMT_HEVC: + codec = HFI_VIDEO_CODEC_HEVC; + break; default: return false; } @@ -83,6 +88,106 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt) } EXPORT_SYMBOL_GPL(venus_helper_check_codec); +static int venus_helper_queue_dpb_bufs(struct venus_inst *inst) +{ + struct intbuf *buf; + int ret = 0; + + list_for_each_entry(buf, &inst->dpbbufs, list) { + struct hfi_frame_data fdata; + + memset(&fdata, 0, sizeof(fdata)); + fdata.alloc_len = buf->size; + fdata.device_addr = buf->da; + fdata.buffer_type = buf->type; + + ret = hfi_session_process_buf(inst, &fdata); + if (ret) + goto fail; + } + +fail: + return ret; +} + +int venus_helper_free_dpb_bufs(struct venus_inst *inst) +{ + struct intbuf *buf, *n; + + list_for_each_entry_safe(buf, n, &inst->dpbbufs, list) { + list_del_init(&buf->list); + dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da, + buf->attrs); + kfree(buf); + } + + INIT_LIST_HEAD(&inst->dpbbufs); + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_free_dpb_bufs); + +int venus_helper_alloc_dpb_bufs(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev; + enum hfi_version ver = core->res->hfi_version; + struct hfi_buffer_requirements bufreq; + u32 buftype = inst->dpb_buftype; + unsigned int dpb_size = 0; + struct intbuf *buf; + unsigned int i; + u32 count; + int ret; + + /* no need to allocate dpb buffers */ + if (!inst->dpb_fmt) + return 0; + + if (inst->dpb_buftype == HFI_BUFFER_OUTPUT) + dpb_size = inst->output_buf_size; + else if (inst->dpb_buftype == HFI_BUFFER_OUTPUT2) + dpb_size = inst->output2_buf_size; + + if (!dpb_size) + return 0; + + ret = venus_helper_get_bufreq(inst, buftype, &bufreq); + if (ret) + return ret; + + count = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + + for (i = 0; i < count; i++) { + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto fail; + } + + buf->type = buftype; + buf->size = dpb_size; + buf->attrs = DMA_ATTR_WRITE_COMBINE | + DMA_ATTR_NO_KERNEL_MAPPING; + buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL, + buf->attrs); + if (!buf->va) { + kfree(buf); + ret = -ENOMEM; + goto fail; + } + + list_add_tail(&buf->list, &inst->dpbbufs); + } + + return 0; + +fail: + venus_helper_free_dpb_bufs(inst); + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_alloc_dpb_bufs); + static int intbufs_set_buffer(struct venus_inst *inst, u32 type) { struct venus_core *core = inst->core; @@ -166,21 +271,38 @@ static int intbufs_unset_buffers(struct venus_inst *inst) return ret; } -static const unsigned int intbuf_types[] = { - HFI_BUFFER_INTERNAL_SCRATCH, - HFI_BUFFER_INTERNAL_SCRATCH_1, - HFI_BUFFER_INTERNAL_SCRATCH_2, +static const unsigned int intbuf_types_1xx[] = { + HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_PERSIST, + HFI_BUFFER_INTERNAL_PERSIST_1, +}; + +static const unsigned int intbuf_types_4xx[] = { + HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_4XX), + HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_4XX), + HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_4XX), HFI_BUFFER_INTERNAL_PERSIST, HFI_BUFFER_INTERNAL_PERSIST_1, }; static int intbufs_alloc(struct venus_inst *inst) { - unsigned int i; + const unsigned int *intbuf; + size_t arr_sz, i; int ret; - for (i = 0; i < ARRAY_SIZE(intbuf_types); i++) { - ret = intbufs_set_buffer(inst, intbuf_types[i]); + if (IS_V4(inst->core)) { + arr_sz = ARRAY_SIZE(intbuf_types_4xx); + intbuf = intbuf_types_4xx; + } else { + arr_sz = ARRAY_SIZE(intbuf_types_1xx); + intbuf = intbuf_types_1xx; + } + + for (i = 0; i < arr_sz; i++) { + ret = intbufs_set_buffer(inst, intbuf[i]); if (ret) goto error; } @@ -257,20 +379,23 @@ static int load_scale_clocks(struct venus_core *core) set_freq: - if (core->res->hfi_version == HFI_VERSION_3XX) { - ret = clk_set_rate(clk, freq); - ret |= clk_set_rate(core->core0_clk, freq); - ret |= clk_set_rate(core->core1_clk, freq); - } else { - ret = clk_set_rate(clk, freq); - } + ret = clk_set_rate(clk, freq); + if (ret) + goto err; - if (ret) { - dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); - return ret; - } + ret = clk_set_rate(core->core0_clk, freq); + if (ret) + goto err; + + ret = clk_set_rate(core->core1_clk, freq); + if (ret) + goto err; return 0; + +err: + dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); + return ret; } static void fill_buffer_desc(const struct venus_buffer *buf, @@ -325,7 +450,10 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) if (vbuf->flags & V4L2_BUF_FLAG_LAST || !fdata.filled_len) fdata.flags |= HFI_BUFFERFLAG_EOS; } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - fdata.buffer_type = HFI_BUFFER_OUTPUT; + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + fdata.buffer_type = HFI_BUFFER_OUTPUT; + else + fdata.buffer_type = inst->opb_buftype; fdata.filled_len = 0; fdata.offset = 0; } @@ -337,18 +465,16 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) return 0; } -static inline int is_reg_unreg_needed(struct venus_inst *inst) +static bool is_dynamic_bufmode(struct venus_inst *inst) { - if (inst->session_type == VIDC_SESSION_TYPE_DEC && - inst->core->res->hfi_version == HFI_VERSION_3XX) - return 0; + struct venus_core *core = inst->core; + struct venus_caps *caps; - if (inst->session_type == VIDC_SESSION_TYPE_DEC && - inst->cap_bufs_mode_dynamic && - inst->core->res->hfi_version == HFI_VERSION_1XX) + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) return 0; - return 1; + return caps->cap_bufs_mode_dynamic; } static int session_unregister_bufs(struct venus_inst *inst) @@ -357,7 +483,7 @@ static int session_unregister_bufs(struct venus_inst *inst) struct hfi_buffer_desc bd; int ret = 0; - if (!is_reg_unreg_needed(inst)) + if (is_dynamic_bufmode(inst)) return 0; list_for_each_entry_safe(buf, n, &inst->registeredbufs, reg_list) { @@ -377,7 +503,7 @@ static int session_register_bufs(struct venus_inst *inst) struct venus_buffer *buf; int ret = 0; - if (!is_reg_unreg_needed(inst)) + if (is_dynamic_bufmode(inst)) return 0; list_for_each_entry(buf, &inst->registeredbufs, reg_list) { @@ -392,6 +518,20 @@ static int session_register_bufs(struct venus_inst *inst) return ret; } +static u32 to_hfi_raw_fmt(u32 v4l2_fmt) +{ + switch (v4l2_fmt) { + case V4L2_PIX_FMT_NV12: + return HFI_COLOR_FORMAT_NV12; + case V4L2_PIX_FMT_NV21: + return HFI_COLOR_FORMAT_NV21; + default: + break; + } + + return 0; +} + int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req) { @@ -423,6 +563,104 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, } EXPORT_SYMBOL_GPL(venus_helper_get_bufreq); +static u32 get_framesize_raw_nv12(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_plane; + u32 y_sclines, uv_sclines, uv_plane; + u32 size; + + y_stride = ALIGN(width, 128); + uv_stride = ALIGN(width, 128); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN(((height + 1) >> 1), 16); + + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + SZ_4K; + size = y_plane + uv_plane + SZ_8K; + + return ALIGN(size, SZ_4K); +} + +static u32 get_framesize_raw_nv12_ubwc(u32 width, u32 height) +{ + u32 y_meta_stride, y_meta_plane; + u32 y_stride, y_plane; + u32 uv_meta_stride, uv_meta_plane; + u32 uv_stride, uv_plane; + u32 extradata = SZ_16K; + + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64); + y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(height, 8), 16); + y_meta_plane = ALIGN(y_meta_plane, SZ_4K); + + y_stride = ALIGN(width, 128); + y_plane = ALIGN(y_stride * ALIGN(height, 32), SZ_4K); + + uv_meta_stride = ALIGN(DIV_ROUND_UP(width / 2, 16), 64); + uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(height / 2, 8), 16); + uv_meta_plane = ALIGN(uv_meta_plane, SZ_4K); + + uv_stride = ALIGN(width, 128); + uv_plane = ALIGN(uv_stride * ALIGN(height / 2, 32), SZ_4K); + + return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane + + max(extradata, y_stride * 48), SZ_4K); +} + +u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) +{ + switch (hfi_fmt) { + case HFI_COLOR_FORMAT_NV12: + case HFI_COLOR_FORMAT_NV21: + return get_framesize_raw_nv12(width, height); + case HFI_COLOR_FORMAT_NV12_UBWC: + return get_framesize_raw_nv12_ubwc(width, height); + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(venus_helper_get_framesz_raw); + +u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height) +{ + u32 hfi_fmt, sz; + bool compressed; + + switch (v4l2_fmt) { + case V4L2_PIX_FMT_MPEG: + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_H264_NO_SC: + case V4L2_PIX_FMT_H264_MVC: + case V4L2_PIX_FMT_H263: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_XVID: + case V4L2_PIX_FMT_VC1_ANNEX_G: + case V4L2_PIX_FMT_VC1_ANNEX_L: + case V4L2_PIX_FMT_VP8: + case V4L2_PIX_FMT_VP9: + case V4L2_PIX_FMT_HEVC: + compressed = true; + break; + default: + compressed = false; + break; + } + + if (compressed) { + sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; + return ALIGN(sz, SZ_4K); + } + + hfi_fmt = to_hfi_raw_fmt(v4l2_fmt); + if (!hfi_fmt) + return 0; + + return venus_helper_get_framesz_raw(hfi_fmt, width, height); +} +EXPORT_SYMBOL_GPL(venus_helper_get_framesz); + int venus_helper_set_input_resolution(struct venus_inst *inst, unsigned int width, unsigned int height) { @@ -438,12 +676,13 @@ int venus_helper_set_input_resolution(struct venus_inst *inst, EXPORT_SYMBOL_GPL(venus_helper_set_input_resolution); int venus_helper_set_output_resolution(struct venus_inst *inst, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + u32 buftype) { u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE; struct hfi_framesize fs; - fs.buffer_type = HFI_BUFFER_OUTPUT; + fs.buffer_type = buftype; fs.width = width; fs.height = height; @@ -451,8 +690,37 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, } EXPORT_SYMBOL_GPL(venus_helper_set_output_resolution); +int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) +{ + const u32 ptype = HFI_PROPERTY_PARAM_WORK_MODE; + struct hfi_video_work_mode wm; + + if (!IS_V4(inst->core)) + return 0; + + wm.video_work_mode = mode; + + return hfi_session_set_property(inst, ptype, &wm); +} +EXPORT_SYMBOL_GPL(venus_helper_set_work_mode); + +int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) +{ + const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; + struct hfi_videocores_usage_type cu; + + if (!IS_V4(inst->core)) + return 0; + + cu.video_core_enable_mask = usage; + + return hfi_session_set_property(inst, ptype, &cu); +} +EXPORT_SYMBOL_GPL(venus_helper_set_core_usage); + int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, - unsigned int output_bufs) + unsigned int output_bufs, + unsigned int output2_bufs) { u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL; struct hfi_buffer_count_actual buf_count; @@ -468,41 +736,122 @@ int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, buf_count.type = HFI_BUFFER_OUTPUT; buf_count.count_actual = output_bufs; - return hfi_session_set_property(inst, ptype, &buf_count); + ret = hfi_session_set_property(inst, ptype, &buf_count); + if (ret) + return ret; + + if (output2_bufs) { + buf_count.type = HFI_BUFFER_OUTPUT2; + buf_count.count_actual = output2_bufs; + + ret = hfi_session_set_property(inst, ptype, &buf_count); + } + + return ret; } EXPORT_SYMBOL_GPL(venus_helper_set_num_bufs); -int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) +int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, + u32 buftype) { + const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; struct hfi_uncompressed_format_select fmt; - u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; - int ret; + + fmt.buffer_type = buftype; + fmt.format = hfi_format; + + return hfi_session_set_property(inst, ptype, &fmt); +} +EXPORT_SYMBOL_GPL(venus_helper_set_raw_format); + +int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) +{ + u32 hfi_format, buftype; if (inst->session_type == VIDC_SESSION_TYPE_DEC) - fmt.buffer_type = HFI_BUFFER_OUTPUT; + buftype = HFI_BUFFER_OUTPUT; else if (inst->session_type == VIDC_SESSION_TYPE_ENC) - fmt.buffer_type = HFI_BUFFER_INPUT; + buftype = HFI_BUFFER_INPUT; else return -EINVAL; - switch (pixfmt) { - case V4L2_PIX_FMT_NV12: - fmt.format = HFI_COLOR_FORMAT_NV12; - break; - case V4L2_PIX_FMT_NV21: - fmt.format = HFI_COLOR_FORMAT_NV21; - break; - default: + hfi_format = to_hfi_raw_fmt(pixfmt); + if (!hfi_format) return -EINVAL; - } - ret = hfi_session_set_property(inst, ptype, &fmt); + return venus_helper_set_raw_format(inst, hfi_format, buftype); +} +EXPORT_SYMBOL_GPL(venus_helper_set_color_format); + +int venus_helper_set_multistream(struct venus_inst *inst, bool out_en, + bool out2_en) +{ + struct hfi_multi_stream multi = {0}; + u32 ptype = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + int ret; + + multi.buffer_type = HFI_BUFFER_OUTPUT; + multi.enable = out_en; + + ret = hfi_session_set_property(inst, ptype, &multi); + if (ret) + return ret; + + multi.buffer_type = HFI_BUFFER_OUTPUT2; + multi.enable = out2_en; + + return hfi_session_set_property(inst, ptype, &multi); +} +EXPORT_SYMBOL_GPL(venus_helper_set_multistream); + +int venus_helper_set_dyn_bufmode(struct venus_inst *inst) +{ + const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; + struct hfi_buffer_alloc_mode mode; + int ret; + + if (!is_dynamic_bufmode(inst)) + return 0; + + mode.type = HFI_BUFFER_OUTPUT; + mode.mode = HFI_BUFFER_MODE_DYNAMIC; + + ret = hfi_session_set_property(inst, ptype, &mode); if (ret) return ret; + mode.type = HFI_BUFFER_OUTPUT2; + + return hfi_session_set_property(inst, ptype, &mode); +} +EXPORT_SYMBOL_GPL(venus_helper_set_dyn_bufmode); + +int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype) +{ + const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; + struct hfi_buffer_size_actual bufsz; + + bufsz.type = buftype; + bufsz.size = bufsize; + + return hfi_session_set_property(inst, ptype, &bufsz); +} +EXPORT_SYMBOL_GPL(venus_helper_set_bufsize); + +unsigned int venus_helper_get_opb_size(struct venus_inst *inst) +{ + /* the encoder has only one output */ + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + return inst->output_buf_size; + + if (inst->opb_buftype == HFI_BUFFER_OUTPUT) + return inst->output_buf_size; + else if (inst->opb_buftype == HFI_BUFFER_OUTPUT2) + return inst->output2_buf_size; + return 0; } -EXPORT_SYMBOL_GPL(venus_helper_set_color_format); +EXPORT_SYMBOL_GPL(venus_helper_get_opb_size); static void delayed_process_buf_func(struct work_struct *work) { @@ -602,9 +951,10 @@ EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_init); int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb) { struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + unsigned int out_buf_size = venus_helper_get_opb_size(inst); if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - vb2_plane_size(vb, 0) < inst->output_buf_size) + vb2_plane_size(vb, 0) < out_buf_size) return -EINVAL; if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_plane_size(vb, 0) < inst->input_buf_size) @@ -674,6 +1024,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) if (ret) hfi_session_abort(inst); + venus_helper_free_dpb_bufs(inst); + load_scale_clocks(core); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -712,8 +1064,14 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_unload_res; + ret = venus_helper_queue_dpb_bufs(inst); + if (ret) + goto err_session_stop; + return 0; +err_session_stop: + hfi_session_stop(inst); err_unload_res: hfi_session_unload_res(inst); err_unreg_bufs: @@ -766,3 +1124,113 @@ void venus_helper_init_instance(struct venus_inst *inst) } } EXPORT_SYMBOL_GPL(venus_helper_init_instance); + +static bool find_fmt_from_caps(struct venus_caps *caps, u32 buftype, u32 fmt) +{ + unsigned int i; + + for (i = 0; i < caps->num_fmts; i++) { + if (caps->fmts[i].buftype == buftype && + caps->fmts[i].fmt == fmt) + return true; + } + + return false; +} + +int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, + u32 *out_fmt, u32 *out2_fmt, bool ubwc) +{ + struct venus_core *core = inst->core; + struct venus_caps *caps; + u32 ubwc_fmt, fmt = to_hfi_raw_fmt(v4l2_fmt); + bool found, found_ubwc; + + *out_fmt = *out2_fmt = 0; + + if (!fmt) + return -EINVAL; + + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) + return -EINVAL; + + if (ubwc) { + ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE; + found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, + ubwc_fmt); + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt); + + if (found_ubwc && found) { + *out_fmt = ubwc_fmt; + *out2_fmt = fmt; + return 0; + } + } + + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, fmt); + if (found) { + *out_fmt = fmt; + *out2_fmt = 0; + return 0; + } + + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt); + if (found) { + *out_fmt = 0; + *out2_fmt = fmt; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts); + +int venus_helper_power_enable(struct venus_core *core, u32 session_type, + bool enable) +{ + void __iomem *ctrl, *stat; + u32 val; + int ret; + + if (!IS_V3(core) && !IS_V4(core)) + return 0; + + if (IS_V3(core)) { + if (session_type == VIDC_SESSION_TYPE_DEC) + ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; + else + ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL; + if (enable) + writel(0, ctrl); + else + writel(1, ctrl); + + return 0; + } + + if (session_type == VIDC_SESSION_TYPE_DEC) { + ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; + } else { + ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; + } + + if (enable) { + writel(0, ctrl); + + ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100); + if (ret) + return ret; + } else { + writel(1, ctrl); + + ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_power_enable); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 971392be5df5..2475f284f396 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -33,14 +33,33 @@ void venus_helper_m2m_device_run(void *priv); void venus_helper_m2m_job_abort(void *priv); int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req); +u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height); +u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height); int venus_helper_set_input_resolution(struct venus_inst *inst, unsigned int width, unsigned int height); int venus_helper_set_output_resolution(struct venus_inst *inst, - unsigned int width, unsigned int height); + unsigned int width, unsigned int height, + u32 buftype); +int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); +int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, - unsigned int output_bufs); + unsigned int output_bufs, + unsigned int output2_bufs); +int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, + u32 buftype); int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); +int venus_helper_set_dyn_bufmode(struct venus_inst *inst); +int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype); +int venus_helper_set_multistream(struct venus_inst *inst, bool out_en, + bool out2_en); +unsigned int venus_helper_get_opb_size(struct venus_inst *inst); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); +int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt, + u32 *out2_fmt, bool ubwc); +int venus_helper_alloc_dpb_bufs(struct venus_inst *inst); +int venus_helper_free_dpb_bufs(struct venus_inst *inst); +int venus_helper_power_enable(struct venus_core *core, u32 session_type, + bool enable); #endif diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index bca894a00c07..24207829982f 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -49,6 +49,8 @@ static u32 to_codec_type(u32 pixfmt) return HFI_VIDEO_CODEC_VP9; case V4L2_PIX_FMT_XVID: return HFI_VIDEO_CODEC_DIVX; + case V4L2_PIX_FMT_HEVC: + return HFI_VIDEO_CODEC_HEVC; default: return 0; } @@ -203,13 +205,12 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt) { struct venus_core *core = inst->core; const struct hfi_ops *ops = core->ops; - u32 codec; int ret; - codec = to_codec_type(pixfmt); + inst->hfi_codec = to_codec_type(pixfmt); reinit_completion(&inst->done); - ret = ops->session_init(inst, inst->session_type, codec); + ret = ops->session_init(inst, inst->session_type, inst->hfi_codec); if (ret) return ret; @@ -312,7 +313,7 @@ int hfi_session_continue(struct venus_inst *inst) { struct venus_core *core = inst->core; - if (core->res->hfi_version != HFI_VERSION_3XX) + if (core->res->hfi_version == HFI_VERSION_1XX) return 0; return core->ops->session_continue(inst); @@ -473,7 +474,8 @@ int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd) if (fd->buffer_type == HFI_BUFFER_INPUT) return ops->session_etb(inst, fd); - else if (fd->buffer_type == HFI_BUFFER_OUTPUT) + else if (fd->buffer_type == HFI_BUFFER_OUTPUT || + fd->buffer_type == HFI_BUFFER_OUTPUT2) return ops->session_ftb(inst, fd); return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index 5466b7d60dd0..6038d8e0ab22 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -74,6 +74,16 @@ struct hfi_event_data { u32 tag; u32 profile; u32 level; + /* the following properties start appear from v4 onwards */ + u32 bit_depth; + u32 pic_struct; + u32 colour_space; + u32 entropy_mode; + u32 buf_count; + struct { + u32 left, top; + u32 width, height; + } input_crop; }; /* define core states */ diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 1cfeb7743041..e8389d8d8c48 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1166,6 +1166,63 @@ pkt_session_set_property_3xx(struct hfi_session_set_property_pkt *pkt, return ret; } +static int +pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata) +{ + void *prop_data; + + if (!pkt || !cookie || !pdata) + return -EINVAL; + + prop_data = &pkt->data[1]; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + pkt->data[0] = ptype; + + /* + * Any session set property which is different in 3XX packetization + * should be added as a new case below. All unchanged session set + * properties will be handled in the default case. + */ + switch (ptype) { + case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: { + struct hfi_buffer_count_actual *in = pdata; + struct hfi_buffer_count_actual_4xx *count = prop_data; + + count->count_actual = in->count_actual; + count->type = in->type; + count->count_min_host = in->count_actual; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count); + break; + } + case HFI_PROPERTY_PARAM_WORK_MODE: { + struct hfi_video_work_mode *in = pdata, *wm = prop_data; + + wm->video_work_mode = in->video_work_mode; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*wm); + break; + } + case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: { + struct hfi_videocores_usage_type *in = pdata, *cu = prop_data; + + cu->video_core_enable_mask = in->video_core_enable_mask; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: + /* not implemented on Venus 4xx */ + break; + default: + return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + } + + return 0; +} + int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt, void *cookie, u32 ptype) { @@ -1181,7 +1238,10 @@ int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt, if (hfi_ver == HFI_VERSION_1XX) return pkt_session_set_property_1x(pkt, cookie, ptype, pdata); - return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + if (hfi_ver == HFI_VERSION_3XX) + return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + + return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata); } void pkt_set_version(enum hfi_version version) diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 55d8eb21403a..15804ad7e65d 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -121,6 +121,7 @@ #define HFI_EXTRADATA_METADATA_FILLER 0x7fe00002 #define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e +#define HFI_INDEX_EXTRADATA_OUTPUT_CROP 0x0700000f #define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010 #define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7f100003 @@ -376,13 +377,18 @@ #define HFI_BUFFER_OUTPUT2 0x3 #define HFI_BUFFER_INTERNAL_PERSIST 0x4 #define HFI_BUFFER_INTERNAL_PERSIST_1 0x5 -#define HFI_BUFFER_INTERNAL_SCRATCH 0x1000001 -#define HFI_BUFFER_EXTRADATA_INPUT 0x1000002 -#define HFI_BUFFER_EXTRADATA_OUTPUT 0x1000003 -#define HFI_BUFFER_EXTRADATA_OUTPUT2 0x1000004 -#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x1000005 -#define HFI_BUFFER_INTERNAL_SCRATCH_2 0x1000006 - +#define HFI_BUFFER_INTERNAL_SCRATCH(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x6 : 0x1000001) +#define HFI_BUFFER_INTERNAL_SCRATCH_1(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x7 : 0x1000005) +#define HFI_BUFFER_INTERNAL_SCRATCH_2(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x8 : 0x1000006) +#define HFI_BUFFER_EXTRADATA_INPUT(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xc : 0x1000002) +#define HFI_BUFFER_EXTRADATA_OUTPUT(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xa : 0x1000003) +#define HFI_BUFFER_EXTRADATA_OUTPUT2(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xb : 0x1000004) #define HFI_BUFFER_TYPE_MAX 11 #define HFI_BUFFER_MODE_STATIC 0x1000001 @@ -424,12 +430,14 @@ #define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED 0x100e #define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT 0x100f #define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED 0x1010 +#define HFI_PROPERTY_PARAM_WORK_MODE 0x1015 /* * HFI_PROPERTY_CONFIG_COMMON_START * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000 */ #define HFI_PROPERTY_CONFIG_FRAME_RATE 0x2001 +#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE 0x2002 /* * HFI_PROPERTY_PARAM_VDEC_COMMON_START @@ -438,6 +446,9 @@ #define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001 #define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR 0x1003002 #define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 0x1003003 +#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH 0x1003007 +#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT 0x1003009 +#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE 0x100300a /* * HFI_PROPERTY_CONFIG_VDEC_COMMON_START @@ -518,6 +529,7 @@ enum hfi_version { HFI_VERSION_1XX, HFI_VERSION_3XX, + HFI_VERSION_4XX }; struct hfi_buffer_info { @@ -767,12 +779,56 @@ struct hfi_framesize { u32 height; }; +#define VIDC_CORE_ID_DEFAULT 0 +#define VIDC_CORE_ID_1 1 +#define VIDC_CORE_ID_2 2 +#define VIDC_CORE_ID_3 3 + +struct hfi_videocores_usage_type { + u32 video_core_enable_mask; +}; + +#define VIDC_WORK_MODE_1 1 +#define VIDC_WORK_MODE_2 2 + +struct hfi_video_work_mode { + u32 video_work_mode; +}; + struct hfi_h264_vui_timing_info { u32 enable; u32 fixed_framerate; u32 time_scale; }; +struct hfi_bit_depth { + u32 buffer_type; + u32 bit_depth; +}; + +struct hfi_picture_type { + u32 is_sync_frame; + u32 picture_type; +}; + +struct hfi_pic_struct { + u32 progressive_only; +}; + +struct hfi_colour_space { + u32 colour_space; +}; + +struct hfi_extradata_input_crop { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 width; + u32 height; +}; + #define HFI_COLOR_FORMAT_MONOCHROME 0x01 #define HFI_COLOR_FORMAT_NV12 0x02 #define HFI_COLOR_FORMAT_NV21 0x03 @@ -802,10 +858,23 @@ struct hfi_uncompressed_format_select { u32 format; }; +struct hfi_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hfi_uncompressed_plane_info { + u32 format; + u32 num_planes; + struct hfi_uncompressed_plane_constraints plane_constraints[1]; +}; + struct hfi_uncompressed_format_supported { u32 buffer_type; u32 format_entries; - u32 format_info[1]; + struct hfi_uncompressed_plane_info plane_info[1]; }; struct hfi_uncompressed_plane_actual { @@ -819,19 +888,6 @@ struct hfi_uncompressed_plane_actual_info { struct hfi_uncompressed_plane_actual plane_format[1]; }; -struct hfi_uncompressed_plane_constraints { - u32 stride_multiples; - u32 max_stride; - u32 min_plane_buffer_height_multiple; - u32 buffer_alignment; -}; - -struct hfi_uncompressed_plane_info { - u32 format; - u32 num_planes; - struct hfi_uncompressed_plane_constraints plane_format[1]; -}; - struct hfi_uncompressed_plane_actual_constraints_info { u32 buffer_type; u32 num_planes; @@ -961,6 +1017,12 @@ struct hfi_buffer_count_actual { u32 count_actual; }; +struct hfi_buffer_count_actual_4xx { + u32 type; + u32 count_actual; + u32 count_min_host; +}; + struct hfi_buffer_size_actual { u32 type; u32 size; @@ -971,6 +1033,14 @@ struct hfi_buffer_display_hold_count_actual { u32 hold_count; }; +/* HFI 4XX reorder the fields, use these macros */ +#define HFI_BUFREQ_HOLD_COUNT(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? 0 : (bufreq)->hold_count) +#define HFI_BUFREQ_COUNT_MIN(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? (bufreq)->hold_count : (bufreq)->count_min) +#define HFI_BUFREQ_COUNT_MIN_HOST(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? (bufreq)->count_min : 0) + struct hfi_buffer_requirements { u32 type; u32 size; diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 90c93d9603dc..0ecdaa15c296 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -21,14 +21,21 @@ #include "hfi.h" #include "hfi_helper.h" #include "hfi_msgs.h" +#include "hfi_parser.h" static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, struct hfi_msg_event_notify_pkt *pkt) { + enum hfi_version ver = core->res->hfi_version; struct hfi_event_data event = {0}; int num_properties_changed; struct hfi_framesize *frame_sz; struct hfi_profile_level *profile_level; + struct hfi_bit_depth *pixel_depth; + struct hfi_pic_struct *pic_struct; + struct hfi_colour_space *colour_info; + struct hfi_buffer_requirements *bufreq; + struct hfi_extradata_input_crop *crop; u8 *data_ptr; u32 ptype; @@ -60,14 +67,52 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, frame_sz = (struct hfi_framesize *)data_ptr; event.width = frame_sz->width; event.height = frame_sz->height; - data_ptr += sizeof(frame_sz); + data_ptr += sizeof(*frame_sz); break; case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: data_ptr += sizeof(u32); profile_level = (struct hfi_profile_level *)data_ptr; event.profile = profile_level->profile; event.level = profile_level->level; - data_ptr += sizeof(profile_level); + data_ptr += sizeof(*profile_level); + break; + case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH: + data_ptr += sizeof(u32); + pixel_depth = (struct hfi_bit_depth *)data_ptr; + event.bit_depth = pixel_depth->bit_depth; + data_ptr += sizeof(*pixel_depth); + break; + case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT: + data_ptr += sizeof(u32); + pic_struct = (struct hfi_pic_struct *)data_ptr; + event.pic_struct = pic_struct->progressive_only; + data_ptr += sizeof(*pic_struct); + break; + case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE: + data_ptr += sizeof(u32); + colour_info = (struct hfi_colour_space *)data_ptr; + event.colour_space = colour_info->colour_space; + data_ptr += sizeof(*colour_info); + break; + case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: + data_ptr += sizeof(u32); + event.entropy_mode = *(u32 *)data_ptr; + data_ptr += sizeof(u32); + break; + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + data_ptr += sizeof(u32); + bufreq = (struct hfi_buffer_requirements *)data_ptr; + event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver); + data_ptr += sizeof(*bufreq); + break; + case HFI_INDEX_EXTRADATA_INPUT_CROP: + data_ptr += sizeof(u32); + crop = (struct hfi_extradata_input_crop *)data_ptr; + event.input_crop.left = crop->left; + event.input_crop.top = crop->top; + event.input_crop.width = crop->width; + event.input_crop.height = crop->height; + data_ptr += sizeof(*crop); break; default: break; @@ -173,81 +218,28 @@ static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst, void *packet) { struct hfi_msg_sys_init_done_pkt *pkt = packet; - u32 rem_bytes, read_bytes = 0, num_properties; - u32 error, ptype; - u8 *data; + int rem_bytes; + u32 error; error = pkt->error_type; if (error != HFI_ERR_NONE) - goto err_no_prop; - - num_properties = pkt->num_properties; + goto done; - if (!num_properties) { + if (!pkt->num_properties) { error = HFI_ERR_SYS_INVALID_PARAMETER; - goto err_no_prop; + goto done; } rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32); - - if (!rem_bytes) { + if (rem_bytes <= 0) { /* missing property data */ error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - goto err_no_prop; + goto done; } - data = (u8 *)&pkt->data[0]; - - if (core->res->hfi_version == HFI_VERSION_3XX) - goto err_no_prop; - - while (num_properties && rem_bytes >= sizeof(u32)) { - ptype = *((u32 *)data); - data += sizeof(u32); + error = hfi_parser(core, inst, pkt->data, rem_bytes); - switch (ptype) { - case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: { - struct hfi_codec_supported *prop; - - prop = (struct hfi_codec_supported *)data; - - if (rem_bytes < sizeof(*prop)) { - error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - break; - } - - read_bytes += sizeof(*prop) + sizeof(u32); - core->dec_codecs = prop->dec_codecs; - core->enc_codecs = prop->enc_codecs; - break; - } - case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: { - struct hfi_max_sessions_supported *prop; - - if (rem_bytes < sizeof(*prop)) { - error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - break; - } - - prop = (struct hfi_max_sessions_supported *)data; - read_bytes += sizeof(*prop) + sizeof(u32); - core->max_sessions_supported = prop->max_sessions; - break; - } - default: - error = HFI_ERR_SYS_INVALID_PARAMETER; - break; - } - - if (error) - break; - - rem_bytes -= read_bytes; - data += read_bytes; - num_properties--; - } - -err_no_prop: +done: core->error = error; complete(&core->done); } @@ -325,51 +317,6 @@ static void hfi_sys_pc_prepare_done(struct venus_core *core, dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type); } -static void -hfi_copy_cap_prop(struct hfi_capability *in, struct venus_inst *inst) -{ - if (!in || !inst) - return; - - switch (in->capability_type) { - case HFI_CAPABILITY_FRAME_WIDTH: - inst->cap_width = *in; - break; - case HFI_CAPABILITY_FRAME_HEIGHT: - inst->cap_height = *in; - break; - case HFI_CAPABILITY_MBS_PER_FRAME: - inst->cap_mbs_per_frame = *in; - break; - case HFI_CAPABILITY_MBS_PER_SECOND: - inst->cap_mbs_per_sec = *in; - break; - case HFI_CAPABILITY_FRAMERATE: - inst->cap_framerate = *in; - break; - case HFI_CAPABILITY_SCALE_X: - inst->cap_scale_x = *in; - break; - case HFI_CAPABILITY_SCALE_Y: - inst->cap_scale_y = *in; - break; - case HFI_CAPABILITY_BITRATE: - inst->cap_bitrate = *in; - break; - case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: - inst->cap_hier_p = *in; - break; - case HFI_CAPABILITY_ENC_LTR_COUNT: - inst->cap_ltr_count = *in; - break; - case HFI_CAPABILITY_CP_OUTPUT2_THRESH: - inst->cap_secure_output2_threshold = *in; - break; - default: - break; - } -} - static unsigned int session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt, struct hfi_profile_level *profile_level) @@ -459,248 +406,27 @@ done: complete(&inst->done); } -static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst, - struct hfi_msg_session_init_done_pkt *pkt) -{ - struct device *dev = core->dev; - u32 rem_bytes, num_props; - u32 ptype, next_offset = 0; - u32 err; - u8 *data; - - rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32); - if (!rem_bytes) { - dev_err(dev, "%s: missing property info\n", __func__); - return HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; - } - - err = pkt->error_type; - if (err) - return err; - - data = (u8 *)&pkt->data[0]; - num_props = pkt->num_properties; - - while (err == HFI_ERR_NONE && num_props && rem_bytes >= sizeof(u32)) { - ptype = *((u32 *)data); - next_offset = sizeof(u32); - - switch (ptype) { - case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: { - struct hfi_codec_mask_supported *masks = - (struct hfi_codec_mask_supported *) - (data + next_offset); - - next_offset += sizeof(*masks); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: { - struct hfi_capabilities *caps; - struct hfi_capability *cap; - u32 num_caps; - - if ((rem_bytes - next_offset) < sizeof(*cap)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - caps = (struct hfi_capabilities *)(data + next_offset); - - num_caps = caps->num_capabilities; - cap = &caps->data[0]; - next_offset += sizeof(u32); - - while (num_caps && - (rem_bytes - next_offset) >= sizeof(u32)) { - hfi_copy_cap_prop(cap, inst); - cap++; - next_offset += sizeof(*cap); - num_caps--; - } - num_props--; - break; - } - case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: { - struct hfi_uncompressed_format_supported *prop = - (struct hfi_uncompressed_format_supported *) - (data + next_offset); - u32 num_fmt_entries; - u8 *fmt; - struct hfi_uncompressed_plane_info *inf; - - if ((rem_bytes - next_offset) < sizeof(*prop)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - num_fmt_entries = prop->format_entries; - next_offset = sizeof(*prop) - sizeof(u32); - fmt = (u8 *)&prop->format_info[0]; - - dev_dbg(dev, "uncomm format support num entries:%u\n", - num_fmt_entries); - - while (num_fmt_entries) { - struct hfi_uncompressed_plane_constraints *cnts; - u32 bytes_to_skip; - - inf = (struct hfi_uncompressed_plane_info *)fmt; - - if ((rem_bytes - next_offset) < sizeof(*inf)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - dev_dbg(dev, "plane info: fmt:%x, planes:%x\n", - inf->format, inf->num_planes); - - cnts = &inf->plane_format[0]; - dev_dbg(dev, "%u %u %u %u\n", - cnts->stride_multiples, - cnts->max_stride, - cnts->min_plane_buffer_height_multiple, - cnts->buffer_alignment); - - bytes_to_skip = sizeof(*inf) - sizeof(*cnts) + - inf->num_planes * sizeof(*cnts); - - fmt += bytes_to_skip; - next_offset += bytes_to_skip; - num_fmt_entries--; - } - num_props--; - break; - } - case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: { - struct hfi_properties_supported *prop = - (struct hfi_properties_supported *) - (data + next_offset); - - next_offset += sizeof(*prop) - sizeof(u32) - + prop->num_properties * sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: { - struct hfi_profile_level_supported *prop = - (struct hfi_profile_level_supported *) - (data + next_offset); - struct hfi_profile_level *pl; - unsigned int prop_count = 0; - unsigned int count = 0; - u8 *ptr; - - ptr = (u8 *)&prop->profile_level[0]; - prop_count = prop->profile_count; - - if (prop_count > HFI_MAX_PROFILE_COUNT) - prop_count = HFI_MAX_PROFILE_COUNT; - - while (prop_count) { - ptr++; - pl = (struct hfi_profile_level *)ptr; - - inst->pl[count].profile = pl->profile; - inst->pl[count].level = pl->level; - prop_count--; - count++; - ptr += sizeof(*pl) / sizeof(u32); - } - - inst->pl_count = count; - next_offset += sizeof(*prop) - sizeof(*pl) + - prop->profile_count * sizeof(*pl); - - num_props--; - break; - } - case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: { - next_offset += - sizeof(struct hfi_interlace_format_supported); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: { - struct hfi_nal_stream_format *nal = - (struct hfi_nal_stream_format *) - (data + next_offset); - dev_dbg(dev, "NAL format: %x\n", nal->format); - next_offset += sizeof(*nal); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: { - next_offset += sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: { - u32 *max_seq_sz = (u32 *)(data + next_offset); - - dev_dbg(dev, "max seq header sz: %x\n", *max_seq_sz); - next_offset += sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { - next_offset += sizeof(struct hfi_intra_refresh); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: { - struct hfi_buffer_alloc_mode_supported *prop = - (struct hfi_buffer_alloc_mode_supported *) - (data + next_offset); - unsigned int i; - - for (i = 0; i < prop->num_entries; i++) { - if (prop->buffer_type == HFI_BUFFER_OUTPUT || - prop->buffer_type == HFI_BUFFER_OUTPUT2) { - switch (prop->data[i]) { - case HFI_BUFFER_MODE_STATIC: - inst->cap_bufs_mode_static = true; - break; - case HFI_BUFFER_MODE_DYNAMIC: - inst->cap_bufs_mode_dynamic = true; - break; - default: - break; - } - } - } - next_offset += sizeof(*prop) - - sizeof(u32) + prop->num_entries * sizeof(u32); - num_props--; - break; - } - default: - dev_dbg(dev, "%s: default case %#x\n", __func__, ptype); - break; - } - - rem_bytes -= next_offset; - data += next_offset; - } - - return err; -} - static void hfi_session_init_done(struct venus_core *core, struct venus_inst *inst, void *packet) { struct hfi_msg_session_init_done_pkt *pkt = packet; - unsigned int error; + int rem_bytes; + u32 error; error = pkt->error_type; if (error != HFI_ERR_NONE) goto done; - if (core->res->hfi_version != HFI_VERSION_1XX) + if (!IS_V1(core)) goto done; - error = init_done_read_prop(core, inst, pkt); + rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32); + if (rem_bytes <= 0) { + error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; + goto done; + } + error = hfi_parser(core, inst, pkt->data, rem_bytes); done: inst->error = error; complete(&inst->done); @@ -779,7 +505,8 @@ static void hfi_session_ftb_done(struct venus_core *core, error = HFI_ERR_SESSION_INVALID_PARAMETER; } - if (buffer_type != HFI_BUFFER_OUTPUT) + if (buffer_type != HFI_BUFFER_OUTPUT && + buffer_type != HFI_BUFFER_OUTPUT2) goto done; if (hfi_flags & HFI_BUFFERFLAG_EOS) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c new file mode 100644 index 000000000000..2293d936e49c --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Linaro Ltd. + * + * Author: Stanimir Varbanov <stanimir.varbanov@linaro.org> + */ +#include <linux/bitops.h> +#include <linux/kernel.h> + +#include "core.h" +#include "hfi_helper.h" +#include "hfi_parser.h" + +typedef void (*func)(struct venus_caps *cap, const void *data, + unsigned int size); + +static void init_codecs(struct venus_core *core) +{ + struct venus_caps *caps = core->caps, *cap; + unsigned long bit; + + for_each_set_bit(bit, &core->dec_codecs, MAX_CODEC_NUM) { + cap = &caps[core->codecs_count++]; + cap->codec = BIT(bit); + cap->domain = VIDC_SESSION_TYPE_DEC; + cap->valid = false; + } + + for_each_set_bit(bit, &core->enc_codecs, MAX_CODEC_NUM) { + cap = &caps[core->codecs_count++]; + cap->codec = BIT(bit); + cap->domain = VIDC_SESSION_TYPE_ENC; + cap->valid = false; + } +} + +static void for_each_codec(struct venus_caps *caps, unsigned int caps_num, + u32 codecs, u32 domain, func cb, void *data, + unsigned int size) +{ + struct venus_caps *cap; + unsigned int i; + + for (i = 0; i < caps_num; i++) { + cap = &caps[i]; + if (cap->valid && cap->domain == domain) + continue; + if (cap->codec & codecs && cap->domain == domain) + cb(cap, data, size); + } +} + +static void +fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num) +{ + const u32 *type = data; + + if (*type == HFI_BUFFER_MODE_DYNAMIC) + cap->cap_bufs_mode_dynamic = true; +} + +static void +parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_buffer_alloc_mode_supported *mode = data; + u32 num_entries = mode->num_entries; + u32 *type; + + if (num_entries > MAX_ALLOC_MODE_ENTRIES) + return; + + type = mode->data; + + while (num_entries--) { + if (mode->buffer_type == HFI_BUFFER_OUTPUT || + mode->buffer_type == HFI_BUFFER_OUTPUT2) + for_each_codec(core->caps, ARRAY_SIZE(core->caps), + codecs, domain, fill_buf_mode, type, 1); + + type++; + } +} + +static void fill_profile_level(struct venus_caps *cap, const void *data, + unsigned int num) +{ + const struct hfi_profile_level *pl = data; + + memcpy(&cap->pl[cap->num_pl], pl, num * sizeof(*pl)); + cap->num_pl += num; +} + +static void +parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_profile_level_supported *pl = data; + struct hfi_profile_level *proflevel = pl->profile_level; + struct hfi_profile_level pl_arr[HFI_MAX_PROFILE_COUNT] = {}; + + if (pl->profile_count > HFI_MAX_PROFILE_COUNT) + return; + + memcpy(pl_arr, proflevel, pl->profile_count * sizeof(*proflevel)); + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_profile_level, pl_arr, pl->profile_count); +} + +static void +fill_caps(struct venus_caps *cap, const void *data, unsigned int num) +{ + const struct hfi_capability *caps = data; + + memcpy(&cap->caps[cap->num_caps], caps, num * sizeof(*caps)); + cap->num_caps += num; +} + +static void +parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_capabilities *caps = data; + struct hfi_capability *cap = caps->data; + u32 num_caps = caps->num_capabilities; + struct hfi_capability caps_arr[MAX_CAP_ENTRIES] = {}; + + if (num_caps > MAX_CAP_ENTRIES) + return; + + memcpy(caps_arr, cap, num_caps * sizeof(*cap)); + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_caps, caps_arr, num_caps); +} + +static void fill_raw_fmts(struct venus_caps *cap, const void *fmts, + unsigned int num_fmts) +{ + const struct raw_formats *formats = fmts; + + memcpy(&cap->fmts[cap->num_fmts], formats, num_fmts * sizeof(*formats)); + cap->num_fmts += num_fmts; +} + +static void +parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_uncompressed_format_supported *fmt = data; + struct hfi_uncompressed_plane_info *pinfo = fmt->plane_info; + struct hfi_uncompressed_plane_constraints *constr; + struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {}; + u32 entries = fmt->format_entries; + unsigned int i = 0; + u32 num_planes; + + while (entries) { + num_planes = pinfo->num_planes; + + rawfmts[i].fmt = pinfo->format; + rawfmts[i].buftype = fmt->buffer_type; + i++; + + if (pinfo->num_planes > MAX_PLANES) + break; + + pinfo = (void *)pinfo + sizeof(*constr) * num_planes + + 2 * sizeof(u32); + entries--; + } + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_raw_fmts, rawfmts, i); +} + +static void parse_codecs(struct venus_core *core, void *data) +{ + struct hfi_codec_supported *codecs = data; + + core->dec_codecs = codecs->dec_codecs; + core->enc_codecs = codecs->enc_codecs; + + if (IS_V1(core)) { + core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; + core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; + } +} + +static void parse_max_sessions(struct venus_core *core, const void *data) +{ + const struct hfi_max_sessions_supported *sessions = data; + + core->max_sessions_supported = sessions->max_sessions; +} + +static void parse_codecs_mask(u32 *codecs, u32 *domain, void *data) +{ + struct hfi_codec_mask_supported *mask = data; + + *codecs = mask->codecs; + *domain = mask->video_domains; +} + +static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain) +{ + if (!inst || !IS_V1(inst->core)) + return; + + *codecs = inst->hfi_codec; + *domain = inst->session_type; +} + +static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain) +{ + struct venus_caps *caps, *cap; + unsigned int i; + u32 dom; + + if (!inst || !IS_V1(inst->core)) + return; + + caps = inst->core->caps; + dom = inst->session_type; + + for (i = 0; i < MAX_CODEC_NUM; i++) { + cap = &caps[i]; + if (cap->codec & codecs && cap->domain == dom) + cap->valid = true; + } +} + +u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, + u32 size) +{ + unsigned int words_count = size >> 2; + u32 *word = buf, *data, codecs = 0, domain = 0; + + if (size % 4) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + parser_init(inst, &codecs, &domain); + + while (words_count) { + data = word + 1; + + switch (*word) { + case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: + parse_codecs(core, data); + init_codecs(core); + break; + case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: + parse_max_sessions(core, data); + break; + case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: + parse_codecs_mask(&codecs, &domain, data); + break; + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + parse_raw_formats(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: + parse_caps(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: + parse_profile_level(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: + parse_alloc_mode(core, codecs, domain, data); + break; + default: + break; + } + + word++; + words_count--; + } + + parser_fini(inst, codecs, domain); + + return HFI_ERR_NONE; +} diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h new file mode 100644 index 000000000000..3e931c747e19 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Linaro Ltd. */ +#ifndef __VENUS_HFI_PARSER_H__ +#define __VENUS_HFI_PARSER_H__ + +#include "core.h" + +u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, + void *buf, u32 size); + +#define WHICH_CAP_MIN 0 +#define WHICH_CAP_MAX 1 +#define WHICH_CAP_STEP 2 + +static inline u32 get_cap(struct venus_inst *inst, u32 type, u32 which) +{ + struct venus_core *core = inst->core; + struct hfi_capability *cap = NULL; + struct venus_caps *caps; + unsigned int i; + + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) + return 0; + + for (i = 0; i < caps->num_caps; i++) { + if (caps->caps[i].capability_type == type) { + cap = &caps->caps[i]; + break; + } + } + + if (!cap) + return 0; + + switch (which) { + case WHICH_CAP_MIN: + return cap->min; + case WHICH_CAP_MAX: + return cap->max; + case WHICH_CAP_STEP: + return cap->step_size; + default: + break; + } + + return 0; +} + +static inline u32 cap_min(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_MIN); +} + +static inline u32 cap_max(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_MAX); +} + +static inline u32 cap_step(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_STEP); +} + +static inline u32 frame_width_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_width_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_width_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_height_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frame_height_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frame_height_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frate_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAMERATE); +} + +static inline u32 frate_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAMERATE); +} + +static inline u32 frate_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAMERATE); +} + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 734ce11b0ed0..124085556b94 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -532,6 +532,24 @@ static int venus_halt_axi(struct venus_hfi_device *hdev) u32 val; int ret; + if (IS_V4(hdev->core)) { + val = venus_readl(hdev, WRAPPER_CPU_AXI_HALT); + val |= WRAPPER_CPU_AXI_HALT_HALT; + venus_writel(hdev, WRAPPER_CPU_AXI_HALT, val); + + ret = readl_poll_timeout(base + WRAPPER_CPU_AXI_HALT_STATUS, + val, + val & WRAPPER_CPU_AXI_HALT_STATUS_IDLE, + POLL_INTERVAL_US, + VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (ret) { + dev_err(dev, "AXI bus port halt timeout\n"); + return ret; + } + + return 0; + } + /* Halt AXI and AXI IMEM VBIF Access */ val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0); val |= VBIF_AXI_HALT_CTRL0_HALT_REQ; @@ -861,6 +879,14 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) if (ret) dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret); + /* + * Idle indicator is disabled by default on some 4xx firmware versions, + * enable it explicitly in order to make suspend functional by checking + * WFI (wait-for-interrupt) bit. + */ + if (IS_V4(hdev->core)) + venus_sys_idle_indicator = true; + ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator); if (ret) dev_warn(dev, "setting idle response ON failed (%d)\n", ret); @@ -1073,6 +1099,10 @@ static int venus_core_init(struct venus_core *core) if (ret) dev_warn(dev, "failed to send image version pkt to fw\n"); + ret = venus_sys_set_default_properties(hdev); + if (ret) + return ret; + return 0; } @@ -1117,10 +1147,6 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type, struct hfi_session_init_pkt pkt; int ret; - ret = venus_sys_set_default_properties(hdev); - if (ret) - return ret; - ret = pkt_session_init(&pkt, inst, session_type, codec); if (ret) goto err; @@ -1426,13 +1452,40 @@ static int venus_suspend_1xx(struct venus_core *core) return 0; } +static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev) +{ + u32 ctrl_status, cpu_status; + + cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + if (cpu_status & WRAPPER_CPU_STATUS_WFI && + ctrl_status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) + return true; + + return false; +} + +static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev) +{ + u32 ctrl_status, cpu_status; + + cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + if (cpu_status & WRAPPER_CPU_STATUS_WFI && + ctrl_status & CPU_CS_SCIACMDARG0_PC_READY) + return true; + + return false; +} + static int venus_suspend_3xx(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); struct device *dev = core->dev; - u32 ctrl_status, wfi_status; + bool val; int ret; - int cnt = 100; if (!hdev->power_enabled || hdev->suspended) return 0; @@ -1446,29 +1499,30 @@ static int venus_suspend_3xx(struct venus_core *core) return -EINVAL; } - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { - wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - - ret = venus_prepare_power_collapse(hdev, false); - if (ret) { - dev_err(dev, "prepare for power collapse fail (%d)\n", - ret); - return ret; - } + /* + * Power collapse sequence for Venus 3xx and 4xx versions: + * 1. Check for ARM9 and video core to be idle by checking WFI bit + * (bit 0) in CPU status register and by checking Idle (bit 30) in + * Control status register for video core. + * 2. Send a command to prepare for power collapse. + * 3. Check for WFI and PC_READY bits. + */ + ret = readx_poll_timeout(venus_cpu_and_video_core_idle, hdev, val, val, + 1500, 100 * 1500); + if (ret) + return ret; - cnt = 100; - while (cnt--) { - wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY && - wfi_status & BIT(0)) - break; - usleep_range(1000, 1500); - } + ret = venus_prepare_power_collapse(hdev, false); + if (ret) { + dev_err(dev, "prepare for power collapse fail (%d)\n", ret); + return ret; } + ret = readx_poll_timeout(venus_cpu_idle_and_pc_ready, hdev, val, val, + 1500, 100 * 1500); + if (ret) + return ret; + mutex_lock(&hdev->lock); ret = venus_power_off(hdev); @@ -1487,7 +1541,7 @@ static int venus_suspend_3xx(struct venus_core *core) static int venus_suspend(struct venus_core *core) { - if (core->res->hfi_version == HFI_VERSION_3XX) + if (IS_V3(core) || IS_V4(core)) return venus_suspend_3xx(core); return venus_suspend_1xx(core); diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h index 98cc350113ab..def0926a6dee 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus_io.h +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -104,10 +104,20 @@ #define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000) #define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008) +#define WRAPPER_CPU_AXI_HALT_HALT BIT(16) #define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c) +#define WRAPPER_CPU_AXI_HALT_STATUS_IDLE BIT(24) #define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010) #define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014) +#define WRAPPER_CPU_STATUS_WFI BIT(0) #define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000) +/* Venus 4xx */ +#define WRAPPER_VCODEC0_MMCC_POWER_STATUS (WRAPPER_BASE + 0x90) +#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x94) + +#define WRAPPER_VCODEC1_MMCC_POWER_STATUS (WRAPPER_BASE + 0x110) +#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x114) + #endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 49bbd1861d3a..dfbbbf0f746f 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -14,6 +14,7 @@ */ #include <linux/clk.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -24,33 +25,11 @@ #include <media/videobuf2-dma-sg.h> #include "hfi_venus_io.h" +#include "hfi_parser.h" #include "core.h" #include "helpers.h" #include "vdec.h" -static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) -{ - u32 y_stride, uv_stride, y_plane; - u32 y_sclines, uv_sclines, uv_plane; - u32 size; - - y_stride = ALIGN(width, 128); - uv_stride = ALIGN(width, 128); - y_sclines = ALIGN(height, 32); - uv_sclines = ALIGN(((height + 1) >> 1), 16); - - y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; - - return ALIGN(size, SZ_4K); -} - -static u32 get_framesize_compressed(unsigned int width, unsigned int height) -{ - return ((width * height * 3 / 2) / 2) + 128; -} - /* * Three resons to keep MPLANE formats (despite that the number of planes * currently is one): @@ -99,6 +78,10 @@ static const struct venus_format vdec_formats[] = { .pixfmt = V4L2_PIX_FMT_XVID, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, }, }; @@ -159,7 +142,6 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct venus_format *fmt; - unsigned int p; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -173,14 +155,12 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); - pixmp->width = 1280; - pixmp->height = 720; } - pixmp->width = clamp(pixmp->width, inst->cap_width.min, - inst->cap_width.max); - pixmp->height = clamp(pixmp->height, inst->cap_height.min, - inst->cap_height.max); + pixmp->width = clamp(pixmp->width, frame_width_min(inst), + frame_width_max(inst)); + pixmp->height = clamp(pixmp->height, frame_height_min(inst), + frame_height_max(inst)); if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) pixmp->height = ALIGN(pixmp->height, 32); @@ -190,18 +170,14 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - for (p = 0; p < pixmp->num_planes; p++) { - pfmt[p].sizeimage = - get_framesize_uncompressed(p, pixmp->width, - pixmp->height); - pfmt[p].bytesperline = ALIGN(pixmp->width, 128); - } - } else { - pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, - pixmp->height); + pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, + pixmp->width, + pixmp->height); + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pfmt[0].bytesperline = ALIGN(pixmp->width, 128); + else pfmt[0].bytesperline = 0; - } return fmt; } @@ -442,12 +418,12 @@ static int vdec_enum_framesizes(struct file *file, void *fh, fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = inst->cap_width.min; - fsize->stepwise.max_width = inst->cap_width.max; - fsize->stepwise.step_width = inst->cap_width.step_size; - fsize->stepwise.min_height = inst->cap_height.min; - fsize->stepwise.max_height = inst->cap_height.max; - fsize->stepwise.step_height = inst->cap_height.step_size; + fsize->stepwise.min_width = frame_width_min(inst); + fsize->stepwise.max_width = frame_width_max(inst); + fsize->stepwise.step_width = frame_width_step(inst); + fsize->stepwise.min_height = frame_height_min(inst); + fsize->stepwise.max_height = frame_height_max(inst); + fsize->stepwise.step_height = frame_height_step(inst); return 0; } @@ -544,11 +520,41 @@ static const struct v4l2_ioctl_ops vdec_ioctl_ops = { static int vdec_set_properties(struct venus_inst *inst) { struct vdec_controls *ctr = &inst->controls.dec; + struct hfi_enable en = { .enable = 1 }; + u32 ptype; + int ret; + + if (ctr->post_loop_deb_mode) { + ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + + return 0; +} + +#define is_ubwc_fmt(fmt) (!!((fmt) & HFI_COLOR_FORMAT_UBWC_BASE)) + +static int vdec_output_conf(struct venus_inst *inst) +{ struct venus_core *core = inst->core; struct hfi_enable en = { .enable = 1 }; + u32 width = inst->out_width; + u32 height = inst->out_height; + u32 out_fmt, out2_fmt; + bool ubwc = false; u32 ptype; int ret; + ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + if (ret) + return ret; + + ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1); + if (ret) + return ret; + if (core->res->hfi_version == HFI_VERSION_1XX) { ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; ret = hfi_session_set_property(inst, ptype, &en); @@ -556,27 +562,84 @@ static int vdec_set_properties(struct venus_inst *inst) return ret; } - if (core->res->hfi_version == HFI_VERSION_3XX || - inst->cap_bufs_mode_dynamic) { - struct hfi_buffer_alloc_mode mode; + /* Force searching UBWC formats for bigger then HD resolutions */ + if (width > 1920 && height > ALIGN(1080, 32)) + ubwc = true; + + /* For Venus v4 UBWC format is mandatory */ + if (IS_V4(core)) + ubwc = true; + + ret = venus_helper_get_out_fmts(inst, inst->fmt_cap->pixfmt, &out_fmt, + &out2_fmt, ubwc); + if (ret) + return ret; + + inst->output_buf_size = + venus_helper_get_framesz_raw(out_fmt, width, height); + inst->output2_buf_size = + venus_helper_get_framesz_raw(out2_fmt, width, height); + + if (is_ubwc_fmt(out_fmt)) { + inst->opb_buftype = HFI_BUFFER_OUTPUT2; + inst->opb_fmt = out2_fmt; + inst->dpb_buftype = HFI_BUFFER_OUTPUT; + inst->dpb_fmt = out_fmt; + } else if (is_ubwc_fmt(out2_fmt)) { + inst->opb_buftype = HFI_BUFFER_OUTPUT; + inst->opb_fmt = out_fmt; + inst->dpb_buftype = HFI_BUFFER_OUTPUT2; + inst->dpb_fmt = out2_fmt; + } else { + inst->opb_buftype = HFI_BUFFER_OUTPUT; + inst->opb_fmt = out_fmt; + inst->dpb_buftype = 0; + inst->dpb_fmt = 0; + } + + ret = venus_helper_set_raw_format(inst, inst->opb_fmt, + inst->opb_buftype); + if (ret) + return ret; - ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; - mode.type = HFI_BUFFER_OUTPUT; - mode.mode = HFI_BUFFER_MODE_DYNAMIC; + if (inst->dpb_fmt) { + ret = venus_helper_set_multistream(inst, false, true); + if (ret) + return ret; - ret = hfi_session_set_property(inst, ptype, &mode); + ret = venus_helper_set_raw_format(inst, inst->dpb_fmt, + inst->dpb_buftype); if (ret) return ret; - } - if (ctr->post_loop_deb_mode) { - ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; - en.enable = 1; - ret = hfi_session_set_property(inst, ptype, &en); + ret = venus_helper_set_output_resolution(inst, width, height, + HFI_BUFFER_OUTPUT2); if (ret) return ret; } + if (IS_V3(core) || IS_V4(core)) { + if (inst->output2_buf_size) { + ret = venus_helper_set_bufsize(inst, + inst->output2_buf_size, + HFI_BUFFER_OUTPUT2); + if (ret) + return ret; + } + + if (inst->output_buf_size) { + ret = venus_helper_set_bufsize(inst, + inst->output_buf_size, + HFI_BUFFER_OUTPUT); + if (ret) + return ret; + } + } + + ret = venus_helper_set_dyn_bufmode(inst); + if (ret) + return ret; + return 0; } @@ -603,19 +666,32 @@ deinit: return ret; } -static int vdec_cap_num_buffers(struct venus_inst *inst, unsigned int *num) +static int vdec_num_buffers(struct venus_inst *inst, unsigned int *in_num, + unsigned int *out_num) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; + *in_num = *out_num = 0; + ret = vdec_init_session(inst); if (ret) return ret; + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + if (ret) + goto deinit; + + *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + goto deinit; - *num = bufreq.count_actual; + *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); +deinit: hfi_session_deinit(inst); return ret; @@ -626,10 +702,12 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct venus_inst *inst = vb2_get_drv_priv(q); - unsigned int p, num; + unsigned int in_num, out_num; int ret = 0; if (*num_planes) { + unsigned int output_buf_size = venus_helper_get_opb_size(inst); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && *num_planes != inst->fmt_out->num_planes) return -EINVAL; @@ -643,41 +721,35 @@ static int vdec_queue_setup(struct vb2_queue *q, return -EINVAL; if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - sizes[0] < inst->output_buf_size) + sizes[0] < output_buf_size) return -EINVAL; return 0; } + ret = vdec_num_buffers(inst, &in_num, &out_num); + if (ret) + return ret; + switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: *num_planes = inst->fmt_out->num_planes; - sizes[0] = get_framesize_compressed(inst->out_width, + sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, + inst->out_width, inst->out_height); inst->input_buf_size = sizes[0]; + *num_buffers = max(*num_buffers, in_num); inst->num_input_bufs = *num_buffers; - - ret = vdec_cap_num_buffers(inst, &num); - if (ret) - break; - - inst->num_output_bufs = num; + inst->num_output_bufs = out_num; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: *num_planes = inst->fmt_cap->num_planes; - - ret = vdec_cap_num_buffers(inst, &num); - if (ret) - break; - - *num_buffers = max(*num_buffers, num); - - for (p = 0; p < *num_planes; p++) - sizes[p] = get_framesize_uncompressed(p, inst->width, - inst->height); - - inst->num_output_bufs = *num_buffers; + sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, + inst->width, + inst->height); inst->output_buf_size = sizes[0]; + *num_buffers = max(*num_buffers, out_num); + inst->num_output_bufs = *num_buffers; break; default: ret = -EINVAL; @@ -689,6 +761,7 @@ static int vdec_queue_setup(struct vb2_queue *q, static int vdec_verify_conf(struct venus_inst *inst) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; @@ -700,14 +773,14 @@ static int vdec_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < bufreq.count_min) + inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (ret) return ret; - if (inst->num_input_bufs < bufreq.count_min) + if (inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; return 0; @@ -716,8 +789,6 @@ static int vdec_verify_conf(struct venus_inst *inst) static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) { struct venus_inst *inst = vb2_get_drv_priv(q); - struct venus_core *core = inst->core; - u32 ptype; int ret; mutex_lock(&inst->lock); @@ -746,24 +817,20 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto deinit_sess; - if (core->res->hfi_version == HFI_VERSION_3XX) { - struct hfi_buffer_size_actual buf_sz; - - ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; - buf_sz.type = HFI_BUFFER_OUTPUT; - buf_sz.size = inst->output_buf_size; - - ret = hfi_session_set_property(inst, ptype, &buf_sz); - if (ret) - goto deinit_sess; - } + ret = vdec_output_conf(inst); + if (ret) + goto deinit_sess; ret = vdec_verify_conf(inst); if (ret) goto deinit_sess; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, - VB2_MAX_FRAME); + VB2_MAX_FRAME, VB2_MAX_FRAME); + if (ret) + goto deinit_sess; + + ret = venus_helper_alloc_dpb_bufs(inst); if (ret) goto deinit_sess; @@ -818,9 +885,11 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, vbuf->field = V4L2_FIELD_NONE; if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + unsigned int opb_sz = venus_helper_get_opb_size(inst); + vb = &vbuf->vb2_buf; vb->planes[0].bytesused = - max_t(unsigned int, inst->output_buf_size, bytesused); + max_t(unsigned int, opb_sz, bytesused); vb->planes[0].data_offset = data_offset; vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; @@ -901,22 +970,7 @@ static void vdec_inst_init(struct venus_inst *inst) inst->fps = 30; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 30; - - inst->cap_width.min = 64; - inst->cap_width.max = 1920; - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_width.max = 3840; - inst->cap_width.step_size = 1; - inst->cap_height.min = 64; - inst->cap_height.max = ALIGN(1080, 32); - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_height.max = ALIGN(2160, 32); - inst->cap_height.step_size = 1; - inst->cap_framerate.min = 1; - inst->cap_framerate.max = 30; - inst->cap_framerate.step_size = 1; - inst->cap_mbs_per_frame.min = 16; - inst->cap_mbs_per_frame.max = 8160; + inst->hfi_codec = HFI_VIDEO_CODEC_H264; } static const struct v4l2_m2m_ops vdec_m2m_ops = { @@ -973,6 +1027,7 @@ static int vdec_open(struct file *file) if (!inst) return -ENOMEM; + INIT_LIST_HEAD(&inst->dpbbufs); INIT_LIST_HEAD(&inst->registeredbufs); INIT_LIST_HEAD(&inst->internalbufs); INIT_LIST_HEAD(&inst->list); @@ -1080,12 +1135,18 @@ static int vdec_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (core->res->hfi_version == HFI_VERSION_3XX) { + if (IS_V3(core) || IS_V4(core)) { core->core0_clk = devm_clk_get(dev, "core"); if (IS_ERR(core->core0_clk)) return PTR_ERR(core->core0_clk); } + if (IS_V4(core)) { + core->core0_bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(core->core0_bus_clk)) + return PTR_ERR(core->core0_bus_clk); + } + platform_set_drvdata(pdev, core); vdev = video_device_alloc(); @@ -1130,15 +1191,21 @@ static int vdec_remove(struct platform_device *pdev) static __maybe_unused int vdec_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); + if (ret) + return ret; + + if (IS_V4(core)) + clk_disable_unprepare(core->core0_bus_clk); + clk_disable_unprepare(core->core0_clk); - writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); - return 0; + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); } static __maybe_unused int vdec_runtime_resume(struct device *dev) @@ -1146,13 +1213,29 @@ static __maybe_unused int vdec_runtime_resume(struct device *dev) struct venus_core *core = dev_get_drvdata(dev); int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); + if (ret) + return ret; + ret = clk_prepare_enable(core->core0_clk); - writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + if (ret) + goto err_power_disable; + if (IS_V4(core)) + ret = clk_prepare_enable(core->core0_bus_clk); + + if (ret) + goto err_unprepare_core0; + + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + +err_unprepare_core0: + clk_disable_unprepare(core->core0_clk); +err_power_disable: + venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); return ret; } diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 032839bbc967..f4604b0cd57e 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -29,7 +29,7 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: @@ -54,7 +54,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ret = hfi_session_get_property(inst, ptype, &hprop); if (!ret) ctr->profile = hprop.profile_level.profile; @@ -130,8 +130,10 @@ int vdec_ctrl_init(struct venus_inst *inst) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + V4L2_MPEG_VIDEO_VP8_PROFILE_3, + 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 6b2ce479584e..41249d1443fa 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -14,6 +14,7 @@ */ #include <linux/clk.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -24,38 +25,13 @@ #include <media/v4l2-ctrls.h> #include "hfi_venus_io.h" +#include "hfi_parser.h" #include "core.h" #include "helpers.h" #include "venc.h" #define NUM_B_FRAMES_MAX 4 -static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) -{ - u32 y_stride, uv_stride, y_plane; - u32 y_sclines, uv_sclines, uv_plane; - u32 size; - - y_stride = ALIGN(width, 128); - uv_stride = ALIGN(width, 128); - y_sclines = ALIGN(height, 32); - uv_sclines = ALIGN(((height + 1) >> 1), 16); - - y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; - size = ALIGN(size, SZ_4K); - - return size; -} - -static u32 get_framesize_compressed(u32 width, u32 height) -{ - u32 sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; - - return ALIGN(sz, SZ_4K); -} - /* * Three resons to keep MPLANE formats (despite that the number of planes * currently is one): @@ -84,6 +60,10 @@ static const struct venus_format venc_formats[] = { .pixfmt = V4L2_PIX_FMT_VP8, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, }, }; @@ -223,7 +203,7 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: return HFI_H264_ENTROPY_CABAC; } - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: switch (value) { case 0: default: @@ -245,6 +225,46 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: return HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY; } + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + default: + return HFI_HEVC_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return HFI_HEVC_PROFILE_MAIN_STILL_PIC; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return HFI_HEVC_PROFILE_MAIN10; + } + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + default: + return HFI_HEVC_LEVEL_1; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return HFI_HEVC_LEVEL_2; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return HFI_HEVC_LEVEL_21; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return HFI_HEVC_LEVEL_3; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return HFI_HEVC_LEVEL_31; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return HFI_HEVC_LEVEL_4; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return HFI_HEVC_LEVEL_41; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return HFI_HEVC_LEVEL_5; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return HFI_HEVC_LEVEL_51; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: + return HFI_HEVC_LEVEL_52; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6: + return HFI_HEVC_LEVEL_6; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1: + return HFI_HEVC_LEVEL_61; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2: + return HFI_HEVC_LEVEL_62; + } } return 0; @@ -283,7 +303,6 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct venus_format *fmt; - unsigned int p; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -297,14 +316,12 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); - pixmp->width = 1280; - pixmp->height = 720; } - pixmp->width = clamp(pixmp->width, inst->cap_width.min, - inst->cap_width.max); - pixmp->height = clamp(pixmp->height, inst->cap_height.min, - inst->cap_height.max); + pixmp->width = clamp(pixmp->width, frame_width_min(inst), + frame_width_max(inst)); + pixmp->height = clamp(pixmp->height, frame_height_min(inst), + frame_height_max(inst)); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) pixmp->height = ALIGN(pixmp->height, 32); @@ -317,19 +334,14 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - for (p = 0; p < pixmp->num_planes; p++) { - pfmt[p].sizeimage = - get_framesize_uncompressed(p, pixmp->width, - pixmp->height); + pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, + pixmp->width, + pixmp->height); - pfmt[p].bytesperline = ALIGN(pixmp->width, 128); - } - } else { - pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, - pixmp->height); + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + pfmt[0].bytesperline = ALIGN(pixmp->width, 128); + else pfmt[0].bytesperline = 0; - } return fmt; } @@ -553,12 +565,12 @@ static int venc_enum_framesizes(struct file *file, void *fh, if (fsize->index) return -EINVAL; - fsize->stepwise.min_width = inst->cap_width.min; - fsize->stepwise.max_width = inst->cap_width.max; - fsize->stepwise.step_width = inst->cap_width.step_size; - fsize->stepwise.min_height = inst->cap_height.min; - fsize->stepwise.max_height = inst->cap_height.max; - fsize->stepwise.step_height = inst->cap_height.step_size; + fsize->stepwise.min_width = frame_width_min(inst); + fsize->stepwise.max_width = frame_width_max(inst); + fsize->stepwise.step_width = frame_width_step(inst); + fsize->stepwise.min_height = frame_height_min(inst); + fsize->stepwise.max_height = frame_height_max(inst); + fsize->stepwise.step_height = frame_height_step(inst); return 0; } @@ -586,18 +598,18 @@ static int venc_enum_frameintervals(struct file *file, void *fh, if (!fival->width || !fival->height) return -EINVAL; - if (fival->width > inst->cap_width.max || - fival->width < inst->cap_width.min || - fival->height > inst->cap_height.max || - fival->height < inst->cap_height.min) + if (fival->width > frame_width_max(inst) || + fival->width < frame_width_min(inst) || + fival->height > frame_height_max(inst) || + fival->height < frame_height_min(inst)) return -EINVAL; fival->stepwise.min.numerator = 1; - fival->stepwise.min.denominator = inst->cap_framerate.max; + fival->stepwise.min.denominator = frate_max(inst); fival->stepwise.max.numerator = 1; - fival->stepwise.max.denominator = inst->cap_framerate.min; + fival->stepwise.max.denominator = frate_min(inst); fival->stepwise.step.numerator = 1; - fival->stepwise.step.denominator = inst->cap_framerate.max; + fival->stepwise.step.denominator = frate_max(inst); return 0; } @@ -642,6 +654,14 @@ static int venc_set_properties(struct venus_inst *inst) u32 ptype, rate_control, bitrate, profile = 0, level = 0; int ret; + ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + if (ret) + return ret; + + ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_2); + if (ret) + return ret; + ptype = HFI_PROPERTY_CONFIG_FRAME_RATE; frate.buffer_type = HFI_BUFFER_OUTPUT; frate.framerate = inst->fps * (1 << 16); @@ -756,7 +776,7 @@ static int venc_set_properties(struct venus_inst *inst) level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL, ctr->level.h264); } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { - profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VPX_PROFILE, + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VP8_PROFILE, ctr->profile.vpx); level = 0; } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) { @@ -767,6 +787,11 @@ static int venc_set_properties(struct venus_inst *inst) } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) { profile = 0; level = 0; + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + ctr->profile.hevc); + level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + ctr->level.hevc); } ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; @@ -794,7 +819,8 @@ static int venc_init_session(struct venus_inst *inst) goto deinit; ret = venus_helper_set_output_resolution(inst, inst->width, - inst->height); + inst->height, + HFI_BUFFER_OUTPUT); if (ret) goto deinit; @@ -835,7 +861,7 @@ static int venc_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct venus_inst *inst = vb2_get_drv_priv(q); - unsigned int p, num, min = 4; + unsigned int num, min = 4; int ret = 0; if (*num_planes) { @@ -870,16 +896,18 @@ static int venc_queue_setup(struct vb2_queue *q, *num_buffers = max(*num_buffers, num); inst->num_input_bufs = *num_buffers; - for (p = 0; p < *num_planes; ++p) - sizes[p] = get_framesize_uncompressed(p, inst->width, - inst->height); + sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, + inst->width, + inst->height); inst->input_buf_size = sizes[0]; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: *num_planes = inst->fmt_cap->num_planes; *num_buffers = max(*num_buffers, min); inst->num_output_bufs = *num_buffers; - sizes[0] = get_framesize_compressed(inst->width, inst->height); + sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, + inst->width, + inst->height); inst->output_buf_size = sizes[0]; break; default: @@ -892,6 +920,7 @@ static int venc_queue_setup(struct vb2_queue *q, static int venc_verify_conf(struct venus_inst *inst) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; @@ -903,7 +932,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < bufreq.count_min) + inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); @@ -911,7 +940,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_input_bufs < bufreq.count_actual || - inst->num_input_bufs < bufreq.count_min) + inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; return 0; @@ -952,7 +981,7 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) goto deinit_sess; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, - inst->num_output_bufs); + inst->num_output_bufs, 0); if (ret) goto deinit_sess; @@ -1090,22 +1119,7 @@ static void venc_inst_init(struct venus_inst *inst) inst->fps = 15; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 15; - - inst->cap_width.min = 96; - inst->cap_width.max = 1920; - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_width.max = 3840; - inst->cap_width.step_size = 2; - inst->cap_height.min = 64; - inst->cap_height.max = ALIGN(1080, 32); - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_height.max = ALIGN(2160, 32); - inst->cap_height.step_size = 2; - inst->cap_framerate.min = 1; - inst->cap_framerate.max = 30; - inst->cap_framerate.step_size = 1; - inst->cap_mbs_per_frame.min = 24; - inst->cap_mbs_per_frame.max = 8160; + inst->hfi_codec = HFI_VIDEO_CODEC_H264; } static int venc_open(struct file *file) @@ -1118,6 +1132,7 @@ static int venc_open(struct file *file) if (!inst) return -ENOMEM; + INIT_LIST_HEAD(&inst->dpbbufs); INIT_LIST_HEAD(&inst->registeredbufs); INIT_LIST_HEAD(&inst->internalbufs); INIT_LIST_HEAD(&inst->list); @@ -1224,12 +1239,18 @@ static int venc_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (core->res->hfi_version == HFI_VERSION_3XX) { + if (IS_V3(core) || IS_V4(core)) { core->core1_clk = devm_clk_get(dev, "core"); if (IS_ERR(core->core1_clk)) return PTR_ERR(core->core1_clk); } + if (IS_V4(core)) { + core->core1_bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(core->core1_bus_clk)) + return PTR_ERR(core->core1_bus_clk); + } + platform_set_drvdata(pdev, core); vdev = video_device_alloc(); @@ -1274,15 +1295,21 @@ static int venc_remove(struct platform_device *pdev) static __maybe_unused int venc_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); + if (ret) + return ret; + + if (IS_V4(core)) + clk_disable_unprepare(core->core1_bus_clk); + clk_disable_unprepare(core->core1_clk); - writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); - return 0; + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); } static __maybe_unused int venc_runtime_resume(struct device *dev) @@ -1290,13 +1317,29 @@ static __maybe_unused int venc_runtime_resume(struct device *dev) struct venus_core *core = dev_get_drvdata(dev); int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); + if (ret) + return ret; + ret = clk_prepare_enable(core->core1_clk); - writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + if (ret) + goto err_power_disable; + + if (IS_V4(core)) + ret = clk_prepare_enable(core->core1_bus_clk); + + if (ret) + goto err_unprepare_core1; + + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); +err_unprepare_core1: + clk_disable_unprepare(core->core1_clk); +err_power_disable: + venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); return ret; } diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 21e938a28662..459101728d26 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -101,7 +101,7 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_PROFILE: ctr->profile.h264 = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile.vpx = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: @@ -248,6 +248,11 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + V4L2_MPEG_VIDEO_VP8_PROFILE_3, + 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX, BITRATE_STEP, BITRATE_DEFAULT); @@ -257,9 +262,6 @@ int venc_ctrl_init(struct venus_inst *inst) BITRATE_STEP, BITRATE_DEFAULT_PEAK); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); - - v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, |