diff options
Diffstat (limited to 'drivers/media/platform/qcom')
-rw-r--r-- | drivers/media/platform/qcom/camss/camss-video.c | 6 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/core.c | 167 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/core.h | 33 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/firmware.c | 13 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/helpers.c | 448 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/helpers.h | 4 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_cmds.c | 2 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_helper.h | 6 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_parser.c | 1 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/hfi_parser.h | 5 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/pm_helpers.c | 959 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/pm_helpers.h | 65 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/vdec.c | 86 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/venc.c | 81 | ||||
-rw-r--r-- | drivers/media/platform/qcom/venus/venc_ctrls.c | 8 |
16 files changed, 1362 insertions, 524 deletions
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 1d50dfbbb762..cdbd6dba1122 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -745,7 +745,7 @@ static int video_open(struct file *file) file->private_data = vfh; - ret = v4l2_pipeline_pm_use(&vdev->entity, 1); + ret = v4l2_pipeline_pm_get(&vdev->entity); if (ret < 0) { dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", ret); @@ -771,7 +771,7 @@ static int video_release(struct file *file) vb2_fop_release(file); - v4l2_pipeline_pm_use(&vdev->entity, 0); + v4l2_pipeline_pm_put(&vdev->entity); file->private_data = NULL; @@ -921,7 +921,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, vdev->lock = &video->lock; strscpy(vdev->name, name, sizeof(vdev->name)); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", ret); diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index b44b11b03e12..64af0bc1edae 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -3,7 +3,7 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ - hfi_parser.o + hfi_parser.o pm_helpers.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 07312a2fab24..194b10b98767 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -3,7 +3,6 @@ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */ -#include <linux/clk.h> #include <linux/init.h> #include <linux/interconnect.h> #include <linux/ioctl.h> @@ -19,9 +18,8 @@ #include <media/v4l2-ioctl.h> #include "core.h" -#include "vdec.h" -#include "venc.h" #include "firmware.h" +#include "pm_helpers.h" static void venus_event_notify(struct venus_core *core, u32 event) { @@ -100,50 +98,6 @@ static void venus_sys_error_handler(struct work_struct *work) mutex_unlock(&core->lock); } -static int venus_clks_get(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - struct device *dev = core->dev; - unsigned int i; - - for (i = 0; i < res->clks_num; i++) { - core->clks[i] = devm_clk_get(dev, res->clks[i]); - if (IS_ERR(core->clks[i])) - return PTR_ERR(core->clks[i]); - } - - return 0; -} - -static int venus_clks_enable(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - unsigned int i; - int ret; - - for (i = 0; i < res->clks_num; i++) { - ret = clk_prepare_enable(core->clks[i]); - if (ret) - goto err; - } - - return 0; -err: - while (i--) - clk_disable_unprepare(core->clks[i]); - - return ret; -} - -static void venus_clks_disable(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - unsigned int i = res->clks_num; - - while (i--) - clk_disable_unprepare(core->clks[i]); -} - static u32 to_v4l2_codec_type(u32 codec) { switch (codec) { @@ -256,9 +210,15 @@ static int venus_probe(struct platform_device *pdev) if (!core->res) return -ENODEV; - ret = venus_clks_get(core); - if (ret) - return ret; + core->pm_ops = venus_pm_get(core->res->hfi_version); + if (!core->pm_ops) + return -ENODEV; + + if (core->pm_ops->core_get) { + ret = core->pm_ops->core_get(dev); + if (ret) + return ret; + } ret = dma_set_mask_and_coherent(dev, core->res->dma_mask); if (ret) @@ -350,6 +310,7 @@ err_runtime_disable: static int venus_remove(struct platform_device *pdev) { struct venus_core *core = platform_get_drvdata(pdev); + const struct venus_pm_ops *pm_ops = core->pm_ops; struct device *dev = core->dev; int ret; @@ -368,6 +329,9 @@ static int venus_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); + if (pm_ops->core_put) + pm_ops->core_put(dev); + icc_put(core->video_path); icc_put(core->cpucfg_path); @@ -379,11 +343,15 @@ static int venus_remove(struct platform_device *pdev) static __maybe_unused int venus_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + const struct venus_pm_ops *pm_ops = core->pm_ops; int ret; ret = hfi_core_suspend(core); + if (ret) + return ret; - venus_clks_disable(core); + if (pm_ops->core_power) + ret = pm_ops->core_power(dev, POWER_OFF); return ret; } @@ -391,21 +359,16 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) static __maybe_unused int venus_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + const struct venus_pm_ops *pm_ops = core->pm_ops; int ret; - ret = venus_clks_enable(core); - if (ret) - return ret; - - ret = hfi_core_resume(core, false); - if (ret) - goto err_clks_disable; - - return 0; + if (pm_ops->core_power) { + ret = pm_ops->core_power(dev, POWER_ON); + if (ret) + return ret; + } -err_clks_disable: - venus_clks_disable(core); - return ret; + return hfi_core_resume(core, false); } static const struct dev_pm_ops venus_pm_ops = { @@ -463,6 +426,9 @@ static const struct venus_resources msm8996_res = { .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), .clks = {"core", "iface", "bus", "mbus" }, .clks_num = 4, + .vcodec0_clks = { "core" }, + .vcodec1_clks = { "core" }, + .vcodec_clks_num = 1, .max_load = 2563200, .hfi_version = HFI_VERSION_3XX, .vmem_id = VIDC_RESOURCE_NONE, @@ -517,6 +483,35 @@ static const struct venus_resources sdm845_res = { .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, + .vcodec0_clks = { "core", "bus" }, + .vcodec1_clks = { "core", "bus" }, + .vcodec_clks_num = 2, + .max_load = 3110400, /* 4096x2160@90 */ + .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 venus_resources sdm845_res_v2 = { + .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, + .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, + .vcodec1_clks = { "vcodec1_core", "vcodec1_bus" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = { "venus", "vcodec0", "vcodec1" }, + .vcodec_pmdomains_num = 3, + .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, .vmem_id = VIDC_RESOURCE_NONE, @@ -526,10 +521,56 @@ static const struct venus_resources sdm845_res = { .fwname = "qcom/venus-5.2/venus.mdt", }; +static const struct freq_tbl sc7180_freq_table[] = { + { 0, 500000000 }, + { 0, 434000000 }, + { 0, 340000000 }, + { 0, 270000000 }, + { 0, 150000000 }, +}; + +static const struct bw_tbl sc7180_bw_table_enc[] = { + { 972000, 750000, 0, 0, 0 }, /* 3840x2160@30 */ + { 489600, 451000, 0, 0, 0 }, /* 1920x1080@60 */ + { 244800, 234000, 0, 0, 0 }, /* 1920x1080@30 */ +}; + +static const struct bw_tbl sc7180_bw_table_dec[] = { + { 1036800, 1386000, 0, 1875000, 0 }, /* 4096x2160@30 */ + { 489600, 865000, 0, 1146000, 0 }, /* 1920x1080@60 */ + { 244800, 530000, 0, 583000, 0 }, /* 1920x1080@30 */ +}; + +static const struct venus_resources sc7180_res = { + .freq_tbl = sc7180_freq_table, + .freq_tbl_size = ARRAY_SIZE(sc7180_freq_table), + .bw_tbl_enc = sc7180_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc), + .bw_tbl_dec = sc7180_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sc7180_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, + .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = { "venus", "vcodec0" }, + .vcodec_pmdomains_num = 2, + .vcodec_num = 1, + .hfi_version = HFI_VERSION_4XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/venus-5.4/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, }, + { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, + { .compatible = "qcom,sc7180-venus", .data = &sc7180_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 11585fb3cae3..bd3ac6a4501f 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -14,7 +14,9 @@ #include "hfi.h" -#define VIDC_CLKS_NUM_MAX 4 +#define VIDC_CLKS_NUM_MAX 4 +#define VIDC_VCODEC_CLKS_NUM_MAX 2 +#define VIDC_PMDOMAINS_NUM_MAX 3 struct freq_tbl { unsigned int load; @@ -55,6 +57,12 @@ struct venus_resources { unsigned int codec_freq_data_size; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; + const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + unsigned int vcodec_clks_num; + const char * const vcodec_pmdomains[VIDC_PMDOMAINS_NUM_MAX]; + unsigned int vcodec_pmdomains_num; + unsigned int vcodec_num; enum hfi_version hfi_version; u32 max_load; unsigned int vmem_id; @@ -100,10 +108,10 @@ struct venus_caps { * @base: IO memory base address * @irq: Venus irq * @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 + * @vcodec0_clks: an array of vcodec0 struct clk pointers + * @vcodec1_clks: an array of vcodec1 struct clk pointers + * @pd_dl_venus: pmdomain device-link for venus domain + * @pmdomains: an array of pmdomains struct device pointers * @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 @@ -132,12 +140,12 @@ struct venus_core { void __iomem *base; int irq; 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 clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; struct icc_path *cpucfg_path; + struct device_link *pd_dl_venus; + struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX]; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -159,6 +167,7 @@ struct venus_core { unsigned int error; bool sys_error; const struct hfi_core_ops *core_ops; + const struct venus_pm_ops *pm_ops; unsigned long enc_codecs; unsigned long dec_codecs; unsigned int max_sessions_supported; @@ -172,6 +181,8 @@ struct venus_core { struct delayed_work work; struct venus_caps caps[MAX_CODEC_NUM]; unsigned int codecs_count; + unsigned int core0_usage_count; + unsigned int core1_usage_count; }; struct vdec_controls { @@ -187,6 +198,7 @@ struct venc_controls { u32 bitrate_mode; u32 bitrate; u32 bitrate_peak; + u32 rc_enable; u32 h264_i_period; u32 h264_entropy_mode; @@ -344,6 +356,7 @@ struct venus_inst { unsigned int subscriptions; int buf_count; struct venus_ts_metadata tss[VIDEO_MAX_FRAME]; + unsigned long payloads[VIDEO_MAX_FRAME]; u64 fps; struct v4l2_fract timeperframe; const struct venus_format *fmt_out; @@ -370,6 +383,8 @@ struct venus_inst { const struct hfi_inst_ops *ops; u32 session_type; union hfi_get_property hprop; + unsigned int core_acquired: 1; + unsigned int bit_depth; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index d3d1748a7ef6..8801a6a7543d 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -44,8 +44,14 @@ static void venus_reset_cpu(struct venus_core *core) int venus_set_hw_state(struct venus_core *core, bool resume) { - if (core->use_tz) - return qcom_scm_set_remote_state(resume, 0); + int ret; + + if (core->use_tz) { + ret = qcom_scm_set_remote_state(resume, 0); + if (resume && ret == -EINVAL) + ret = 0; + return ret; + } if (resume) venus_reset_cpu(core); @@ -100,8 +106,7 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, mem_va = memremap(r.start, *mem_size, MEMREMAP_WC); if (!mem_va) { - dev_err(dev, "unable to map memory region: %pa+%zx\n", - &r.start, *mem_size); + dev_err(dev, "unable to map memory region: %pR\n", &r); ret = -ENOMEM; goto err_release_fw; } diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index a172f1ac0b35..bcc603804041 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -3,12 +3,8 @@ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */ -#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> #include <linux/slab.h> #include <media/videobuf2-dma-sg.h> #include <media/v4l2-mem2mem.h> @@ -17,7 +13,7 @@ #include "core.h" #include "helpers.h" #include "hfi_helper.h" -#include "hfi_venus_io.h" +#include "pm_helpers.h" struct intbuf { struct list_head list; @@ -360,266 +356,6 @@ err: } EXPORT_SYMBOL_GPL(venus_helper_intbufs_realloc); -static u32 load_per_instance(struct venus_inst *inst) -{ - u32 mbs; - - if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP)) - return 0; - - mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16); - - return mbs * inst->fps; -} - -static u32 load_per_type(struct venus_core *core, u32 session_type) -{ - struct venus_inst *inst = NULL; - u32 mbs_per_sec = 0; - - mutex_lock(&core->lock); - list_for_each_entry(inst, &core->instances, list) { - if (inst->session_type != session_type) - continue; - - mbs_per_sec += load_per_instance(inst); - } - mutex_unlock(&core->lock); - - return mbs_per_sec; -} - -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 device *dev = core->dev; - u32 mbs_per_sec; - unsigned int i; - int ret; - - mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) + - load_per_type(core, VIDC_SESSION_TYPE_DEC); - - if (mbs_per_sec > core->res->max_load) - dev_warn(dev, "HW is overloaded, needed: %d max: %d\n", - mbs_per_sec, core->res->max_load); - - if (!mbs_per_sec && num_rows > 1) { - freq = table[num_rows - 1].freq; - goto set_freq; - } - - for (i = 0; i < num_rows; i++) { - if (mbs_per_sec > table[i].load) - break; - freq = table[i].freq; - } - -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; -} - -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; -} - -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) { @@ -723,7 +459,7 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) if (inst->session_type == VIDC_SESSION_TYPE_DEC) put_ts_metadata(inst, vbuf); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (inst->session_type == VIDC_SESSION_TYPE_ENC) fdata.buffer_type = HFI_BUFFER_OUTPUT; @@ -890,6 +626,78 @@ static u32 get_framesize_raw_nv12_ubwc(u32 width, u32 height) max(extradata, y_stride * 48), SZ_4K); } +static u32 get_framesize_raw_p010(u32 width, u32 height) +{ + u32 y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines; + + y_stride = ALIGN(width * 2, 256); + uv_stride = ALIGN(width * 2, 256); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN((height + 1) >> 1, 16); + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines; + + return ALIGN((y_plane + uv_plane), SZ_4K); +} + +static u32 get_framesize_raw_p010_ubwc(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_sclines, uv_sclines; + u32 y_ubwc_plane, uv_ubwc_plane; + u32 y_meta_stride, y_meta_scanlines; + u32 uv_meta_stride, uv_meta_scanlines; + u32 y_meta_plane, uv_meta_plane; + u32 size; + + y_stride = ALIGN(width * 2, 256); + uv_stride = ALIGN(width * 2, 256); + y_sclines = ALIGN(height, 16); + uv_sclines = ALIGN((height + 1) >> 1, 16); + + y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K); + uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K); + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64); + y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16); + y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K); + uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64); + uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K); + + size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane; + + return ALIGN(size, SZ_4K); +} + +static u32 get_framesize_raw_yuv420_tp10_ubwc(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_sclines, uv_sclines; + u32 y_ubwc_plane, uv_ubwc_plane; + u32 y_meta_stride, y_meta_scanlines; + u32 uv_meta_stride, uv_meta_scanlines; + u32 y_meta_plane, uv_meta_plane; + u32 extradata = SZ_16K; + u32 size; + + y_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); + uv_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); + y_sclines = ALIGN(height, 16); + uv_sclines = ALIGN((height + 1) >> 1, 16); + + y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K); + uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K); + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 48), 64); + y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16); + y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K); + uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64); + uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K); + + size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane; + size += max(extradata + SZ_8K, y_stride * 48); + + return ALIGN(size, SZ_4K); +} + u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) { switch (hfi_fmt) { @@ -898,6 +706,12 @@ u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) return get_framesize_raw_nv12(width, height); case HFI_COLOR_FORMAT_NV12_UBWC: return get_framesize_raw_nv12_ubwc(width, height); + case HFI_COLOR_FORMAT_P010: + return get_framesize_raw_p010(width, height); + case HFI_COLOR_FORMAT_P010_UBWC: + return get_framesize_raw_p010_ubwc(width, height); + case HFI_COLOR_FORMAT_YUV420_TP10_UBWC: + return get_framesize_raw_yuv420_tp10_ubwc(width, height); default: return 0; } @@ -987,21 +801,6 @@ int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) } 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; - - inst->clk_data.core_id = usage; - 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_init_codec_freq_data(struct venus_inst *inst) { const struct codec_freq_data *data; @@ -1289,6 +1088,15 @@ int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb) } EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_prepare); +static void cache_payload(struct venus_inst *inst, struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + unsigned int idx = vbuf->vb2_buf.index; + + if (vbuf->vb2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->payloads[idx] = vb2_get_plane_payload(vb, 0); +} + void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -1300,6 +1108,8 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); + cache_payload(inst, vb); + if (inst->session_type == VIDC_SESSION_TYPE_ENC && !(inst->streamon_out && inst->streamon_cap)) goto unlock; @@ -1354,7 +1164,7 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -1365,6 +1175,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) else inst->streamon_cap = 0; + venus_pm_release_core(inst); + mutex_unlock(&inst->lock); } EXPORT_SYMBOL_GPL(venus_helper_vb2_stop_streaming); @@ -1417,7 +1229,7 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_bufs_free; - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); ret = hfi_session_load_res(inst); if (ret) @@ -1512,6 +1324,27 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, if (!caps) return -EINVAL; + if (inst->bit_depth == VIDC_BITDEPTH_10 && + inst->session_type == VIDC_SESSION_TYPE_DEC) { + found_ubwc = + find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, + HFI_COLOR_FORMAT_YUV420_TP10_UBWC); + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, + HFI_COLOR_FORMAT_NV12); + if (found_ubwc && found) { + /* + * Hard-code DPB buffers to be 10bit UBWC and decoder + * output buffers in 8bit NV12 until V4L2 is able to + * expose compressed/tiled formats to applications. + */ + *out_fmt = HFI_COLOR_FORMAT_YUV420_TP10_UBWC; + *out2_fmt = HFI_COLOR_FORMAT_NV12; + return 0; + } + + return -EINVAL; + } + if (ubwc) { ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE; found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, @@ -1542,52 +1375,3 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, 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 34dcd0c13f06..b64875564064 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -34,7 +34,6 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, 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, unsigned int output2_bufs); @@ -53,14 +52,11 @@ 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); 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, diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 4f645076abfb..c67e412f8201 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1207,6 +1207,8 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: + case HFI_PROPERTY_PARAM_VENC_SESSION_QP: + case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE: /* not implemented on Venus 4xx */ return -ENOTSUPP; default: diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index b70551e296b7..f6613df1d16b 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -550,6 +550,7 @@ struct hfi_bitrate { #define HFI_CAPABILITY_LCU_SIZE 0x14 #define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15 #define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16 +#define HFI_CAPABILITY_MAX_VIDEOCORES 0x2b struct hfi_capability { u32 capability_type; @@ -792,6 +793,9 @@ struct hfi_h264_vui_timing_info { u32 time_scale; }; +#define VIDC_BITDEPTH_8 0x00000 +#define VIDC_BITDEPTH_10 0x20002 + struct hfi_bit_depth { u32 buffer_type; u32 bit_depth; @@ -840,8 +844,10 @@ struct hfi_extradata_input_crop { #define HFI_COLOR_FORMAT_10_BIT_BASE 0x4000 #define HFI_COLOR_FORMAT_YUV420_TP10 0x4002 +#define HFI_COLOR_FORMAT_P010 0x4003 #define HFI_COLOR_FORMAT_NV12_UBWC 0x8002 #define HFI_COLOR_FORMAT_YUV420_TP10_UBWC 0xc002 +#define HFI_COLOR_FORMAT_P010_UBWC 0xc003 #define HFI_COLOR_FORMAT_RGBA8888_UBWC 0x8010 struct hfi_uncompressed_format_select { diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 2293d936e49c..7f515a4b9bd1 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -181,6 +181,7 @@ static void parse_codecs(struct venus_core *core, void *data) if (IS_V1(core)) { core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; + core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC; } } diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h index 3e931c747e19..264e6dd2415f 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.h +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -107,4 +107,9 @@ static inline u32 frate_step(struct venus_inst *inst) return cap_step(inst, HFI_CAPABILITY_FRAMERATE); } +static inline u32 core_num_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_MAX_VIDEOCORES); +} + #endif diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c new file mode 100644 index 000000000000..abf93158857b --- /dev/null +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -0,0 +1,959 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Ltd. + * + * Author: Stanimir Varbanov <stanimir.varbanov@linaro.org> + */ +#include <linux/clk.h> +#include <linux/interconnect.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> +#include <media/v4l2-mem2mem.h> + +#include "core.h" +#include "hfi_parser.h" +#include "hfi_venus_io.h" +#include "pm_helpers.h" + +static bool legacy_binding; + +static int core_clks_get(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *dev = core->dev; + unsigned int i; + + for (i = 0; i < res->clks_num; i++) { + core->clks[i] = devm_clk_get(dev, res->clks[i]); + if (IS_ERR(core->clks[i])) + return PTR_ERR(core->clks[i]); + } + + return 0; +} + +static int core_clks_enable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + for (i = 0; i < res->clks_num; i++) { + ret = clk_prepare_enable(core->clks[i]); + if (ret) + goto err; + } + + return 0; +err: + while (i--) + clk_disable_unprepare(core->clks[i]); + + return ret; +} + +static void core_clks_disable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i = res->clks_num; + + while (i--) + clk_disable_unprepare(core->clks[i]); +} + +static int core_clks_set_rate(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->vcodec0_clks[0], freq); + if (ret) + return ret; + + ret = clk_set_rate(core->vcodec1_clks[0], freq); + if (ret) + return ret; + + return 0; +} + +static int vcodec_clks_get(struct venus_core *core, struct device *dev, + struct clk **clks, const char * const *id) +{ + const struct venus_resources *res = core->res; + unsigned int i; + + for (i = 0; i < res->vcodec_clks_num; i++) { + if (!id[i]) + continue; + clks[i] = devm_clk_get(dev, id[i]); + if (IS_ERR(clks[i])) + return PTR_ERR(clks[i]); + } + + return 0; +} + +static int vcodec_clks_enable(struct venus_core *core, struct clk **clks) +{ + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + for (i = 0; i < res->vcodec_clks_num; i++) { + ret = clk_prepare_enable(clks[i]); + if (ret) + goto err; + } + + return 0; +err: + while (i--) + clk_disable_unprepare(clks[i]); + + return ret; +} + +static void vcodec_clks_disable(struct venus_core *core, struct clk **clks) +{ + const struct venus_resources *res = core->res; + unsigned int i = res->vcodec_clks_num; + + while (i--) + clk_disable_unprepare(clks[i]); +} + +static u32 load_per_instance(struct venus_inst *inst) +{ + u32 mbs; + + if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP)) + return 0; + + mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16); + + return mbs * inst->fps; +} + +static u32 load_per_type(struct venus_core *core, u32 session_type) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->session_type != session_type) + continue; + + mbs_per_sec += load_per_instance(inst); + } + mutex_unlock(&core->lock); + + return mbs_per_sec; +} + +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 load_scale_v1(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 device *dev = core->dev; + u32 mbs_per_sec; + unsigned int i; + int ret; + + mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) + + load_per_type(core, VIDC_SESSION_TYPE_DEC); + + if (mbs_per_sec > core->res->max_load) + dev_warn(dev, "HW is overloaded, needed: %d max: %d\n", + mbs_per_sec, core->res->max_load); + + if (!mbs_per_sec && num_rows > 1) { + freq = table[num_rows - 1].freq; + goto set_freq; + } + + for (i = 0; i < num_rows; i++) { + if (mbs_per_sec > table[i].load) + break; + freq = table[i].freq; + } + +set_freq: + + ret = core_clks_set_rate(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; +} + +static int core_get_v1(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return core_clks_get(core); +} + +static int core_power_v1(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + if (on == POWER_ON) + ret = core_clks_enable(core); + else + core_clks_disable(core); + + return ret; +} + +static const struct venus_pm_ops pm_ops_v1 = { + .core_get = core_get_v1, + .core_power = core_power_v1, + .load_scale = load_scale_v1, +}; + +static void +vcodec_control_v3(struct venus_core *core, u32 session_type, bool enable) +{ + void __iomem *ctrl; + + 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); +} + +static int vdec_get_v3(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return vcodec_clks_get(core, dev, core->vcodec0_clks, + core->res->vcodec0_clks); +} + +static int vdec_power_v3(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, true); + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec0_clks); + else + vcodec_clks_disable(core, core->vcodec0_clks); + + vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, false); + + return ret; +} + +static int venc_get_v3(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return vcodec_clks_get(core, dev, core->vcodec1_clks, + core->res->vcodec1_clks); +} + +static int venc_power_v3(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, true); + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec1_clks); + else + vcodec_clks_disable(core, core->vcodec1_clks); + + vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, false); + + return ret; +} + +static const struct venus_pm_ops pm_ops_v3 = { + .core_get = core_get_v1, + .core_power = core_power_v1, + .vdec_get = vdec_get_v3, + .vdec_power = vdec_power_v3, + .venc_get = venc_get_v3, + .venc_power = venc_power_v3, + .load_scale = load_scale_v1, +}; + +static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) +{ + void __iomem *ctrl, *stat; + u32 val; + int ret; + + if (coreid == VIDC_CORE_ID_1) { + 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; +} + +static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) +{ + int ret; + + if (coreid_mask & VIDC_CORE_ID_1) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + vcodec_clks_disable(core, core->vcodec0_clks); + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret) + return ret; + + ret = pm_runtime_put_sync(core->pmdomains[1]); + if (ret < 0) + return ret; + } + + if (coreid_mask & VIDC_CORE_ID_2) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + vcodec_clks_disable(core, core->vcodec1_clks); + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret) + return ret; + + ret = pm_runtime_put_sync(core->pmdomains[2]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) +{ + int ret; + + if (coreid_mask & VIDC_CORE_ID_1) { + ret = pm_runtime_get_sync(core->pmdomains[1]); + if (ret < 0) + 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) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret < 0) + return ret; + } + + if (coreid_mask & VIDC_CORE_ID_2) { + ret = pm_runtime_get_sync(core->pmdomains[2]); + if (ret < 0) + 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) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret < 0) + return ret; + } + + return 0; +} + +static void +min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) +{ + u32 mbs_per_sec, load, core1_load = 0, core2_load = 0; + u32 cores_max = core_num_max(inst); + struct venus_core *core = inst->core; + struct venus_inst *inst_pos; + unsigned long vpp_freq; + u32 coreid; + + mutex_lock(&core->lock); + + list_for_each_entry(inst_pos, &core->instances, list) { + if (inst_pos == inst) + continue; + vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; + coreid = inst_pos->clk_data.core_id; + + mbs_per_sec = load_per_instance(inst_pos); + load = mbs_per_sec * vpp_freq; + + if ((coreid & VIDC_CORE_ID_3) == VIDC_CORE_ID_3) { + core1_load += load / 2; + core2_load += load / 2; + } else if (coreid & VIDC_CORE_ID_1) { + core1_load += load; + } else if (coreid & VIDC_CORE_ID_2) { + core2_load += load; + } + } + + *min_coreid = core1_load <= core2_load ? + VIDC_CORE_ID_1 : VIDC_CORE_ID_2; + *min_load = min(core1_load, core2_load); + + if (cores_max < VIDC_CORE_ID_2 || core->res->vcodec_num < 2) { + *min_coreid = VIDC_CORE_ID_1; + *min_load = core1_load; + } + + mutex_unlock(&core->lock); +} + +static int decide_core(struct venus_inst *inst) +{ + const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; + struct venus_core *core = inst->core; + u32 min_coreid, min_load, inst_load; + struct hfi_videocores_usage_type cu; + unsigned long max_freq; + + if (legacy_binding) { + if (inst->session_type == VIDC_SESSION_TYPE_DEC) + cu.video_core_enable_mask = VIDC_CORE_ID_1; + else + cu.video_core_enable_mask = VIDC_CORE_ID_2; + + goto done; + } + + if (inst->clk_data.core_id != VIDC_CORE_ID_DEFAULT) + return 0; + + inst_load = load_per_instance(inst); + inst_load *= inst->clk_data.codec_freq_data->vpp_freq; + max_freq = core->res->freq_tbl[0].freq; + + min_loaded_core(inst, &min_coreid, &min_load); + + if ((inst_load + min_load) > max_freq) { + dev_warn(core->dev, "HW is overloaded, needed: %u max: %lu\n", + inst_load, max_freq); + return -EINVAL; + } + + inst->clk_data.core_id = min_coreid; + cu.video_core_enable_mask = min_coreid; + +done: + return hfi_session_set_property(inst, ptype, &cu); +} + +static int acquire_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + unsigned int coreid_mask = 0; + + if (inst->core_acquired) + return 0; + + inst->core_acquired = true; + + if (inst->clk_data.core_id & VIDC_CORE_ID_1) { + if (core->core0_usage_count++) + return 0; + + coreid_mask = VIDC_CORE_ID_1; + } + + if (inst->clk_data.core_id & VIDC_CORE_ID_2) { + if (core->core1_usage_count++) + return 0; + + coreid_mask |= VIDC_CORE_ID_2; + } + + return poweron_coreid(core, coreid_mask); +} + +static int release_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + unsigned int coreid_mask = 0; + int ret; + + if (!inst->core_acquired) + return 0; + + if (inst->clk_data.core_id & VIDC_CORE_ID_1) { + if (--core->core0_usage_count) + goto done; + + coreid_mask = VIDC_CORE_ID_1; + } + + if (inst->clk_data.core_id & VIDC_CORE_ID_2) { + if (--core->core1_usage_count) + goto done; + + coreid_mask |= VIDC_CORE_ID_2; + } + + ret = poweroff_coreid(core, coreid_mask); + if (ret) + return ret; + +done: + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; + return 0; +} + +static int coreid_power_v4(struct venus_inst *inst, int on) +{ + struct venus_core *core = inst->core; + int ret; + + if (legacy_binding) + return 0; + + if (on == POWER_ON) { + ret = decide_core(inst); + if (ret) + return ret; + + mutex_lock(&core->lock); + ret = acquire_core(inst); + mutex_unlock(&core->lock); + } else { + mutex_lock(&core->lock); + ret = release_core(inst); + mutex_unlock(&core->lock); + } + + return ret; +} + +static int vdec_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (!legacy_binding) + return 0; + + return vcodec_clks_get(core, dev, core->vcodec0_clks, + core->res->vcodec0_clks); +} + +static void vdec_put_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + unsigned int i; + + if (!legacy_binding) + return; + + for (i = 0; i < core->res->vcodec_clks_num; i++) + core->vcodec0_clks[i] = NULL; +} + +static int vdec_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (!legacy_binding) + return 0; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec0_clks); + else + vcodec_clks_disable(core, core->vcodec0_clks); + + vcodec_control_v4(core, VIDC_CORE_ID_1, false); + + return ret; +} + +static int venc_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (!legacy_binding) + return 0; + + return vcodec_clks_get(core, dev, core->vcodec1_clks, + core->res->vcodec1_clks); +} + +static void venc_put_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + unsigned int i; + + if (!legacy_binding) + return; + + for (i = 0; i < core->res->vcodec_clks_num; i++) + core->vcodec1_clks[i] = NULL; +} + +static int venc_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (!legacy_binding) + return 0; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec1_clks); + else + vcodec_clks_disable(core, core->vcodec1_clks); + + vcodec_control_v4(core, VIDC_CORE_ID_2, false); + + return ret; +} + +static int vcodec_domains_get(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + struct device *pd; + unsigned int i; + + if (!res->vcodec_pmdomains_num) + return -ENODEV; + + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + pd = dev_pm_domain_attach_by_name(dev, + res->vcodec_pmdomains[i]); + if (IS_ERR(pd)) + return PTR_ERR(pd); + core->pmdomains[i] = pd; + } + + core->pd_dl_venus = device_link_add(dev, core->pmdomains[0], + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS | + DL_FLAG_RPM_ACTIVE); + if (!core->pd_dl_venus) + return -ENODEV; + + return 0; +} + +static void vcodec_domains_put(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + unsigned int i; + + if (!res->vcodec_pmdomains_num) + return; + + if (core->pd_dl_venus) + device_link_del(core->pd_dl_venus); + + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + if (IS_ERR_OR_NULL(core->pmdomains[i])) + continue; + dev_pm_domain_detach(core->pmdomains[i], true); + } +} + +static int core_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + int ret; + + ret = core_clks_get(core); + if (ret) + return ret; + + if (!res->vcodec_pmdomains_num) + legacy_binding = true; + + dev_info(dev, "%s legacy binding\n", legacy_binding ? "" : "non"); + + ret = vcodec_clks_get(core, dev, core->vcodec0_clks, res->vcodec0_clks); + if (ret) + return ret; + + ret = vcodec_clks_get(core, dev, core->vcodec1_clks, res->vcodec1_clks); + if (ret) + return ret; + + if (legacy_binding) + return 0; + + ret = vcodec_domains_get(dev); + if (ret) + return ret; + + return 0; +} + +static void core_put_v4(struct device *dev) +{ + if (legacy_binding) + return; + + vcodec_domains_put(dev); +} + +static int core_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + if (on == POWER_ON) + ret = core_clks_enable(core); + else + core_clks_disable(core); + + return ret; +} + +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 load_scale_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 device *dev = core->dev; + unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; + unsigned long filled_len = 0; + int i, ret; + + for (i = 0; i < inst->num_input_bufs; i++) + filled_len = max(filled_len, inst->payloads[i]); + + 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 = core_clks_set_rate(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; +} + +static const struct venus_pm_ops pm_ops_v4 = { + .core_get = core_get_v4, + .core_put = core_put_v4, + .core_power = core_power_v4, + .vdec_get = vdec_get_v4, + .vdec_put = vdec_put_v4, + .vdec_power = vdec_power_v4, + .venc_get = venc_get_v4, + .venc_put = venc_put_v4, + .venc_power = venc_power_v4, + .coreid_power = coreid_power_v4, + .load_scale = load_scale_v4, +}; + +const struct venus_pm_ops *venus_pm_get(enum hfi_version version) +{ + switch (version) { + case HFI_VERSION_1XX: + default: + return &pm_ops_v1; + case HFI_VERSION_3XX: + return &pm_ops_v3; + case HFI_VERSION_4XX: + return &pm_ops_v4; + } + + return NULL; +} diff --git a/drivers/media/platform/qcom/venus/pm_helpers.h b/drivers/media/platform/qcom/venus/pm_helpers.h new file mode 100644 index 000000000000..aa2f6afa2354 --- /dev/null +++ b/drivers/media/platform/qcom/venus/pm_helpers.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2019 Linaro Ltd. */ +#ifndef __VENUS_PM_HELPERS_H__ +#define __VENUS_PM_HELPERS_H__ + +struct device; + +#define POWER_ON 1 +#define POWER_OFF 0 + +struct venus_pm_ops { + int (*core_get)(struct device *dev); + void (*core_put)(struct device *dev); + int (*core_power)(struct device *dev, int on); + + int (*vdec_get)(struct device *dev); + void (*vdec_put)(struct device *dev); + int (*vdec_power)(struct device *dev, int on); + + int (*venc_get)(struct device *dev); + void (*venc_put)(struct device *dev); + int (*venc_power)(struct device *dev, int on); + + int (*coreid_power)(struct venus_inst *inst, int on); + + int (*load_scale)(struct venus_inst *inst); +}; + +const struct venus_pm_ops *venus_pm_get(enum hfi_version version); + +static inline int venus_pm_load_scale(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + + if (!core->pm_ops || !core->pm_ops->load_scale) + return 0; + + return core->pm_ops->load_scale(inst); +} + +static inline int venus_pm_acquire_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; + + if (pm_ops && pm_ops->coreid_power) + ret = pm_ops->coreid_power(inst, POWER_ON); + + return ret; +} + +static inline int venus_pm_release_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; + + if (pm_ops && pm_ops->coreid_power) + ret = pm_ops->coreid_power(inst, POWER_OFF); + + return ret; +} + +#endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 8feaf5daece9..4ed2628585a1 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -20,6 +20,7 @@ #include "core.h" #include "helpers.h" #include "vdec.h" +#include "pm_helpers.h" /* * Three resons to keep MPLANE formats (despite that the number of planes @@ -578,10 +579,6 @@ static int vdec_output_conf(struct venus_inst *inst) 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); @@ -868,7 +865,7 @@ reconfigure: if (ret) goto free_dpb_bufs; - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); ret = hfi_session_continue(inst); if (ret) @@ -950,6 +947,10 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&inst->lock); + ret = venus_pm_acquire_core(inst); + if (ret) + goto error; + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ret = vdec_start_capture(inst); else @@ -1076,7 +1077,8 @@ static void vdec_session_release(struct venus_inst *inst) hfi_session_abort(inst); venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); + venus_pm_release_core(inst); INIT_LIST_HEAD(&inst->registeredbufs); mutex_unlock(&inst->lock); @@ -1191,6 +1193,9 @@ static void vdec_event_change(struct venus_inst *inst, inst->out_width = ev_data->width; inst->out_height = ev_data->height; + if (inst->bit_depth != ev_data->bit_depth) + inst->bit_depth = ev_data->bit_depth; + dev_dbg(dev, "event %s sufficient resources (%ux%u)\n", sufficient ? "" : "not", ev_data->width, ev_data->height); @@ -1336,6 +1341,9 @@ static int vdec_open(struct file *file) inst->num_output_bufs = 1; inst->codec_state = VENUS_DEC_STATE_DEINIT; inst->buf_count = 0; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; + inst->bit_depth = VIDC_BITDEPTH_8; init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); @@ -1432,20 +1440,14 @@ static int vdec_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - 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); - } + platform_set_drvdata(pdev, core); - 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); + if (core->pm_ops->vdec_get) { + ret = core->pm_ops->vdec_get(dev); + if (ret) + return ret; } - platform_set_drvdata(pdev, core); - vdev = video_device_alloc(); if (!vdev) return -ENOMEM; @@ -1458,7 +1460,7 @@ static int vdec_probe(struct platform_device *pdev) vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto err_vdev_release; @@ -1482,57 +1484,33 @@ static int vdec_remove(struct platform_device *pdev) video_unregister_device(core->vdev_dec); pm_runtime_disable(core->dev_dec); + if (core->pm_ops->vdec_put) + core->pm_ops->vdec_put(core->dev_dec); + return 0; } static __maybe_unused int vdec_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - 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); + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - clk_disable_unprepare(core->core0_clk); + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_OFF); - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + return ret; } static __maybe_unused int vdec_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); - if (ret) - return ret; - - ret = clk_prepare_enable(core->core0_clk); - if (ret) - goto err_power_disable; - - if (IS_V4(core)) - ret = clk_prepare_enable(core->core0_bus_clk); - - if (ret) - goto err_unprepare_core0; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_ON); -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/venc.c b/drivers/media/platform/qcom/venus/venc.c index 453edf966d4f..9981a2a27c90 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -20,6 +20,7 @@ #include "core.h" #include "helpers.h" #include "venc.h" +#include "pm_helpers.h" #define NUM_B_FRAMES_MAX 4 @@ -655,10 +656,6 @@ static int venc_set_properties(struct venus_inst *inst) 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); @@ -731,7 +728,9 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; - if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + if (!ctr->rc_enable) + rate_control = HFI_RATE_CONTROL_OFF; + else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) rate_control = HFI_RATE_CONTROL_VBR_CFR; else rate_control = HFI_RATE_CONTROL_CBR_CFR; @@ -991,6 +990,10 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto bufs_done; + ret = venus_pm_acquire_core(inst); + if (ret) + goto deinit_sess; + ret = venc_set_properties(inst); if (ret) goto deinit_sess; @@ -1159,6 +1162,8 @@ static int venc_open(struct file *file) inst->core = core; inst->session_type = VIDC_SESSION_TYPE_ENC; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; venus_helper_init_instance(inst); @@ -1255,20 +1260,14 @@ static int venc_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - 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); - } + platform_set_drvdata(pdev, core); - 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); + if (core->pm_ops->venc_get) { + ret = core->pm_ops->venc_get(dev); + if (ret) + return ret; } - platform_set_drvdata(pdev, core); - vdev = video_device_alloc(); if (!vdev) return -ENOMEM; @@ -1281,7 +1280,7 @@ static int venc_probe(struct platform_device *pdev) vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto err_vdev_release; @@ -1305,57 +1304,33 @@ static int venc_remove(struct platform_device *pdev) video_unregister_device(core->vdev_enc); pm_runtime_disable(core->dev_enc); + if (core->pm_ops->venc_put) + core->pm_ops->venc_put(core->dev_enc); + return 0; } static __maybe_unused int venc_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - 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); + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - clk_disable_unprepare(core->core1_clk); + if (pm_ops->venc_power) + ret = pm_ops->venc_power(dev, POWER_OFF); - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); + return ret; } static __maybe_unused int venc_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); - if (ret) - return ret; - - ret = clk_prepare_enable(core->core1_clk); - if (ret) - goto err_power_disable; - - if (IS_V4(core)) - ret = clk_prepare_enable(core->core1_bus_clk); - - if (ret) - goto err_unprepare_core1; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); + if (pm_ops->venc_power) + ret = pm_ops->venc_power(dev, POWER_ON); -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 877c0b3299e9..8362dde7949e 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -199,6 +199,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) } mutex_unlock(&inst->lock); break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + ctr->rc_enable = ctrl->val; + break; default: return -EINVAL; } @@ -214,7 +217,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 30); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 31); if (ret) return ret; @@ -351,6 +354,9 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 0, 0, 0, 0); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); + ret = inst->ctrl_handler.error; if (ret) goto err; |