diff options
Diffstat (limited to 'drivers/media/platform/qcom/venus')
-rw-r--r-- | drivers/media/platform/qcom/venus/Kconfig | 2 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/core.c | 16 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/core.h | 2 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_parser.c | 100 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_venus.c | 18 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/pm_helpers.c | 38 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/vdec.c | 18 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/venc_ctrls.c | 9 |
8 files changed, 143 insertions, 60 deletions
diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig index bc2e410b29cb..ffb731ecd48c 100644 --- a/drivers/media/platform/qcom/venus/Kconfig +++ b/drivers/media/platform/qcom/venus/Kconfig @@ -2,7 +2,7 @@ config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" depends on V4L_MEM2MEM_DRIVERS depends on VIDEO_DEV && QCOM_SMEM - depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST + depends on (ARCH_QCOM && ARM64 && IOMMU_API) || COMPILE_TEST select OF_DYNAMIC if ARCH_QCOM select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 77d48578ecd2..d305d74bb152 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -438,7 +438,7 @@ static int venus_probe(struct platform_device *pdev) ret = v4l2_device_register(dev, &core->v4l2_dev); if (ret) - goto err_core_deinit; + goto err_hfi_destroy; platform_set_drvdata(pdev, core); @@ -476,24 +476,24 @@ static int venus_probe(struct platform_device *pdev) ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC); if (ret) - goto err_venus_shutdown; + goto err_core_deinit; ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC); if (ret) - goto err_venus_shutdown; + goto err_core_deinit; ret = pm_runtime_put_sync(dev); if (ret) { pm_runtime_get_noresume(dev); - goto err_dev_unregister; + goto err_core_deinit; } venus_dbgfs_init(core); return 0; -err_dev_unregister: - v4l2_device_unregister(&core->v4l2_dev); +err_core_deinit: + hfi_core_deinit(core, false); err_venus_shutdown: venus_shutdown(core); err_firmware_deinit: @@ -506,9 +506,9 @@ err_runtime_disable: pm_runtime_put_noidle(dev); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); + v4l2_device_unregister(&core->v4l2_dev); +err_hfi_destroy: hfi_destroy(core); -err_core_deinit: - hfi_core_deinit(core, false); err_core_put: if (core->pm_ops->core_put) core->pm_ops->core_put(core); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index abeeafa86697..b412e0c5515a 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -172,6 +172,7 @@ struct venus_format { * @venus_ver: the venus firmware version * @dump_core: a flag indicating that a core dump is required * @ocs: OF changeset pointer + * @hwmode_dev: a flag indicating that HW_CTRL_TRIGGER is used in clock driver */ struct venus_core { void __iomem *base; @@ -235,6 +236,7 @@ struct venus_core { } venus_ver; unsigned long dump_core; struct of_changeset *ocs; + bool hwmode_dev; }; struct vdec_controls { diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 3df241dc3a11..1b3db2caa99f 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -19,6 +19,8 @@ static void init_codecs(struct venus_core *core) struct hfi_plat_caps *caps = core->caps, *cap; unsigned long bit; + core->codecs_count = 0; + if (hweight_long(core->dec_codecs) + hweight_long(core->enc_codecs) > MAX_CODEC_NUM) return; @@ -62,7 +64,7 @@ fill_buf_mode(struct hfi_plat_caps *cap, const void *data, unsigned int num) cap->cap_bufs_mode_dynamic = true; } -static void +static int parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) { struct hfi_buffer_alloc_mode_supported *mode = data; @@ -70,7 +72,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) u32 *type; if (num_entries > MAX_ALLOC_MODE_ENTRIES) - return; + return -EINVAL; type = mode->data; @@ -82,6 +84,8 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) type++; } + + return sizeof(*mode); } static void fill_profile_level(struct hfi_plat_caps *cap, const void *data, @@ -96,7 +100,7 @@ static void fill_profile_level(struct hfi_plat_caps *cap, const void *data, cap->num_pl += num; } -static void +static int parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data) { struct hfi_profile_level_supported *pl = data; @@ -104,12 +108,14 @@ parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data) struct hfi_profile_level pl_arr[HFI_MAX_PROFILE_COUNT] = {}; if (pl->profile_count > HFI_MAX_PROFILE_COUNT) - return; + return -EINVAL; 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); + + return pl->profile_count * sizeof(*proflevel) + sizeof(u32); } static void @@ -124,7 +130,7 @@ fill_caps(struct hfi_plat_caps *cap, const void *data, unsigned int num) cap->num_caps += num; } -static void +static int parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) { struct hfi_capabilities *caps = data; @@ -133,12 +139,14 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) struct hfi_capability caps_arr[MAX_CAP_ENTRIES] = {}; if (num_caps > MAX_CAP_ENTRIES) - return; + return -EINVAL; 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); + + return sizeof(*caps); } static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts, @@ -153,7 +161,7 @@ static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts, cap->num_fmts += num_fmts; } -static void +static int parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) { struct hfi_uncompressed_format_supported *fmt = data; @@ -162,7 +170,8 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {}; u32 entries = fmt->format_entries; unsigned int i = 0; - u32 num_planes; + u32 num_planes = 0; + u32 size; while (entries) { num_planes = pinfo->num_planes; @@ -172,7 +181,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) i++; if (i >= MAX_FMT_ENTRIES) - return; + return -EINVAL; if (pinfo->num_planes > MAX_PLANES) break; @@ -184,9 +193,13 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, fill_raw_fmts, rawfmts, i); + size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32)) + + 2 * sizeof(u32); + + return size; } -static void parse_codecs(struct venus_core *core, void *data) +static int parse_codecs(struct venus_core *core, void *data) { struct hfi_codec_supported *codecs = data; @@ -198,21 +211,27 @@ static void parse_codecs(struct venus_core *core, void *data) core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC; } + + return sizeof(*codecs); } -static void parse_max_sessions(struct venus_core *core, const void *data) +static int 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; + + return sizeof(*sessions); } -static void parse_codecs_mask(u32 *codecs, u32 *domain, void *data) +static int parse_codecs_mask(u32 *codecs, u32 *domain, void *data) { struct hfi_codec_mask_supported *mask = data; *codecs = mask->codecs; *domain = mask->video_domains; + + return sizeof(*mask); } static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain) @@ -281,8 +300,9 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) 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; + u32 *words = buf, *payload, codecs = 0, domain = 0; + u32 *frame_size = buf + size; + u32 rem_bytes = size; int ret; ret = hfi_platform_parser(core, inst); @@ -299,38 +319,66 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, memset(core->caps, 0, sizeof(core->caps)); } - while (words_count) { - data = word + 1; + while (words < frame_size) { + payload = words + 1; - switch (*word) { + switch (*words) { case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: - parse_codecs(core, data); + if (rem_bytes <= sizeof(struct hfi_codec_supported)) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + ret = parse_codecs(core, payload); + if (ret < 0) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + init_codecs(core); break; case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: - parse_max_sessions(core, data); + if (rem_bytes <= sizeof(struct hfi_max_sessions_supported)) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + ret = parse_max_sessions(core, payload); break; case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: - parse_codecs_mask(&codecs, &domain, data); + if (rem_bytes <= sizeof(struct hfi_codec_mask_supported)) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + ret = parse_codecs_mask(&codecs, &domain, payload); break; case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: - parse_raw_formats(core, codecs, domain, data); + if (rem_bytes <= sizeof(struct hfi_uncompressed_format_supported)) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + ret = parse_raw_formats(core, codecs, domain, payload); break; case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: - parse_caps(core, codecs, domain, data); + if (rem_bytes <= sizeof(struct hfi_capabilities)) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + ret = parse_caps(core, codecs, domain, payload); break; case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: - parse_profile_level(core, codecs, domain, data); + if (rem_bytes <= sizeof(struct hfi_profile_level_supported)) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + ret = parse_profile_level(core, codecs, domain, payload); break; case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: - parse_alloc_mode(core, codecs, domain, data); + if (rem_bytes <= sizeof(struct hfi_buffer_alloc_mode_supported)) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + ret = parse_alloc_mode(core, codecs, domain, payload); break; default: + ret = sizeof(u32); break; } - word++; - words_count--; + if (ret < 0) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + words += ret / sizeof(u32); + rem_bytes -= ret; } if (!core->max_sessions_supported) diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index a9167867063c..b5f2ea879950 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -187,6 +187,9 @@ static int venus_write_queue(struct venus_hfi_device *hdev, /* ensure rd/wr indices's are read from memory */ rmb(); + if (qsize > IFACEQ_QUEUE_SIZE / 4) + return -EINVAL; + if (wr_idx >= rd_idx) empty_space = qsize - (wr_idx - rd_idx); else @@ -255,6 +258,9 @@ static int venus_read_queue(struct venus_hfi_device *hdev, wr_idx = qhdr->write_idx; qsize = qhdr->q_size; + if (qsize > IFACEQ_QUEUE_SIZE / 4) + return -EINVAL; + /* make sure data is valid before using it */ rmb(); @@ -1035,18 +1041,26 @@ static void venus_sfr_print(struct venus_hfi_device *hdev) { struct device *dev = hdev->core->dev; struct hfi_sfr *sfr = hdev->sfr.kva; + u32 size; void *p; if (!sfr) return; - p = memchr(sfr->data, '\0', sfr->buf_size); + size = sfr->buf_size; + if (!size) + return; + + if (size > ALIGNED_SFR_SIZE) + size = ALIGNED_SFR_SIZE; + + p = memchr(sfr->data, '\0', size); /* * SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates * that Venus is in the process of crashing. */ if (!p) - sfr->data[sfr->buf_size - 1] = '\0'; + sfr->data[size - 1] = '\0'; dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data); } diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 33a5a659c0ad..409aa9bd0b5d 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -412,9 +412,17 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) u32 val; int ret; - if (IS_V6(core)) - return dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable); - else if (coreid == VIDC_CORE_ID_1) { + ret = dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable); + if (ret == -EOPNOTSUPP) { + core->hwmode_dev = false; + goto legacy; + } + + core->hwmode_dev = true; + return ret; + +legacy: + if (coreid == VIDC_CORE_ID_1) { ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; } else { @@ -450,7 +458,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec0_clks); - if (!IS_V6(core)) { + if (!core->hwmode_dev) { ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); if (ret) return ret; @@ -468,7 +476,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) vcodec_clks_disable(core, core->vcodec1_clks); - if (!IS_V6(core)) { + if (!core->hwmode_dev) { ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); if (ret) return ret; @@ -491,11 +499,9 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - if (!IS_V6(core)) { - ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); - if (ret) - return ret; - } + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; ret = vcodec_clks_enable(core, core->vcodec0_clks); if (ret) @@ -511,11 +517,9 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) if (ret < 0) return ret; - if (!IS_V6(core)) { - ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); - if (ret) - return ret; - } + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; ret = vcodec_clks_enable(core, core->vcodec1_clks); if (ret) @@ -811,7 +815,7 @@ static int vdec_power_v4(struct device *dev, int on) else vcodec_clks_disable(core, core->vcodec0_clks); - vcodec_control_v4(core, VIDC_CORE_ID_1, false); + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); return ret; } @@ -856,7 +860,7 @@ static int venc_power_v4(struct device *dev, int on) else vcodec_clks_disable(core, core->vcodec1_clks); - vcodec_control_v4(core, VIDC_CORE_ID_2, false); + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); return ret; } diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 9f82882b77bc..99ce5fd41577 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -154,14 +154,14 @@ find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type) return NULL; for (i = 0; i < size; i++) { - bool valid; + bool valid = false; if (fmt[i].type != type) continue; if (V4L2_TYPE_IS_OUTPUT(type)) { valid = venus_helper_check_codec(inst, fmt[i].pixfmt); - } else if (V4L2_TYPE_IS_CAPTURE(type)) { + } else { valid = venus_helper_check_format(inst, fmt[i].pixfmt); if (fmt[i].pixfmt == V4L2_PIX_FMT_QC10C && @@ -1110,10 +1110,20 @@ static int vdec_start_output(struct venus_inst *inst) if (inst->codec_state == VENUS_DEC_STATE_SEEK) { ret = venus_helper_process_initial_out_bufs(inst); - if (inst->next_buf_last) + if (ret) + return ret; + + if (inst->next_buf_last) { inst->codec_state = VENUS_DEC_STATE_DRC; - else + } else { inst->codec_state = VENUS_DEC_STATE_DECODING; + + if (inst->streamon_cap) { + ret = venus_helper_queue_dpb_bufs(inst); + if (ret) + return ret; + } + } goto done; } diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 51801a962ed2..4d36c44f9d44 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -662,11 +662,16 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_COLORIMETRY_HDR10_CLL_INFO, - v4l2_ctrl_ptr_create(&p_hdr10_cll)); + v4l2_ctrl_ptr_create(&p_hdr10_cll), + v4l2_ctrl_ptr_create(NULL), + v4l2_ctrl_ptr_create(NULL)); v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY, - v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering)); + v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering), + v4l2_ctrl_ptr_create(NULL), + v4l2_ctrl_ptr_create(NULL)); + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE, |