diff options
Diffstat (limited to 'drivers/media/platform/qcom/venus')
-rw-r--r-- | drivers/media/platform/qcom/venus/core.c | 58 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/core.h | 63 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/helpers.c | 440 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/helpers.h | 12 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi.c | 11 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi.h | 2 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_venus.c | 6 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/vdec.c | 549 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/vdec_ctrls.c | 7 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/venc.c | 20 |
10 files changed, 989 insertions, 179 deletions
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 0acc7576cc58..07312a2fab24 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -5,6 +5,7 @@ */ #include <linux/clk.h> #include <linux/init.h> +#include <linux/interconnect.h> #include <linux/ioctl.h> #include <linux/list.h> #include <linux/module.h> @@ -198,7 +199,7 @@ static int venus_enumerate_codecs(struct venus_core *core, u32 type) goto err; for (i = 0; i < MAX_CODEC_NUM; i++) { - codec = (1 << i) & codecs; + codec = (1UL << i) & codecs; if (!codec) continue; @@ -239,6 +240,14 @@ static int venus_probe(struct platform_device *pdev) if (IS_ERR(core->base)) return PTR_ERR(core->base); + core->video_path = of_icc_get(dev, "video-mem"); + if (IS_ERR(core->video_path)) + return PTR_ERR(core->video_path); + + core->cpucfg_path = of_icc_get(dev, "cpu-cfg"); + if (IS_ERR(core->cpucfg_path)) + return PTR_ERR(core->cpucfg_path); + core->irq = platform_get_irq(pdev, 0); if (core->irq < 0) return core->irq; @@ -273,6 +282,10 @@ static int venus_probe(struct platform_device *pdev) if (ret) return ret; + ret = icc_set_bw(core->cpucfg_path, 0, kbps_to_icc(1000)); + if (ret) + return ret; + ret = hfi_create(core, &venus_core_ops); if (ret) return ret; @@ -355,6 +368,9 @@ static int venus_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); + icc_put(core->video_path); + icc_put(core->cpucfg_path); + v4l2_device_unregister(&core->v4l2_dev); return ret; @@ -427,10 +443,11 @@ static const struct venus_resources msm8916_res = { }; static const struct freq_tbl msm8996_freq_table[] = { - { 1944000, 490000000 }, /* 4k UHD @ 60 */ - { 972000, 320000000 }, /* 4k UHD @ 30 */ - { 489600, 150000000 }, /* 1080p @ 60 */ - { 244800, 75000000 }, /* 1080p @ 30 */ + { 1944000, 520000000 }, /* 4k UHD @ 60 (decode only) */ + { 972000, 520000000 }, /* 4k UHD @ 30 */ + { 489600, 346666667 }, /* 1080p @ 60 */ + { 244800, 150000000 }, /* 1080p @ 30 */ + { 108000, 75000000 }, /* 720p @ 30 */ }; static const struct reg_val msm8996_reg_preset[] = { @@ -464,9 +481,40 @@ static const struct freq_tbl sdm845_freq_table[] = { { 244800, 100000000 }, /* 1920x1080@30 */ }; +static struct codec_freq_data sdm845_codec_freq_data[] = { + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_ENC, 675, 10 }, + { V4L2_PIX_FMT_MPEG2, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_H264, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_HEVC, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP8, VIDC_SESSION_TYPE_DEC, 200, 10 }, + { V4L2_PIX_FMT_VP9, VIDC_SESSION_TYPE_DEC, 200, 10 }, +}; + +static const struct bw_tbl sdm845_bw_table_enc[] = { + { 1944000, 1612000, 0, 2416000, 0 }, /* 3840x2160@60 */ + { 972000, 951000, 0, 1434000, 0 }, /* 3840x2160@30 */ + { 489600, 723000, 0, 973000, 0 }, /* 1920x1080@60 */ + { 244800, 370000, 0, 495000, 0 }, /* 1920x1080@30 */ +}; + +static const struct bw_tbl sdm845_bw_table_dec[] = { + { 2073600, 3929000, 0, 5551000, 0 }, /* 4096x2160@60 */ + { 1036800, 1987000, 0, 2797000, 0 }, /* 4096x2160@30 */ + { 489600, 1040000, 0, 1298000, 0 }, /* 1920x1080@60 */ + { 244800, 530000, 0, 659000, 0 }, /* 1920x1080@30 */ +}; + static const struct venus_resources sdm845_res = { .freq_tbl = sdm845_freq_table, .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), + .bw_tbl_enc = sdm845_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), + .bw_tbl_dec = sdm845_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), + .codec_freq_data = sdm845_codec_freq_data, + .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, .max_load = 3110400, /* 4096x2160@90 */ diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 9ab95fd57760..11585fb3cae3 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -26,12 +26,33 @@ struct reg_val { u32 value; }; +struct codec_freq_data { + u32 pixfmt; + u32 session_type; + unsigned long vpp_freq; + unsigned long vsp_freq; +}; + +struct bw_tbl { + u32 mbs_per_sec; + u32 avg; + u32 peak; + u32 avg_10bit; + u32 peak_10bit; +}; + struct venus_resources { u64 dma_mask; const struct freq_tbl *freq_tbl; unsigned int freq_tbl_size; + const struct bw_tbl *bw_tbl_enc; + unsigned int bw_tbl_enc_size; + const struct bw_tbl *bw_tbl_dec; + unsigned int bw_tbl_dec_size; const struct reg_val *reg_tbl; unsigned int reg_tbl_size; + const struct codec_freq_data *codec_freq_data; + unsigned int codec_freq_data_size; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; enum hfi_version hfi_version; @@ -46,6 +67,7 @@ struct venus_format { u32 pixfmt; unsigned int num_planes; u32 type; + u32 flags; }; #define MAX_PLANES 4 @@ -114,6 +136,8 @@ struct venus_core { struct clk *core1_clk; struct clk *core0_bus_clk; struct clk *core1_bus_clk; + struct icc_path *video_path; + struct icc_path *cpucfg_path; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -207,8 +231,33 @@ struct venus_buffer { struct list_head ref_list; }; +struct clock_data { + u32 core_id; + unsigned long freq; + const struct codec_freq_data *codec_freq_data; +}; + #define to_venus_buffer(ptr) container_of(ptr, struct venus_buffer, vb) +enum venus_dec_state { + VENUS_DEC_STATE_DEINIT = 0, + VENUS_DEC_STATE_INIT = 1, + VENUS_DEC_STATE_CAPTURE_SETUP = 2, + VENUS_DEC_STATE_STOPPED = 3, + VENUS_DEC_STATE_SEEK = 4, + VENUS_DEC_STATE_DRAIN = 5, + VENUS_DEC_STATE_DECODING = 6, + VENUS_DEC_STATE_DRC = 7 +}; + +struct venus_ts_metadata { + bool used; + u64 ts_ns; + u64 ts_us; + u32 flags; + struct v4l2_timecode tc; +}; + /** * struct venus_inst - holds per instance parameters * @@ -232,6 +281,10 @@ struct venus_buffer { * @colorspace: current color space * @quantization: current quantization * @xfer_func: current xfer function + * @codec_state: current codec API state (see DEC/ENC_STATE_) + * @reconf_wait: wait queue for resolution change event + * @subscriptions: used to hold current events subscriptions + * @buf_count: used to count number of buffers (reqbuf(0)) * @fps: holds current FPS * @timeperframe: holds current time per frame structure * @fmt_out: a reference to output format structure @@ -246,8 +299,6 @@ struct venus_buffer { * @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 @@ -266,6 +317,7 @@ struct venus_inst { struct list_head list; struct mutex lock; struct venus_core *core; + struct clock_data clk_data; struct list_head dpbbufs; struct list_head internalbufs; struct list_head registeredbufs; @@ -287,6 +339,11 @@ struct venus_inst { u8 ycbcr_enc; u8 quantization; u8 xfer_func; + enum venus_dec_state codec_state; + wait_queue_head_t reconf_wait; + unsigned int subscriptions; + int buf_count; + struct venus_ts_metadata tss[VIDEO_MAX_FRAME]; u64 fps; struct v4l2_fract timeperframe; const struct venus_format *fmt_out; @@ -301,8 +358,6 @@ struct venus_inst { u32 opb_buftype; u32 opb_fmt; bool reconfig; - u32 reconfig_width; - u32 reconfig_height; u32 hfi_codec; u32 sequence_cap; u32 sequence_out; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 71b06dfc6dc4..a172f1ac0b35 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -5,6 +5,7 @@ */ #include <linux/clk.h> #include <linux/iopoll.h> +#include <linux/interconnect.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> @@ -79,7 +80,7 @@ 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) +int venus_helper_queue_dpb_bufs(struct venus_inst *inst) { struct intbuf *buf; int ret = 0; @@ -100,6 +101,7 @@ static int venus_helper_queue_dpb_bufs(struct venus_inst *inst) fail: return ret; } +EXPORT_SYMBOL_GPL(venus_helper_queue_dpb_bufs); int venus_helper_free_dpb_bufs(struct venus_inst *inst) { @@ -278,7 +280,7 @@ static const unsigned int intbuf_types_4xx[] = { HFI_BUFFER_INTERNAL_PERSIST_1, }; -static int intbufs_alloc(struct venus_inst *inst) +int venus_helper_intbufs_alloc(struct venus_inst *inst) { const unsigned int *intbuf; size_t arr_sz, i; @@ -304,11 +306,59 @@ error: intbufs_unset_buffers(inst); return ret; } +EXPORT_SYMBOL_GPL(venus_helper_intbufs_alloc); -static int intbufs_free(struct venus_inst *inst) +int venus_helper_intbufs_free(struct venus_inst *inst) { return intbufs_unset_buffers(inst); } +EXPORT_SYMBOL_GPL(venus_helper_intbufs_free); + +int venus_helper_intbufs_realloc(struct venus_inst *inst) +{ + enum hfi_version ver = inst->core->res->hfi_version; + struct hfi_buffer_desc bd; + struct intbuf *buf, *n; + int ret; + + list_for_each_entry_safe(buf, n, &inst->internalbufs, list) { + if (buf->type == HFI_BUFFER_INTERNAL_PERSIST || + buf->type == HFI_BUFFER_INTERNAL_PERSIST_1) + continue; + + memset(&bd, 0, sizeof(bd)); + bd.buffer_size = buf->size; + bd.buffer_type = buf->type; + bd.num_buffers = 1; + bd.device_addr = buf->da; + bd.response_required = true; + + ret = hfi_session_unset_buffers(inst, &bd); + + dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da, + buf->attrs); + + list_del_init(&buf->list); + kfree(buf); + } + + ret = intbufs_set_buffer(inst, HFI_BUFFER_INTERNAL_SCRATCH(ver)); + if (ret) + goto err; + + ret = intbufs_set_buffer(inst, HFI_BUFFER_INTERNAL_SCRATCH_1(ver)); + if (ret) + goto err; + + ret = intbufs_set_buffer(inst, HFI_BUFFER_INTERNAL_SCRATCH_2(ver)); + if (ret) + goto err; + + return 0; +err: + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_intbufs_realloc); static u32 load_per_instance(struct venus_inst *inst) { @@ -339,12 +389,91 @@ static u32 load_per_type(struct venus_core *core, u32 session_type) return mbs_per_sec; } -static int load_scale_clocks(struct venus_core *core) +static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak) +{ + const struct venus_resources *res = inst->core->res; + const struct bw_tbl *bw_tbl; + unsigned int num_rows, i; + + *avg = 0; + *peak = 0; + + if (mbs == 0) + return; + + if (inst->session_type == VIDC_SESSION_TYPE_ENC) { + num_rows = res->bw_tbl_enc_size; + bw_tbl = res->bw_tbl_enc; + } else if (inst->session_type == VIDC_SESSION_TYPE_DEC) { + num_rows = res->bw_tbl_dec_size; + bw_tbl = res->bw_tbl_dec; + } else { + return; + } + + if (!bw_tbl || num_rows == 0) + return; + + for (i = 0; i < num_rows; i++) { + if (mbs > bw_tbl[i].mbs_per_sec) + break; + + if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) { + *avg = bw_tbl[i].avg_10bit; + *peak = bw_tbl[i].peak_10bit; + } else { + *avg = bw_tbl[i].avg; + *peak = bw_tbl[i].peak; + } + } +} + +static int load_scale_bw(struct venus_core *core) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec, avg, peak, total_avg = 0, total_peak = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + mbs_per_sec = load_per_instance(inst); + mbs_to_bw(inst, mbs_per_sec, &avg, &peak); + total_avg += avg; + total_peak += peak; + } + mutex_unlock(&core->lock); + + dev_dbg(core->dev, "total: avg_bw: %u, peak_bw: %u\n", + total_avg, total_peak); + + return icc_set_bw(core->video_path, total_avg, total_peak); +} + +static int set_clk_freq(struct venus_core *core, unsigned long freq) +{ + struct clk *clk = core->clks[0]; + int ret; + + ret = clk_set_rate(clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(core->core0_clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(core->core1_clk, freq); + if (ret) + return ret; + + return 0; +} + +static int scale_clocks(struct venus_inst *inst) { + struct venus_core *core = inst->core; const struct freq_tbl *table = core->res->freq_tbl; unsigned int num_rows = core->res->freq_tbl_size; unsigned long freq = table[0].freq; - struct clk *clk = core->clks[0]; struct device *dev = core->dev; u32 mbs_per_sec; unsigned int i; @@ -370,24 +499,126 @@ static int load_scale_clocks(struct venus_core *core) set_freq: - ret = clk_set_rate(clk, freq); - if (ret) - goto err; + ret = set_clk_freq(core, freq); + 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 = load_scale_bw(core); + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } - ret = clk_set_rate(core->core1_clk, freq); - if (ret) - goto err; + return 0; +} + +static unsigned long calculate_inst_freq(struct venus_inst *inst, + unsigned long filled_len) +{ + unsigned long vpp_freq = 0, vsp_freq = 0; + u32 fps = (u32)inst->fps; + u32 mbs_per_sec; + + mbs_per_sec = load_per_instance(inst) / fps; + + vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; + /* 21 / 20 is overhead factor */ + vpp_freq += vpp_freq / 20; + vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; + + /* 10 / 7 is overhead factor */ + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + vsp_freq += (inst->controls.enc.bitrate * 10) / 7; + else + vsp_freq += ((fps * filled_len * 8) * 10) / 7; + + return max(vpp_freq, vsp_freq); +} + +static int scale_clocks_v4(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct freq_tbl *table = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct device *dev = core->dev; + unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; + unsigned long filled_len = 0; + struct venus_buffer *buf, *n; + struct vb2_buffer *vb; + int i, ret; + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + vb = &buf->vb.vb2_buf; + filled_len = max(filled_len, vb2_get_plane_payload(vb, 0)); + } + + if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len) + return 0; + + freq = calculate_inst_freq(inst, filled_len); + inst->clk_data.freq = freq; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->clk_data.core_id == VIDC_CORE_ID_1) { + freq_core1 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_2) { + freq_core2 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_3) { + freq_core1 += inst->clk_data.freq; + freq_core2 += inst->clk_data.freq; + } + } + mutex_unlock(&core->lock); + + freq = max(freq_core1, freq_core2); + + if (freq >= table[0].freq) { + freq = table[0].freq; + dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n", + freq, table[0].freq); + goto set_freq; + } + + for (i = num_rows - 1 ; i >= 0; i--) { + if (freq <= table[i].freq) { + freq = table[i].freq; + break; + } + } + +set_freq: + + ret = set_clk_freq(core, freq); + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", + freq, ret); + return ret; + } + + ret = load_scale_bw(core); + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } return 0; +} -err: - dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); - return ret; +int venus_helper_load_scale_clocks(struct venus_inst *inst) +{ + if (IS_V4(inst->core)) + return scale_clocks_v4(inst); + + return scale_clocks(inst); } +EXPORT_SYMBOL_GPL(venus_helper_load_scale_clocks); static void fill_buffer_desc(const struct venus_buffer *buf, struct hfi_buffer_desc *bd, bool response) @@ -413,6 +644,57 @@ static void return_buf_error(struct venus_inst *inst, v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); } +static void +put_ts_metadata(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) +{ + struct vb2_buffer *vb = &vbuf->vb2_buf; + unsigned int i; + int slot = -1; + u64 ts_us = vb->timestamp; + + for (i = 0; i < ARRAY_SIZE(inst->tss); i++) { + if (!inst->tss[i].used) { + slot = i; + break; + } + } + + if (slot == -1) { + dev_dbg(inst->core->dev, "%s: no free slot\n", __func__); + return; + } + + do_div(ts_us, NSEC_PER_USEC); + + inst->tss[slot].used = true; + inst->tss[slot].flags = vbuf->flags; + inst->tss[slot].tc = vbuf->timecode; + inst->tss[slot].ts_us = ts_us; + inst->tss[slot].ts_ns = vb->timestamp; +} + +void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, + struct vb2_v4l2_buffer *vbuf) +{ + struct vb2_buffer *vb = &vbuf->vb2_buf; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(inst->tss); ++i) { + if (!inst->tss[i].used) + continue; + + if (inst->tss[i].ts_us != timestamp_us) + continue; + + inst->tss[i].used = false; + vbuf->flags |= inst->tss[i].flags; + vbuf->timecode = inst->tss[i].tc; + vb->timestamp = inst->tss[i].ts_ns; + break; + } +} +EXPORT_SYMBOL_GPL(venus_helper_get_ts_metadata); + static int session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) { @@ -437,6 +719,11 @@ 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; + + if (inst->session_type == VIDC_SESSION_TYPE_DEC) + put_ts_metadata(inst, vbuf); + + venus_helper_load_scale_clocks(inst); } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (inst->session_type == VIDC_SESSION_TYPE_ENC) fdata.buffer_type = HFI_BUFFER_OUTPUT; @@ -472,7 +759,7 @@ static bool is_dynamic_bufmode(struct venus_inst *inst) return caps->cap_bufs_mode_dynamic; } -static int session_unregister_bufs(struct venus_inst *inst) +int venus_helper_unregister_bufs(struct venus_inst *inst) { struct venus_buffer *buf, *n; struct hfi_buffer_desc bd; @@ -489,6 +776,7 @@ static int session_unregister_bufs(struct venus_inst *inst) return ret; } +EXPORT_SYMBOL_GPL(venus_helper_unregister_bufs); static int session_register_bufs(struct venus_inst *inst) { @@ -704,6 +992,7 @@ 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; + inst->clk_data.core_id = usage; if (!IS_V4(inst->core)) return 0; @@ -713,6 +1002,36 @@ int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) } EXPORT_SYMBOL_GPL(venus_helper_set_core_usage); +int venus_helper_init_codec_freq_data(struct venus_inst *inst) +{ + const struct codec_freq_data *data; + unsigned int i, data_size; + u32 pixfmt; + int ret = 0; + + if (!IS_V4(inst->core)) + return 0; + + data = inst->core->res->codec_freq_data; + data_size = inst->core->res->codec_freq_data_size; + pixfmt = inst->session_type == VIDC_SESSION_TYPE_DEC ? + inst->fmt_out->pixfmt : inst->fmt_cap->pixfmt; + + for (i = 0; i < data_size; i++) { + if (data[i].pixfmt == pixfmt && + data[i].session_type == inst->session_type) { + inst->clk_data.codec_freq_data = &data[i]; + break; + } + } + + if (!inst->clk_data.codec_freq_data) + ret = -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_init_codec_freq_data); + int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs) @@ -947,6 +1266,17 @@ 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); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dev_err(inst->core->dev, "%s field isn't supported\n", + __func__); + return -EINVAL; + } + } if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && vb2_plane_size(vb, 0) < out_buf_size) @@ -970,16 +1300,19 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); - if (!(inst->streamon_out & inst->streamon_cap)) + if (inst->session_type == VIDC_SESSION_TYPE_ENC && + !(inst->streamon_out && inst->streamon_cap)) goto unlock; - ret = is_buf_refed(inst, vbuf); - if (ret) - goto unlock; + if (vb2_start_streaming_called(vb->vb2_queue)) { + ret = is_buf_refed(inst, vbuf); + if (ret) + goto unlock; - ret = session_process_buf(inst, vbuf); - if (ret) - return_buf_error(inst, vbuf); + ret = session_process_buf(inst, vbuf); + if (ret) + return_buf_error(inst, vbuf); + } unlock: mutex_unlock(&inst->lock); @@ -1009,8 +1342,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) if (inst->streamon_out & inst->streamon_cap) { ret = hfi_session_stop(inst); ret |= hfi_session_unload_res(inst); - ret |= session_unregister_bufs(inst); - ret |= intbufs_free(inst); + ret |= venus_helper_unregister_bufs(inst); + ret |= venus_helper_intbufs_free(inst); ret |= hfi_session_deinit(inst); if (inst->session_error || core->sys_error) @@ -1021,7 +1354,7 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) venus_helper_free_dpb_bufs(inst); - load_scale_clocks(core); + venus_helper_load_scale_clocks(inst); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -1036,12 +1369,47 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) } EXPORT_SYMBOL_GPL(venus_helper_vb2_stop_streaming); +int venus_helper_process_initial_cap_bufs(struct venus_inst *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct v4l2_m2m_buffer *buf, *n; + int ret; + + v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buf, n) { + ret = session_process_buf(inst, &buf->vb); + if (ret) { + return_buf_error(inst, &buf->vb); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_process_initial_cap_bufs); + +int venus_helper_process_initial_out_bufs(struct venus_inst *inst) +{ + struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; + struct v4l2_m2m_buffer *buf, *n; + int ret; + + v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { + ret = session_process_buf(inst, &buf->vb); + if (ret) { + return_buf_error(inst, &buf->vb); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_process_initial_out_bufs); + int venus_helper_vb2_start_streaming(struct venus_inst *inst) { - struct venus_core *core = inst->core; int ret; - ret = intbufs_alloc(inst); + ret = venus_helper_intbufs_alloc(inst); if (ret) return ret; @@ -1049,7 +1417,7 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_bufs_free; - load_scale_clocks(core); + venus_helper_load_scale_clocks(inst); ret = hfi_session_load_res(inst); if (ret) @@ -1059,20 +1427,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: - session_unregister_bufs(inst); + venus_helper_unregister_bufs(inst); err_bufs_free: - intbufs_free(inst); + venus_helper_intbufs_free(inst); return ret; } EXPORT_SYMBOL_GPL(venus_helper_vb2_start_streaming); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 153783687a0c..34dcd0c13f06 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -9,6 +9,7 @@ #include <media/videobuf2-v4l2.h> struct venus_inst; +struct venus_core; bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt); struct vb2_v4l2_buffer *venus_helper_find_buf(struct venus_inst *inst, @@ -32,6 +33,7 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, unsigned int width, unsigned int height, u32 buftype); int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); +int venus_helper_init_codec_freq_data(struct venus_inst *inst); 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, @@ -53,4 +55,14 @@ 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); +int venus_helper_intbufs_alloc(struct venus_inst *inst); +int venus_helper_intbufs_free(struct venus_inst *inst); +int venus_helper_intbufs_realloc(struct venus_inst *inst); +int venus_helper_queue_dpb_bufs(struct venus_inst *inst); +int venus_helper_unregister_bufs(struct venus_inst *inst); +int venus_helper_load_scale_clocks(struct venus_inst *inst); +int venus_helper_process_initial_cap_bufs(struct venus_inst *inst); +int venus_helper_process_initial_out_bufs(struct venus_inst *inst); +void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, + struct vb2_v4l2_buffer *vbuf); #endif diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index 6ad0c1772ea7..3d8b1284d1f3 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -198,6 +198,9 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt) const struct hfi_ops *ops = core->ops; int ret; + if (inst->state != INST_UNINIT) + return -EINVAL; + inst->hfi_codec = to_codec_type(pixfmt); reinit_completion(&inst->done); @@ -276,6 +279,7 @@ int hfi_session_start(struct venus_inst *inst) return 0; } +EXPORT_SYMBOL_GPL(hfi_session_start); int hfi_session_stop(struct venus_inst *inst) { @@ -299,6 +303,7 @@ int hfi_session_stop(struct venus_inst *inst) return 0; } +EXPORT_SYMBOL_GPL(hfi_session_stop); int hfi_session_continue(struct venus_inst *inst) { @@ -328,6 +333,7 @@ int hfi_session_abort(struct venus_inst *inst) return 0; } +EXPORT_SYMBOL_GPL(hfi_session_abort); int hfi_session_load_res(struct venus_inst *inst) { @@ -374,15 +380,16 @@ int hfi_session_unload_res(struct venus_inst *inst) return 0; } +EXPORT_SYMBOL_GPL(hfi_session_unload_res); -int hfi_session_flush(struct venus_inst *inst) +int hfi_session_flush(struct venus_inst *inst, u32 type) { const struct hfi_ops *ops = inst->core->ops; int ret; reinit_completion(&inst->done); - ret = ops->session_flush(inst, HFI_FLUSH_ALL); + ret = ops->session_flush(inst, type); if (ret) return ret; diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index b121cb1427ac..855822c9f39b 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -161,7 +161,7 @@ int hfi_session_continue(struct venus_inst *inst); int hfi_session_abort(struct venus_inst *inst); int hfi_session_load_res(struct venus_inst *inst); int hfi_session_unload_res(struct venus_inst *inst); -int hfi_session_flush(struct venus_inst *inst); +int hfi_session_flush(struct venus_inst *inst, u32 type); int hfi_session_set_buffers(struct venus_inst *inst, struct hfi_buffer_desc *bd); int hfi_session_unset_buffers(struct venus_inst *inst, diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 7129a2aea09a..0d8855014ab3 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -1472,6 +1472,7 @@ 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; bool val; int ret; @@ -1487,6 +1488,10 @@ 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) + goto power_off; + /* * Power collapse sequence for Venus 3xx and 4xx versions: * 1. Check for ARM9 and video core to be idle by checking WFI bit @@ -1511,6 +1516,7 @@ static int venus_suspend_3xx(struct venus_core *core) if (ret) return ret; +power_off: mutex_lock(&hdev->lock); ret = venus_power_off(hdev); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index e1f998656c07..8feaf5daece9 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -37,42 +37,52 @@ static const struct venus_format vdec_formats[] = { .pixfmt = V4L2_PIX_FMT_MPEG4, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_H263, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_G, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_H264, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VP8, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_VP9, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_XVID, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, { .pixfmt = V4L2_PIX_FMT_HEVC, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, }, }; @@ -133,6 +143,7 @@ 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; + u32 szimage; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -161,14 +172,17 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, - pixmp->width, - pixmp->height); + szimage = venus_helper_get_framesz(pixmp->pixelformat, pixmp->width, + pixmp->height); - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pfmt[0].sizeimage = szimage; pfmt[0].bytesperline = ALIGN(pixmp->width, 128); - else + } else { + pfmt[0].sizeimage = clamp_t(u32, pfmt[0].sizeimage, 0, SZ_8M); + pfmt[0].sizeimage = max(pfmt[0].sizeimage, szimage); pfmt[0].bytesperline = 0; + } return fmt; } @@ -182,33 +196,56 @@ static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f) return 0; } +static int vdec_check_src_change(struct venus_inst *inst) +{ + int ret; + + if (inst->subscriptions & V4L2_EVENT_SOURCE_CHANGE && + inst->codec_state == VENUS_DEC_STATE_INIT && + !inst->reconfig) + return -EINVAL; + + if (inst->subscriptions & V4L2_EVENT_SOURCE_CHANGE) + return 0; + + /* + * The code snippet below is a workaround for backward compatibility + * with applications which doesn't support V4L2 events. It will be + * dropped in future once those applications are fixed. + */ + + if (inst->codec_state != VENUS_DEC_STATE_INIT) + goto done; + + ret = wait_event_timeout(inst->reconf_wait, inst->reconfig, + msecs_to_jiffies(100)); + if (!ret) + return -EINVAL; + + if (!(inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) || + !inst->reconfig) + dev_dbg(inst->core->dev, "%s: wrong state\n", __func__); + +done: + return 0; +} + static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct venus_inst *inst = to_inst(file); const struct venus_format *fmt = NULL; struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + int ret; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) fmt = inst->fmt_cap; else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) fmt = inst->fmt_out; - if (inst->reconfig) { - struct v4l2_format format = {}; - - inst->out_width = inst->reconfig_width; - inst->out_height = inst->reconfig_height; - inst->reconfig = false; - - format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - format.fmt.pix_mp.pixelformat = inst->fmt_cap->pixfmt; - format.fmt.pix_mp.width = inst->out_width; - format.fmt.pix_mp.height = inst->out_height; - - vdec_try_fmt_common(inst, &format); - - inst->width = format.fmt.pix_mp.width; - inst->height = format.fmt.pix_mp.height; + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + ret = vdec_check_src_change(inst); + if (ret) + return ret; } pixmp->pixelformat = fmt->pixfmt; @@ -266,6 +303,7 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) inst->ycbcr_enc = pixmp->ycbcr_enc; inst->quantization = pixmp->quantization; inst->xfer_func = pixmp->xfer_func; + inst->input_buf_size = pixmp->plane_fmt[0].sizeimage; } memset(&format, 0, sizeof(format)); @@ -351,6 +389,7 @@ static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) return -EINVAL; f->pixelformat = fmt->pixfmt; + f->flags = fmt->flags; return 0; } @@ -422,11 +461,18 @@ static int vdec_enum_framesizes(struct file *file, void *fh, static int vdec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { + struct venus_inst *inst = container_of(fh, struct venus_inst, fh); + int ret; + switch (sub->type) { case V4L2_EVENT_EOS: return v4l2_event_subscribe(fh, sub, 2, NULL); case V4L2_EVENT_SOURCE_CHANGE: - return v4l2_src_change_event_subscribe(fh, sub); + ret = v4l2_src_change_event_subscribe(fh, sub); + if (ret) + return ret; + inst->subscriptions |= V4L2_EVENT_SOURCE_CHANGE; + return 0; case V4L2_EVENT_CTRL: return v4l2_ctrl_subscribe_event(fh, sub); default: @@ -435,45 +481,35 @@ static int vdec_subscribe_event(struct v4l2_fh *fh, } static int -vdec_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) -{ - switch (cmd->cmd) { - case V4L2_DEC_CMD_STOP: - if (cmd->flags & V4L2_DEC_CMD_STOP_TO_BLACK) - return -EINVAL; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) { struct venus_inst *inst = to_inst(file); struct hfi_frame_data fdata = {0}; int ret; - ret = vdec_try_decoder_cmd(file, fh, cmd); + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, cmd); if (ret) return ret; mutex_lock(&inst->lock); - /* - * Implement V4L2_DEC_CMD_STOP by enqueue an empty buffer on decoder - * input to signal EOS. - */ - if (!(inst->streamon_out & inst->streamon_cap)) - goto unlock; + if (cmd->cmd == V4L2_DEC_CMD_STOP) { + /* + * Implement V4L2_DEC_CMD_STOP by enqueue an empty buffer on + * decoder input to signal EOS. + */ + if (!(inst->streamon_out && inst->streamon_cap)) + goto unlock; - fdata.buffer_type = HFI_BUFFER_INPUT; - fdata.flags |= HFI_BUFFERFLAG_EOS; - fdata.device_addr = 0xdeadbeef; + fdata.buffer_type = HFI_BUFFER_INPUT; + fdata.flags |= HFI_BUFFERFLAG_EOS; + fdata.device_addr = 0xdeadb000; - ret = hfi_session_process_buf(inst, &fdata); + ret = hfi_session_process_buf(inst, &fdata); + + if (!ret && inst->codec_state == VENUS_DEC_STATE_DECODING) + inst->codec_state = VENUS_DEC_STATE_DRAIN; + } unlock: mutex_unlock(&inst->lock); @@ -504,7 +540,7 @@ static const struct v4l2_ioctl_ops vdec_ioctl_ops = { .vidioc_enum_framesizes = vdec_enum_framesizes, .vidioc_subscribe_event = vdec_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_try_decoder_cmd = vdec_try_decoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, .vidioc_decoder_cmd = vdec_decoder_cmd, }; @@ -634,20 +670,22 @@ static int vdec_output_conf(struct venus_inst *inst) return 0; } -static int vdec_init_session(struct venus_inst *inst) +static int vdec_session_init(struct venus_inst *inst) { int ret; ret = hfi_session_init(inst, inst->fmt_out->pixfmt); - if (ret) + if (ret == -EINVAL) + return 0; + else if (ret) return ret; - ret = venus_helper_set_input_resolution(inst, inst->out_width, - inst->out_height); + ret = venus_helper_set_input_resolution(inst, frame_width_min(inst), + frame_height_min(inst)); if (ret) goto deinit; - ret = venus_helper_set_color_format(inst, inst->fmt_cap->pixfmt); + ret = venus_helper_init_codec_freq_data(inst); if (ret) goto deinit; @@ -666,26 +704,19 @@ static int vdec_num_buffers(struct venus_inst *inst, unsigned int *in_num, *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; + return ret; *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); if (ret) - goto deinit; + return ret; *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); -deinit: - hfi_session_deinit(inst); - - return ret; + return 0; } static int vdec_queue_setup(struct vb2_queue *q, @@ -718,6 +749,10 @@ static int vdec_queue_setup(struct vb2_queue *q, return 0; } + ret = vdec_session_init(inst); + if (ret) + return ret; + ret = vdec_num_buffers(inst, &in_num, &out_num); if (ret) return ret; @@ -728,6 +763,7 @@ static int vdec_queue_setup(struct vb2_queue *q, sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, inst->out_width, inst->out_height); + sizes[0] = max(sizes[0], inst->input_buf_size); inst->input_buf_size = sizes[0]; *num_buffers = max(*num_buffers, in_num); inst->num_input_bufs = *num_buffers; @@ -741,6 +777,11 @@ static int vdec_queue_setup(struct vb2_queue *q, inst->output_buf_size = sizes[0]; *num_buffers = max(*num_buffers, out_num); inst->num_output_bufs = *num_buffers; + + mutex_lock(&inst->lock); + if (inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) + inst->codec_state = VENUS_DEC_STATE_STOPPED; + mutex_unlock(&inst->lock); break; default: ret = -EINVAL; @@ -777,80 +818,295 @@ static int vdec_verify_conf(struct venus_inst *inst) return 0; } -static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) +static int vdec_start_capture(struct venus_inst *inst) { - struct venus_inst *inst = vb2_get_drv_priv(q); int ret; - mutex_lock(&inst->lock); + if (!inst->streamon_out) + return 0; - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - inst->streamon_out = 1; - else - inst->streamon_cap = 1; + if (inst->codec_state == VENUS_DEC_STATE_DECODING) { + if (inst->reconfig) + goto reconfigure; - if (!(inst->streamon_out & inst->streamon_cap)) { - mutex_unlock(&inst->lock); + venus_helper_queue_dpb_bufs(inst); + venus_helper_process_initial_cap_bufs(inst); + inst->streamon_cap = 1; return 0; } - venus_helper_init_instance(inst); + if (inst->codec_state != VENUS_DEC_STATE_STOPPED) + return 0; - inst->reconfig = false; - inst->sequence_cap = 0; - inst->sequence_out = 0; +reconfigure: + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT); + if (ret) + return ret; - ret = vdec_init_session(inst); + ret = vdec_output_conf(inst); if (ret) - goto bufs_done; + return ret; + + ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, + VB2_MAX_FRAME, VB2_MAX_FRAME); + if (ret) + return ret; + + ret = venus_helper_intbufs_realloc(inst); + if (ret) + goto err; + + ret = venus_helper_alloc_dpb_bufs(inst); + if (ret) + goto err; + + ret = venus_helper_queue_dpb_bufs(inst); + if (ret) + goto free_dpb_bufs; + + ret = venus_helper_process_initial_cap_bufs(inst); + if (ret) + goto free_dpb_bufs; + + venus_helper_load_scale_clocks(inst); + + ret = hfi_session_continue(inst); + if (ret) + goto free_dpb_bufs; + + inst->codec_state = VENUS_DEC_STATE_DECODING; + + inst->streamon_cap = 1; + inst->sequence_cap = 0; + inst->reconfig = false; + + return 0; + +free_dpb_bufs: + venus_helper_free_dpb_bufs(inst); +err: + return ret; +} + +static int vdec_start_output(struct venus_inst *inst) +{ + int ret; + + if (inst->codec_state == VENUS_DEC_STATE_SEEK) { + ret = venus_helper_process_initial_out_bufs(inst); + inst->codec_state = VENUS_DEC_STATE_DECODING; + goto done; + } + + if (inst->codec_state == VENUS_DEC_STATE_INIT || + inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) { + ret = venus_helper_process_initial_out_bufs(inst); + goto done; + } + + if (inst->codec_state != VENUS_DEC_STATE_DEINIT) + return -EINVAL; + + venus_helper_init_instance(inst); + inst->sequence_out = 0; + inst->reconfig = false; ret = vdec_set_properties(inst); if (ret) - goto deinit_sess; + return ret; ret = vdec_output_conf(inst); if (ret) - goto deinit_sess; + return ret; ret = vdec_verify_conf(inst); if (ret) - goto deinit_sess; + return ret; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, VB2_MAX_FRAME, VB2_MAX_FRAME); if (ret) - goto deinit_sess; + return ret; - ret = venus_helper_alloc_dpb_bufs(inst); + ret = venus_helper_vb2_start_streaming(inst); if (ret) - goto deinit_sess; + return ret; - ret = venus_helper_vb2_start_streaming(inst); + ret = venus_helper_process_initial_out_bufs(inst); if (ret) - goto deinit_sess; + return ret; - mutex_unlock(&inst->lock); + inst->codec_state = VENUS_DEC_STATE_INIT; + +done: + inst->streamon_out = 1; + return ret; +} + +static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + int ret; + + mutex_lock(&inst->lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + ret = vdec_start_capture(inst); + else + ret = vdec_start_output(inst); + + if (ret) + goto error; + mutex_unlock(&inst->lock); return 0; -deinit_sess: - hfi_session_deinit(inst); -bufs_done: +error: venus_helper_buffers_done(inst, VB2_BUF_STATE_QUEUED); + mutex_unlock(&inst->lock); + return ret; +} + +static void vdec_cancel_dst_buffers(struct venus_inst *inst) +{ + struct vb2_v4l2_buffer *buf; + + while ((buf = v4l2_m2m_dst_buf_remove(inst->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); +} + +static int vdec_stop_capture(struct venus_inst *inst) +{ + int ret = 0; + + switch (inst->codec_state) { + case VENUS_DEC_STATE_DECODING: + ret = hfi_session_flush(inst, HFI_FLUSH_ALL); + /* fallthrough */ + case VENUS_DEC_STATE_DRAIN: + vdec_cancel_dst_buffers(inst); + inst->codec_state = VENUS_DEC_STATE_STOPPED; + break; + case VENUS_DEC_STATE_DRC: + ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT); + vdec_cancel_dst_buffers(inst); + inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; + INIT_LIST_HEAD(&inst->registeredbufs); + venus_helper_free_dpb_bufs(inst); + break; + default: + return 0; + } + + return ret; +} + +static int vdec_stop_output(struct venus_inst *inst) +{ + int ret = 0; + + switch (inst->codec_state) { + case VENUS_DEC_STATE_DECODING: + case VENUS_DEC_STATE_DRAIN: + case VENUS_DEC_STATE_STOPPED: + ret = hfi_session_flush(inst, HFI_FLUSH_ALL); + inst->codec_state = VENUS_DEC_STATE_SEEK; + break; + case VENUS_DEC_STATE_INIT: + case VENUS_DEC_STATE_CAPTURE_SETUP: + ret = hfi_session_flush(inst, HFI_FLUSH_INPUT); + break; + default: + break; + } + + return ret; +} + +static void vdec_stop_streaming(struct vb2_queue *q) +{ + struct venus_inst *inst = vb2_get_drv_priv(q); + int ret = -EINVAL; + + mutex_lock(&inst->lock); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + ret = vdec_stop_capture(inst); + else + ret = vdec_stop_output(inst); + + venus_helper_buffers_done(inst, VB2_BUF_STATE_ERROR); + + if (ret) + goto unlock; + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) inst->streamon_out = 0; else inst->streamon_cap = 0; + +unlock: mutex_unlock(&inst->lock); - return ret; +} + +static void vdec_session_release(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + int ret, abort = 0; + + mutex_lock(&inst->lock); + + inst->codec_state = VENUS_DEC_STATE_DEINIT; + + ret = hfi_session_stop(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = hfi_session_unload_res(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = venus_helper_unregister_bufs(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = venus_helper_intbufs_free(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + ret = hfi_session_deinit(inst); + abort = (ret && ret != -EINVAL) ? 1 : 0; + + if (inst->session_error || core->sys_error) + abort = 1; + + if (abort) + hfi_session_abort(inst); + + venus_helper_free_dpb_bufs(inst); + venus_helper_load_scale_clocks(inst); + INIT_LIST_HEAD(&inst->registeredbufs); + + mutex_unlock(&inst->lock); +} + +static int vdec_buf_init(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + inst->buf_count++; + + return venus_helper_vb2_buf_init(vb); +} + +static void vdec_buf_cleanup(struct vb2_buffer *vb) +{ + struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + + inst->buf_count--; + if (!inst->buf_count) + vdec_session_release(inst); } static const struct vb2_ops vdec_vb2_ops = { .queue_setup = vdec_queue_setup, - .buf_init = venus_helper_vb2_buf_init, + .buf_init = vdec_buf_init, + .buf_cleanup = vdec_buf_cleanup, .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = vdec_start_streaming, - .stop_streaming = venus_helper_vb2_stop_streaming, + .stop_streaming = vdec_stop_streaming, .buf_queue = venus_helper_vb2_buf_queue, }; @@ -874,9 +1130,9 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, vbuf->flags = flags; vbuf->field = V4L2_FIELD_NONE; + vb = &vbuf->vb2_buf; if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - vb = &vbuf->vb2_buf; vb2_set_plane_payload(vb, 0, bytesused); vb->planes[0].data_offset = data_offset; vb->timestamp = timestamp_us * NSEC_PER_USEC; @@ -886,28 +1142,85 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; v4l2_event_queue_fh(&inst->fh, &ev); + + if (inst->codec_state == VENUS_DEC_STATE_DRAIN) + inst->codec_state = VENUS_DEC_STATE_STOPPED; } } else { vbuf->sequence = inst->sequence_out++; } + venus_helper_get_ts_metadata(inst, timestamp_us, vbuf); + if (hfi_flags & HFI_BUFFERFLAG_READONLY) venus_helper_acquire_buf_ref(vbuf); if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT) state = VB2_BUF_STATE_ERROR; + if (hfi_flags & HFI_BUFFERFLAG_DROP_FRAME) { + state = VB2_BUF_STATE_ERROR; + vb2_set_plane_payload(vb, 0, 0); + vb->timestamp = 0; + } + v4l2_m2m_buf_done(vbuf, state); } +static void vdec_event_change(struct venus_inst *inst, + struct hfi_event_data *ev_data, bool sufficient) +{ + static const struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; + struct device *dev = inst->core->dev_dec; + struct v4l2_format format = {}; + + mutex_lock(&inst->lock); + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + format.fmt.pix_mp.pixelformat = inst->fmt_cap->pixfmt; + format.fmt.pix_mp.width = ev_data->width; + format.fmt.pix_mp.height = ev_data->height; + + vdec_try_fmt_common(inst, &format); + + inst->width = format.fmt.pix_mp.width; + inst->height = format.fmt.pix_mp.height; + + inst->out_width = ev_data->width; + inst->out_height = ev_data->height; + + dev_dbg(dev, "event %s sufficient resources (%ux%u)\n", + sufficient ? "" : "not", ev_data->width, ev_data->height); + + if (sufficient) { + hfi_session_continue(inst); + } else { + switch (inst->codec_state) { + case VENUS_DEC_STATE_INIT: + inst->codec_state = VENUS_DEC_STATE_CAPTURE_SETUP; + break; + case VENUS_DEC_STATE_DECODING: + inst->codec_state = VENUS_DEC_STATE_DRC; + break; + default: + break; + } + } + + inst->reconfig = true; + v4l2_event_queue_fh(&inst->fh, &ev); + wake_up(&inst->reconf_wait); + + mutex_unlock(&inst->lock); +} + static void vdec_event_notify(struct venus_inst *inst, u32 event, struct hfi_event_data *data) { struct venus_core *core = inst->core; struct device *dev = core->dev_dec; - static const struct v4l2_event ev = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; switch (event) { case EVT_SESSION_ERROR: @@ -917,18 +1230,10 @@ static void vdec_event_notify(struct venus_inst *inst, u32 event, case EVT_SYS_EVENT_CHANGE: switch (data->event_type) { case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES: - hfi_session_continue(inst); - dev_dbg(dev, "event sufficient resources\n"); + vdec_event_change(inst, data, true); break; case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES: - inst->reconfig_height = data->height; - inst->reconfig_width = data->width; - inst->reconfig = true; - - v4l2_event_queue_fh(&inst->fh, &ev); - - dev_dbg(dev, "event not sufficient resources (%ux%u)\n", - data->width, data->height); + vdec_event_change(inst, data, false); break; case HFI_EVENT_RELEASE_BUFFER_REFERENCE: venus_helper_release_buf_ref(inst, data->tag); @@ -949,20 +1254,25 @@ static const struct hfi_inst_ops vdec_hfi_ops = { static void vdec_inst_init(struct venus_inst *inst) { + inst->hfi_codec = HFI_VIDEO_CODEC_H264; inst->fmt_out = &vdec_formats[6]; inst->fmt_cap = &vdec_formats[0]; - inst->width = 1280; - inst->height = ALIGN(720, 32); - inst->out_width = 1280; - inst->out_height = 720; + inst->width = frame_width_min(inst); + inst->height = ALIGN(frame_height_min(inst), 32); + inst->out_width = frame_width_min(inst); + inst->out_height = frame_height_min(inst); inst->fps = 30; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 30; - inst->hfi_codec = HFI_VIDEO_CODEC_H264; + inst->opb_buftype = HFI_BUFFER_OUTPUT; +} + +static void vdec_m2m_device_run(void *priv) +{ } static const struct v4l2_m2m_ops vdec_m2m_ops = { - .device_run = venus_helper_m2m_device_run, + .device_run = vdec_m2m_device_run, .job_abort = venus_helper_m2m_job_abort, }; @@ -980,7 +1290,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct venus_buffer); src_vq->allow_zero_bytesused = 1; - src_vq->min_buffers_needed = 1; + src_vq->min_buffers_needed = 0; src_vq->dev = inst->core->dev; ret = vb2_queue_init(src_vq); if (ret) @@ -994,7 +1304,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct venus_buffer); dst_vq->allow_zero_bytesused = 1; - dst_vq->min_buffers_needed = 1; + dst_vq->min_buffers_needed = 0; dst_vq->dev = inst->core->dev; ret = vb2_queue_init(dst_vq); if (ret) { @@ -1024,7 +1334,9 @@ static int vdec_open(struct file *file) inst->core = core; inst->session_type = VIDC_SESSION_TYPE_DEC; inst->num_output_bufs = 1; - + inst->codec_state = VENUS_DEC_STATE_DEINIT; + inst->buf_count = 0; + init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); ret = pm_runtime_get_sync(core->dev_dec); @@ -1104,9 +1416,6 @@ static const struct v4l2_file_operations vdec_fops = { .unlocked_ioctl = video_ioctl2, .poll = v4l2_m2m_fop_poll, .mmap = v4l2_m2m_fop_mmap, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = v4l2_compat_ioctl32, -#endif }; static int vdec_probe(struct platform_device *pdev) diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 300350bfe8bd..3a963cbd342a 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -7,6 +7,7 @@ #include <media/v4l2-ctrls.h> #include "core.h" +#include "helpers.h" #include "vdec.h" static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) @@ -38,7 +39,9 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct venus_inst *inst = ctrl_to_inst(ctrl); struct vdec_controls *ctr = &inst->controls.dec; + struct hfi_buffer_requirements bufreq; union hfi_get_property hprop; + enum hfi_version ver = inst->core->res->hfi_version; u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; int ret; @@ -62,7 +65,9 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ctrl->val = ctr->post_loop_deb_mode; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: - ctrl->val = inst->num_output_bufs; + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (!ret) + ctrl->val = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); break; default: return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index a5f3d2c46bea..453edf966d4f 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -294,6 +294,7 @@ 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; + u32 sizeimage; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -325,9 +326,10 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, - pixmp->width, - pixmp->height); + sizeimage = venus_helper_get_framesz(pixmp->pixelformat, + pixmp->width, + pixmp->height); + pfmt[0].sizeimage = max(ALIGN(pfmt[0].sizeimage, SZ_4K), sizeimage); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) pfmt[0].bytesperline = ALIGN(pixmp->width, 128); @@ -399,8 +401,10 @@ static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) inst->fmt_out = fmt; - else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { inst->fmt_cap = fmt; + inst->output_buf_size = pixmp->plane_fmt[0].sizeimage; + } return 0; } @@ -838,6 +842,10 @@ static int venc_init_session(struct venus_inst *inst) if (ret) goto deinit; + ret = venus_helper_init_codec_freq_data(inst); + if (ret) + goto deinit; + ret = venc_set_properties(inst); if (ret) goto deinit; @@ -918,6 +926,7 @@ static int venc_queue_setup(struct vb2_queue *q, sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, inst->width, inst->height); + sizes[0] = max(sizes[0], inst->output_buf_size); inst->output_buf_size = sizes[0]; break; default: @@ -1230,9 +1239,6 @@ static const struct v4l2_file_operations venc_fops = { .unlocked_ioctl = video_ioctl2, .poll = v4l2_m2m_fop_poll, .mmap = v4l2_m2m_fop_mmap, -#ifdef CONFIG_COMPAT - .compat_ioctl32 = v4l2_compat_ioctl32, -#endif }; static int venc_probe(struct platform_device *pdev) |