From: Nagaraju Siddineni <[email protected]> - Extend device‑tree with properties for secure firmware, DRM, codec capabilities, format/stride, resolution checks, and security controls. - Introduce per‑codec and device‑wide bitrate limits. - Implement runtime management: scheduler workers, dedicated workqueues, and required mutex/spinlock synchronization. - Refine decoder‑context handling (full init/cleanup, CRC dump, DPB handling, vb2 queue setup, QoS parsing). - Add polling with error‑state handling. - Expand DebugFS to expose core‑balance, feature flags, format support, QoS data, and queue metrics. - Update macros, include missing headers, improve logging, adjust scheduler/PBS configuration, and revise buffer‑size structures.
Signed-off-by: Nagaraju Siddineni <[email protected]> Signed-off-by: Himanshu Dewangan <[email protected]> --- .../dts/exynos/exynosautov920-evt2-mfc.dtsi | 443 ++++++++++++++++++ .../media/platform/samsung/exynos-mfc/mfc.c | 420 ++++++++++++++++- .../platform/samsung/exynos-mfc/mfc_debugfs.c | 60 ++- 3 files changed, 915 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi b/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi index 49c61958467e..40cac2b20804 100644 --- a/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi +++ b/arch/arm64/boot/dts/exynos/exynosautov920-evt2-mfc.dtsi @@ -67,25 +67,142 @@ mfc: mfc@19cd0000 { /* MFC version */ ip_ver = <0x1600010C>; + /* Secure Firmware Loading */ + secure_fw_loading = <1>; + /* MFC firmware name */ fw_name = "mfc_fw_v16.0.bin"; /* Debug mode */ debug_mode = <1>; + max_num_drm_inst = <3>; + + /* NAL-Q size */ + nal_q_entry_size = <768>; + nal_q_dump_size = <376>; + /* Features <on/off version> */ + skype = <1 0x0>; + black_bar = <1 0x0>; + color_aspect_dec = <1 0x0>; + static_info_dec = <1 0x0>; + color_aspect_enc = <1 0x0>; + static_info_enc = <1 0x180314>; + vp9_stride_align = <1 0x0>; mem_clear = <1 0x0>; /* Support from v11.0 (except 11.2) */ wait_fw_status = <1 0x190122>; + /* DRM switch predict for cache flush */ + drm_switch_predict = <1 0x0>; + /* Use metadata interface */ + metadata_interface = <1 0x200910>; + /* Average QP of encoder per frame */ + average_qp = <1 0x201030>; + /* MV search mode */ + mv_search_mode = <1 0x201118>; + /* Encoder IDR flag */ + enc_idr_flag = <1 0x210611>; + /* Encoder min quality mode */ + min_quality_mode = <1 0x210705>; + /* Encoder time stamp delta */ + enc_ts_delta = <1 0x211027>; + /* Encoder I limit RC mode for WFD */ + wfd_rc_mode = <1 0x211229>; /* Scheduler 0: round-robin, 1: PBS */ scheduler = <1>; /* The number of priority in PBS */ pbs_num_prio = <1>; + /* Support AV1 Film Grain Feature */ + av1_film_grain = <1 0x200717>; + + /* Decoder stride align (default: 16, AMD GPU: 256) */ + stride_align = <256>; + /* Decoder stride calculation type (new: 1, old: 0) */ + stride_type = <1>; + + /* Formats */ + support_10bit = <1>; + support_422 = <0>; + support_rgb = <1>; + + /* Resolution check (0: do not check, 1: FHD, 2: 4K, 3: 8K) */ + support_check_res = <3>; + + /* error type for sync_point display */ + /* (1: concealment display, 2: error display, 3: error no display) */ + display_err_type = <2>; + + /* FW base security ctrl */ + security_ctrl = <1>; + /* output buffer Q framerate */ + display_framerate = <0>; + + /* Encoder default parameter: max number is 100 */ + enc_param_num = <25>; + enc_param_addr = <0xF7B4 0xF7B8 0xF7B0 0xF798 0xFA2C + 0xF790 0xFA34 0xFA38 0xFA3C 0xF7C0 + 0xF7C8 0xF7CC 0xFA60 0xFDD4 0xFDDC + 0xFB54 0xFB58 0xFBA8 0xFD90 0xFD94 + 0xFD40 0xFD48 0xFD4C 0xFD50 0xFD80>; + enc_param_val = <0x80 0x80 0x0 0x4000 0x3FD00 + 0x0 0x0 0x2710 0x3E8 0x0 + 0x0 0x0 0x0 0x8050D211 0x0 + 0x3011 0x0 0x0 0x2D 0xA00 + 0x1D 0xF4240 0x33003300 0x2 0x1>; + + /* BW <peak, read, write> : KB/UHD frame */ + bw_enc_h264 = <45456 56112 11170>; + bw_enc_hevc = <46756 52766 9763>; + bw_enc_hevc_10bit = <53865 64753 12556>; + bw_enc_vp8 = <64000 67318 22518>; + bw_enc_vp9 = <72326 59726 16530>; + bw_enc_vp9_10bit = <149085 114928 31419>; + bw_enc_mpeg4 = <44647 55324 9531>; + bw_dec_h264 = <32605 34381 21263>; + bw_dec_hevc = <29973 28851 17538>; + bw_dec_hevc_10bit = <52859 46245 31351>; + bw_dec_vp8 = <28672 30468 22324>; + bw_dec_vp9 = <18351 18947 16877>; + bw_dec_vp9_10bit = <42384 34452 31766>; + bw_dec_av1 = <23787 19570 15856>; + bw_dec_av1_10bit = <41407 35490 29699>; + bw_dec_mpeg4 = <31540 25368 15770>; + + /* QoS bitrate */ + num_mfc_freq = <7>; + mfc_freqs = <160000 267000 332000 400000 533000 664000 800000>; + + /* QoS weight (%) */ + dynamic_weight = <1>; + qos_weight_h264_hevc = <100>; + qos_weight_vp8_vp9 = <100>; + qos_weight_av1 = <70>; + qos_weight_other_codec = <25>; + qos_weight_3plane = <80>; + qos_weight_10bit = <75>; + qos_weight_422 = <70>; + qos_weight_bframe = <50>; + qos_weight_num_of_ref = <60>; + qos_weight_gpb = <50>; + qos_weight_num_of_tile = <75>; + qos_weight_super64_bframe = <60>; + qos_weight_mbaff = <60>; + + /* core balance(%) for resource managing */ + core_balance = <54>; + /* MFC IOVA threshold (MB) */ iova_threshold = <1700>; + /* need control for mfc idle clock */ + idle_clk_ctrl = <1>; + + /* QoS level for pm_qos dynamic control */ + qos_ctrl_level = <1>; + /* Sub nodes for MFC core */ #address-cells = <2>; #size-cells = <2>; @@ -135,6 +252,15 @@ mfc_core0: MFC-0@19cd0000 { axid_mask = <0xFFFF>; tsmux_axid = <0x1>; + /* QoS */ + num_default_qos_steps = <10>; + num_encoder_qos_steps = <7>; + max_mb = <5093269>; + max_hw_mb = <3888000>; + mfc_freq_control = <1>; + mo_control = <1>; + bw_control = <1>; + /* mem-log buffer size */ memlog_size = <0x80000>; memlog_sfr_size = <0x1000>; @@ -148,6 +274,160 @@ mfc_core0: MFC-0@19cd0000 { iommu@19c70000 { reg = <0x0 0x19C70000 0x0 0x9000>; }; + + /* Default QoS table */ + mfc_default_qos_table { + mfc_d_qos_variant_0 { + thrd_mb = <0>; + freq_mfc = <133000>; + freq_int = <133000>; + freq_mif = <421000>; + bts_scen = "default"; + time_fw = <1238>; + }; + mfc_d_qos_variant_1 { + thrd_mb = <546599>; + freq_mfc = <267000>; + freq_int = <133000>; + freq_mif = <546000>; + bts_scen = "default"; + time_fw = <842>; + }; + mfc_d_qos_variant_2 { + thrd_mb = <1167159>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <676000>; + bts_scen = "default"; + time_fw = <667>; + }; + mfc_d_qos_variant_3 { + thrd_mb = <1859381>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <845000>; + bts_scen = "default"; + time_fw = <578>; + }; + mfc_d_qos_variant_4 { + thrd_mb = <2078433>; + freq_mfc = <468000>; + freq_int = <200000>; + freq_mif = <1014000>; + bts_scen = "default"; + time_fw = <483>; + }; + mfc_d_qos_variant_5 { + thrd_mb = <2563082>; + freq_mfc = <533000>; + freq_int = <332000>; + freq_mif = <1352000>; + bts_scen = "default"; + time_fw = <362>; + }; + mfc_d_qos_variant_6 { + thrd_mb = <3173773>; + freq_mfc = <663000>; + freq_int = <400000>; + freq_mif = <1352000>; + bts_scen = "default"; + time_fw = <306>; + }; + mfc_d_qos_variant_7 { + thrd_mb = <3716529>; + freq_mfc = <745000>; + freq_int = <533000>; + freq_mif = <1539000>; + bts_scen = "default"; + time_fw = <267>; + }; + mfc_d_qos_variant_8 { + thrd_mb = <4282126>; + freq_mfc = <745000>; + freq_int = <663000>; + freq_mif = <2028000>; + bts_scen = "default"; + time_fw = <235>; + }; + mfc_d_qos_variant_9 { + thrd_mb = <4855818>; + freq_mfc = <800000>; + freq_int = <800000>; + freq_mif = <3172000>; + bts_scen = "mfc_uhd_10bit"; + time_fw = <175>; + }; + }; + + /* Encoder only QoS table */ + mfc_encoder_qos_table { + mfc_e_qos_variant_0 { + thrd_mb = <0>; + freq_mfc = <133000>; + freq_int = <133000>; + freq_mif = <421000>; + bts_scen = "default"; + time_fw = <1238>; + }; + mfc_e_qos_variant_1 { + thrd_mb = <546599>; + freq_mfc = <267000>; + freq_int = <133000>; + freq_mif = <546000>; + bts_scen = "default"; + time_fw = <842>; + }; + mfc_e_qos_variant_2 { + thrd_mb = <1167159>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <676000>; + bts_scen = "default"; + time_fw = <667>; + }; + mfc_e_qos_variant_3 { + thrd_mb = <1859381>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <845000>; + bts_scen = "default"; + time_fw = <578>; + }; + mfc_e_qos_variant_4 { + thrd_mb = <2078433>; + freq_mfc = <533000>; + freq_int = <400000>; + freq_mif = <845000>; + bts_scen = "default"; + time_fw = <471>; + }; + mfc_e_qos_variant_5 { + thrd_mb = <2553457>; + freq_mfc = <468000>; + freq_int = <332000>; + freq_mif = <1539000>; + bts_scen = "mfc_uhd"; + time_fw = <359>; + }; + mfc_e_qos_variant_6 { + thrd_mb = <3175870>; + freq_mfc = <663000>; + freq_int = <663000>; + freq_mif = <1716000>; + bts_scen = "default"; + time_fw = <287>; + }; + }; + + /* QoS table for performance boost mode */ + mfc_perf_boost_table { + num_cluster = <3>; + freq_cluster = <1742000 1898000 1456000>; + freq_mfc = <666000>; + freq_int = <534000>; + freq_mif = <2730000>; + bts_scen = "mfc_uhd_10bit"; + }; }; mfc_core1: MFC-1@19ed0000 { @@ -173,6 +453,15 @@ mfc_core1: MFC-1@19ed0000 { share_sysmmu = <0>; axid_mask = <0xFFFF>; + /* QoS */ + num_default_qos_steps = <10>; + num_encoder_qos_steps = <7>; + max_mb = <5315425>; + max_hw_mb = <3888000>; + mfc_freq_control = <1>; + mo_control = <1>; + bw_control = <1>; + /* Device virtual address */ #dma-address-cells = <1>; #dma-size-cells = <1>; @@ -182,6 +471,160 @@ mfc_core1: MFC-1@19ed0000 { iommu@19e70000 { reg = <0x0 0x19E70000 0x0 0x9000>; }; + + /* Default QoS table */ + mfc_default_qos_table { + mfc_d_qos_variant_0 { + thrd_mb = <0>; + freq_mfc = <133000>; + freq_int = <133000>; + freq_mif = <421000>; + bts_scen = "default"; + time_fw = <1238>; + }; + mfc_d_qos_variant_1 { + thrd_mb = <546599>; + freq_mfc = <234000>; + freq_int = <133000>; + freq_mif = <546000>; + bts_scen = "default"; + time_fw = <842>; + }; + mfc_d_qos_variant_2 { + thrd_mb = <1167159>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <676000>; + bts_scen = "default"; + time_fw = <667>; + }; + mfc_d_qos_variant_3 { + thrd_mb = <1859381>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <845000>; + bts_scen = "default"; + time_fw = <578>; + }; + mfc_d_qos_variant_4 { + thrd_mb = <2078433>; + freq_mfc = <468000>; + freq_int = <200000>; + freq_mif = <1014000>; + bts_scen = "default"; + time_fw = <483>; + }; + mfc_d_qos_variant_5 { + thrd_mb = <2563082>; + freq_mfc = <533000>; + freq_int = <332000>; + freq_mif = <1352000>; + bts_scen = "default"; + time_fw = <362>; + }; + mfc_d_qos_variant_6 { + thrd_mb = <3173773>; + freq_mfc = <663000>; + freq_int = <400000>; + freq_mif = <1352000>; + bts_scen = "default"; + time_fw = <306>; + }; + mfc_d_qos_variant_7 { + thrd_mb = <3716529>; + freq_mfc = <745000>; + freq_int = <533000>; + freq_mif = <1539000>; + bts_scen = "default"; + time_fw = <267>; + }; + mfc_d_qos_variant_8 { + thrd_mb = <4282126>; + freq_mfc = <745000>; + freq_int = <663000>; + freq_mif = <2028000>; + bts_scen = "default"; + time_fw = <235>; + }; + mfc_d_qos_variant_9 { + thrd_mb = <4855818>; + freq_mfc = <800000>; + freq_int = <800000>; + freq_mif = <3172000>; + bts_scen = "mfc_8k_dec30"; + time_fw = <175>; + }; + }; + + /* Encoder only QoS table */ + mfc_encoder_qos_table { + mfc_e_qos_variant_0 { + thrd_mb = <0>; + freq_mfc = <133000>; + freq_int = <133000>; + freq_mif = <421000>; + bts_scen = "default"; + time_fw = <1238>; + }; + mfc_e_qos_variant_1 { + thrd_mb = <546599>; + freq_mfc = <267000>; + freq_int = <133000>; + freq_mif = <546000>; + bts_scen = "default"; + time_fw = <842>; + }; + mfc_e_qos_variant_2 { + thrd_mb = <1167159>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <676000>; + bts_scen = "default"; + time_fw = <667>; + }; + mfc_e_qos_variant_3 { + thrd_mb = <1859381>; + freq_mfc = <332000>; + freq_int = <200000>; + freq_mif = <845000>; + bts_scen = "default"; + time_fw = <578>; + }; + mfc_e_qos_variant_4 { + thrd_mb = <2078433>; + freq_mfc = <533000>; + freq_int = <400000>; + freq_mif = <845000>; + bts_scen = "default"; + time_fw = <471>; + }; + mfc_e_qos_variant_5 { + thrd_mb = <2553457>; + freq_mfc = <468000>; + freq_int = <332000>; + freq_mif = <1539000>; + bts_scen = "mfc_uhd"; + time_fw = <359>; + }; + mfc_e_qos_variant_6 { + thrd_mb = <3175870>; + freq_mfc = <663000>; + freq_int = <663000>; + freq_mif = <1716000>; + bts_scen = "default"; + time_fw = <287>; + }; + }; + + /* QoS table for performance boost mode */ + mfc_perf_boost_table { + num_cluster = <3>; + freq_cluster = <1742000 1898000 1456000>; + freq_mfc = <666000>; + freq_int = <534000>; + freq_mif = <2730000>; + bts_scen = "mfc_uhd_10bit"; + }; }; }; }; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc.c b/drivers/media/platform/samsung/exynos-mfc/mfc.c index b5b66083cc8b..fb9a7317e812 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc.c @@ -21,6 +21,8 @@ #include <linux/poll.h> #include <linux/iommu.h> +#include "mfc_dec_v4l2.h" +#include "mfc_dec_vb2.h" #include "mfc_rm.h" #include "mfc_debugfs.h" @@ -29,6 +31,9 @@ #include "mfc_core_hw_reg_api.h" +#include "base/mfc_qos.h" + +#include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_buf.h" #include "base/mfc_mem.h" @@ -41,6 +46,188 @@ struct _mfc_trace g_mfc_trace[MFC_TRACE_COUNT_MAX]; struct _mfc_trace g_mfc_trace_rm[MFC_TRACE_COUNT_MAX]; struct _mfc_trace g_mfc_trace_longterm[MFC_TRACE_COUNT_MAX]; +static void mfc_butler_worker(struct work_struct *work) +{ + struct mfc_dev *dev; + struct mfc_ctx *ctx; + int i; + + dev = container_of(work, struct mfc_dev, butler_work); + + /* If there is multi core instance, it has high priority */ + if (dev->multi_core_inst_bits) { + bool is_switch_to_single; + bool is_multi_mode; + + i = __ffs(dev->multi_core_inst_bits); + ctx = dev->ctx[i]; + if (!ctx) { + mfc_dev_err("[RM] There is no ctx\n"); + return; + } + + /* [DRC] In the case of MFC_OP_SWITCH_TO_SINGLE, + * also need to request with MFC_WORK_TRY. + * Because op_mode is maintained as MFC_OP_SWITCH_TO_SINGLE before subcore_deinit. + * And, subcore_deinit can be started by MFC_WORK_TRY. + */ + is_multi_mode = IS_MULTI_MODE(ctx); + is_switch_to_single = + (ctx->op_mode == MFC_OP_SWITCH_TO_SINGLE && ctx->handle_drc_multi_mode); + if (!is_multi_mode && !is_switch_to_single) + return; + + mfc_rm_request_work(dev, MFC_WORK_TRY, ctx); + } else { + mfc_rm_request_work(dev, MFC_WORK_BUTLER, NULL); + } +} + +static void __mfc_deinit_dec_ctx(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec = ctx->dec_priv; + unsigned int size; + + if (dec->crc && (ctx->dev->debugfs.sfr_dump & MFC_DUMP_DEC_CRC)) { + if (dec->crc_idx * 4 > SZ_1K) + size = SZ_1K; + else + size = dec->crc_idx * 4; + print_hex_dump + (KERN_ERR, "CRC: ", DUMP_PREFIX_OFFSET, 32, 1, + dec->crc, size, false); + vfree(dec->crc); + } + + mfc_cleanup_iovmm(ctx); + + mfc_delete_queue(&ctx->src_buf_ready_queue); + mfc_delete_queue(&ctx->dst_buf_queue); + mfc_delete_queue(&ctx->err_buf_queue); + mfc_delete_queue(&ctx->meminfo_inbuf_q); + + mfc_mem_cleanup_user_shared_handle(ctx, &dec->sh_handle_dpb); + if (dec->ref_info) + vfree(dec->ref_info); + + kfree(dec); +} + +static int __mfc_init_dec_ctx(struct mfc_ctx *ctx) +{ + struct mfc_dec *dec; + int ret = 0; + int i; + + dec = kzalloc(sizeof(*dec), GFP_KERNEL); + if (!dec) + return -ENOMEM; + + ctx->dec_priv = dec; + + ctx->subcore_inst_no = MFC_NO_INSTANCE_SET; + ctx->curr_src_index = -1; + ctx->user_prio = -1; + + mfc_create_queue(&ctx->src_buf_ready_queue); + mfc_create_queue(&ctx->dst_buf_queue); + mfc_create_queue(&ctx->err_buf_queue); + mfc_create_queue(&ctx->meminfo_inbuf_q); + + for (i = 0; i < MFC_MAX_BUFFERS; i++) { + INIT_LIST_HEAD(&ctx->src_ctrls[i]); + INIT_LIST_HEAD(&ctx->dst_ctrls[i]); + } + bitmap_zero(ctx->src_ctrls_avail, MFC_MAX_BUFFERS); + bitmap_zero(ctx->dst_ctrls_avail, MFC_MAX_BUFFERS); + + ctx->capture_state = QUEUE_FREE; + ctx->output_state = QUEUE_FREE; + + ctx->type = MFCINST_DECODER; + ctx->c_ops = &mfc_ctrls_ops; + ctx->b_ops = &mfc_bufs_ops; + + mfc_dec_set_default_format(ctx); + mfc_rate_reset_framerate(ctx); + + ctx->qos_ratio = 100; + INIT_LIST_HEAD(&ctx->bitrate_list); + INIT_LIST_HEAD(&ctx->src_ts.ts_list); + + dec->display_delay = -1; + dec->is_interlaced = 0; + dec->immediate_display = 0; + dec->is_dts_mode = 0; + dec->inter_res_change = 0; + dec->disp_drc.disp_res_change = 0; + dec->disp_drc.push_idx = 0; + dec->disp_drc.pop_idx = 0; + + dec->is_dynamic_dpb = 1; + dec->dynamic_used = 0; + dec->is_dpb_full = 0; + dec->queued_dpb = 0; + dec->display_index = -1; + dec->dpb_table_used = 0; + dec->sh_handle_dpb.fd = -1; + mutex_init(&dec->dpb_mutex); + + mfc_init_dpb_table(ctx); + + dec->sh_handle_dpb.data_size = sizeof(struct dec_dpb_ref_info) * MFC_MAX_BUFFERS; + dec->ref_info = vmalloc(dec->sh_handle_dpb.data_size); + if (!dec->ref_info) { + mfc_ctx_err("failed to allocate decoder information data\n"); + ret = -ENOMEM; + goto fail_dec_init; + } + for (i = 0; i < MFC_MAX_BUFFERS; i++) + dec->ref_info[i].dpb[0].fd[0] = MFC_INFO_INIT_FD; + + if (ctx->dev->debugfs.sfr_dump & MFC_DUMP_DEC_CRC) { + dec->crc = vmalloc(SZ_1K); + if (!dec->crc) { + ret = -ENOMEM; + goto fail_dec_init; + } + dec->crc_idx = 0; + } + + /* Init videobuf2 queue for OUTPUT */ + ctx->vq_src.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + ctx->vq_src.drv_priv = ctx; + ctx->vq_src.buf_struct_size = (unsigned int)sizeof(struct mfc_buf); + ctx->vq_src.io_modes = VB2_USERPTR | VB2_DMABUF; + ctx->vq_src.ops = mfc_get_dec_vb2_ops(); + ctx->vq_src.mem_ops = mfc_mem_ops(); + ctx->vq_src.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret = vb2_queue_init(&ctx->vq_src); + if (ret) { + mfc_ctx_err("Failed to initialize videobuf2 queue(output)\n"); + goto fail_dec_init; + } + /* Init videobuf2 queue for CAPTURE */ + ctx->vq_dst.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + ctx->vq_dst.drv_priv = ctx; + ctx->vq_dst.buf_struct_size = (unsigned int)sizeof(struct mfc_buf); + ctx->vq_dst.io_modes = VB2_USERPTR | VB2_DMABUF; + ctx->vq_dst.ops = mfc_get_dec_vb2_ops(); + ctx->vq_dst.mem_ops = mfc_mem_ops(); + ctx->vq_dst.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret = vb2_queue_init(&ctx->vq_dst); + if (ret) { + mfc_ctx_err("Failed to initialize videobuf2 queue(capture)\n"); + goto fail_dec_init; + } + + return ret; + +fail_dec_init: + __mfc_deinit_dec_ctx(ctx); + return ret; +} + /* Open an MFC node */ static int mfc_open(struct file *file) { @@ -49,6 +236,7 @@ static int mfc_open(struct file *file) int i, ret = 0; enum mfc_node_type node; struct video_device *vdev = NULL; + unsigned long total_mb = 0, max_hw_mb = 0; if (!dev) { mfc_pr_err("no mfc device to run\n"); @@ -60,6 +248,26 @@ static int mfc_open(struct file *file) if (mutex_lock_interruptible(&dev->mfc_mutex)) return -ERESTARTSYS; + /* Check mfc_open() of spec over */ + for (i = 0; i < dev->num_core; i++) + max_hw_mb += dev->core[i]->core_pdata->max_hw_mb; + for (i = 0; i < MFC_NUM_CONTEXTS; i++) { + if (!dev->ctx[i]) + continue; + total_mb += dev->ctx[i]->weighted_mb; + mfc_show_ctx_info(dev->ctx[i]); + } + if (total_mb >= max_hw_mb) { + mfc_dev_info + ("[RM] now MFC work with full spec(mb: %lu / %lu)\n", + total_mb, max_hw_mb); + for (i = 0; i < MFC_NUM_CONTEXTS; i++) { + if (!dev->ctx[i]) + continue; + mfc_print_ctx_info(dev->ctx[i]); + } + } + node = mfc_get_node_type(file); if (node == MFCNODE_INVALID) { mfc_dev_err("cannot specify node type\n"); @@ -109,20 +317,32 @@ static int mfc_open(struct file *file) } } + spin_lock_init(&ctx->buf_queue_lock); + spin_lock_init(&ctx->meminfo_queue_lock); spin_lock_init(&ctx->corelock.lock); + spin_lock_init(&ctx->src_ts.ts_lock); + spin_lock_init(&ctx->dst_q_ts.ts_lock); + spin_lock_init(&ctx->dst_dq_ts.ts_lock); + spin_lock_init(&ctx->src_q_ts.ts_lock); mutex_init(&ctx->intlock.core_mutex); mutex_init(&ctx->op_mode_mutex); + mutex_init(&ctx->drc_wait_mutex); init_waitqueue_head(&ctx->corelock.wq); + init_waitqueue_head(&ctx->corelock.migrate_wq); + INIT_LIST_HEAD(&ctx->dst_q_ts.ts_list); + INIT_LIST_HEAD(&ctx->dst_dq_ts.ts_list); + INIT_LIST_HEAD(&ctx->src_q_ts.ts_list); mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE); if (mfc_is_decoder_node(node)) { - ctx->type = MFCINST_DECODER; + ret = __mfc_init_dec_ctx(ctx); dev->num_dec_inst++; } else { - ctx->type = MFCINST_ENCODER; dev->num_enc_inst++; } + if (ret) + goto err_ctx_init; if (dev->num_inst == 1) { /* all of the ctx list */ @@ -131,6 +351,12 @@ static int mfc_open(struct file *file) /* idle mode */ spin_lock_init(&dev->idle_bits_lock); } + if (mfc_is_decoder_node(node)) + ret = call_cop(ctx, init_ctx_ctrls, ctx); + if (ret) { + mfc_ctx_err("failed in init_ctx_ctrls\n"); + goto err_ctx_init; + } mfc_ctx_info ("NORMAL %s instance is opened [%d]\n", @@ -155,11 +381,16 @@ static int mfc_open(struct file *file) ("[INFO] %s opened (ctx:%d, total:%d)\n", mfc_is_decoder_node(node) ? "DEC" : "ENC", ctx->num, dev->num_inst); + queue_work(dev->butler_wq, &dev->butler_work); + mutex_unlock(&dev->mfc_mutex); return ret; /* Deinit when failure occurred */ err_inst_init: + call_cop(ctx, cleanup_ctx_ctrls, ctx); + +err_ctx_init: if (mfc_is_decoder_node(node)) dev->num_dec_inst--; else @@ -192,10 +423,13 @@ static int mfc_release(struct file *file) { struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data); struct mfc_dev *dev = ctx->dev; + struct mfc_ctx *move_ctx; int ret = 0; + int i; unsigned long flags; mutex_lock(&dev->mfc_mutex); + mutex_lock(&dev->mfc_migrate_mutex); mfc_ctx_info ("%s instance release is called [%d]\n", @@ -215,6 +449,12 @@ static int mfc_release(struct file *file) * So, we need to performed stop_streaming * before instance de-init(CLOSE_INSTANCE). */ + if (ctx->type == MFCINST_DECODER) { + vb2_queue_release(&ctx->vq_src); + vb2_queue_release(&ctx->vq_dst); + + call_cop(ctx, cleanup_ctx_ctrls, ctx); + } ret = mfc_rm_instance_deinit(dev, ctx); if (ret) { @@ -224,13 +464,27 @@ static int mfc_release(struct file *file) dev->num_inst--; - if (ctx->type == MFCINST_DECODER) + if (IS_MULTI_CORE_DEVICE(dev)) + mfc_rm_load_balancing(ctx, MFC_RM_LOAD_DELETE_UPDATE); + + if (ctx->type == MFCINST_DECODER) { + __mfc_deinit_dec_ctx(ctx); dev->num_dec_inst--; - else if (ctx->type == MFCINST_ENCODER) + } else if (ctx->type == MFCINST_ENCODER) { dev->num_enc_inst--; + } MFC_TRACE_CTX_LT("[INFO] Release finished (ctx:%d, total:%d)\n", ctx->num, dev->num_inst); + /* If ctx is move_ctx in migration worker, clear move_ctx */ + for (i = 0; i < dev->move_ctx_cnt; i++) { + move_ctx = dev->move_ctx[i]; + if (move_ctx && move_ctx->num == ctx->num) { + dev->move_ctx[i] = NULL; + break; + } + } + spin_lock_irqsave(&dev->ctx_list_lock, flags); dev->ctx[ctx->num] = NULL; kfree(ctx); @@ -238,17 +492,49 @@ static int mfc_release(struct file *file) mfc_dev_info("mfc driver release finished [%d]\n", dev->num_inst); + queue_work(dev->butler_wq, &dev->butler_work); + end_release: + mutex_unlock(&dev->mfc_migrate_mutex); mutex_unlock(&dev->mfc_mutex); return ret; } +/* Poll */ +static __poll_t mfc_poll(struct file *file, struct poll_table_struct *wait) +{ + struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data); + unsigned long req_events = poll_requested_events(wait); + __poll_t ret = 0; + + mfc_ctx_debug_enter(); + + if (mfc_rm_query_state(ctx, EQUAL, MFCINST_ERROR)) { + if (req_events & (POLLOUT | POLLWRNORM)) + mfc_ctx_err("SRC: Call on POLL after unrecoverable error\n"); + else + mfc_ctx_err("DST: Call on POLL after unrecoverable error\n"); + return EPOLLERR; + } + + if (req_events & (POLLOUT | POLLWRNORM)) { + mfc_ctx_debug(2, "wait source buffer\n"); + ret = vb2_poll(&ctx->vq_src, file, wait); + } else if (req_events & (POLLIN | POLLRDNORM)) { + mfc_ctx_debug(2, "wait destination buffer\n"); + ret = vb2_poll(&ctx->vq_dst, file, wait); + } + + mfc_ctx_debug_leave(); + return ret; +} + /* v4l2 ops */ static const struct v4l2_file_operations mfc_fops = { .owner = THIS_MODULE, .open = mfc_open, .release = mfc_release, - .poll = NULL, + .poll = mfc_poll, .unlocked_ioctl = video_ioctl2, }; @@ -278,6 +564,9 @@ static void __mfc_parse_dt_resource(struct mfc_dev *dev, struct device_node *np) of_property_read_u32_index(np_tmp, "info", idx++, &codec_mode); resource = &dev->pdata->mfc_resource[codec_mode]; of_property_read_u32_index(np_tmp, "info", idx++, (u32 *)&resource->op_core_type); + of_property_read_u32_index(np_tmp, "info", idx++, &resource->max_kbps); + if (resource->max_kbps > dev->max_kbps) + dev->max_kbps = resource->max_kbps; } } @@ -309,17 +598,104 @@ static int __mfc_parse_dt(struct device_node *np, struct mfc_dev *mfc) /* Resource of standard */ __mfc_parse_dt_resource(mfc, np); + /* Features */ + of_property_read_u32_array(np, "skype", &pdata->skype.support, 2); + of_property_read_u32_array + (np, "black_bar", + &pdata->black_bar.support, 2); + of_property_read_u32_array + (np, "color_aspect_dec", + &pdata->color_aspect_dec.support, 2); + of_property_read_u32_array + (np, "static_info_dec", + &pdata->static_info_dec.support, 2); + of_property_read_u32_array + (np, "vp9_stride_align", + &pdata->vp9_stride_align.support, 2); of_property_read_u32_array (np, "mem_clear", &pdata->mem_clear.support, 2); of_property_read_u32_array (np, "wait_fw_status", &pdata->wait_fw_status.support, 2); + of_property_read_u32_array + (np, "hevc_pic_output_flag", + &pdata->hevc_pic_output_flag.support, 2); + + /* H/W limitation or option */ + of_property_read_u32(np, "stride_align", &pdata->stride_align); + of_property_read_u32(np, "stride_type", &pdata->stride_type); + of_property_read_u32(np, "stream_buf_limit", &pdata->stream_buf_limit); + of_property_read_u32(np, "support_8K_cavlc", &pdata->support_8K_cavlc); + + /* Formats */ + of_property_read_u32(np, "support_422", &pdata->support_422); + + /* Resolution */ + of_property_read_u32(np, "support_check_res", &pdata->support_check_res); + + /* HWACG */ + of_property_read_u32(np, "support_hwacg", &pdata->support_hwacg); + + /* display_err_type */ + of_property_read_u32(np, "display_err_type", &pdata->display_err_type); + + /* output buffer Q framerate */ + of_property_read_u32(np, "display_framerate", &pdata->display_framerate); + + of_property_read_u32_array + (np, "bw_dec_h264", + &pdata->mfc_bw_info.bw_dec_h264.peak, 3); + + /* QoS weight */ + of_property_read_u32(np, "dynamic_weight", &pdata->dynamic_weight); + of_property_read_u32 + (np, "qos_weight_h264_hevc", + &pdata->qos_weight.weight_h264_hevc); + of_property_read_u32 + (np, "qos_weight_3plane", + &pdata->qos_weight.weight_3plane); + of_property_read_u32 + (np, "qos_weight_num_of_tile", + &pdata->qos_weight.weight_num_of_tile); + of_property_read_u32 + (np, "qos_weight_mbaff", + &pdata->qos_weight.weight_mbaff); + + /* Bitrate control for QoS */ + of_property_read_u32(np, "num_mfc_freq", &pdata->num_mfc_freq); + if (pdata->num_mfc_freq) + of_property_read_u32_array + (np, "mfc_freqs", + pdata->mfc_freqs, pdata->num_mfc_freq); + + /* Core balance(%) for resource managing */ + of_property_read_u32(np, "core_balance", &pdata->core_balance); + + /* MFC IOVA threshold */ + of_property_read_u32(np, "iova_threshold", &pdata->iova_threshold); + + /* MFC idle clock control */ + of_property_read_u32(np, "idle_clk_ctrl", &pdata->idle_clk_ctrl); + + /* QoS level for pm_qos dynamic control */ + of_property_read_u32(np, "qos_ctrl_level", &pdata->qos_ctrl_level); + + /* Memlog size */ + of_property_read_u32(np, "memlog_size", &pdata->memlog_size); + of_property_read_u32(np, "memlog_sfr_size", &pdata->memlog_sfr_size); + + /* offset for saving result of regression */ + of_property_read_u32 + (np, "reg_h264_loop_filter_disable", + &pdata->reg_h264_loop_filter_disable); /* Scheduler */ of_property_read_u32(np, "scheduler", &pdata->scheduler); of_property_read_u32(np, "pbs_num_prio", &pdata->pbs_num_prio); + of_property_read_u32(np, "support_mv_hevc", &pdata->support_mv_hevc); + return 0; } @@ -341,6 +717,11 @@ static struct video_device *__mfc_video_device_register vfd->minor = -1; vfd->release = video_device_release; + if (IS_DEC_NODE(node_num)) + vfd->ioctl_ops = mfc_get_dec_v4l2_ioctl_ops(); + else if (IS_ENC_NODE(node_num)) + vfd->ioctl_ops = NULL; + vfd->lock = &dev->mfc_mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->vfl_dir = VFL_DIR_M2M; @@ -413,6 +794,7 @@ static int mfc_probe(struct platform_device *pdev) dma_set_mask(&pdev->dev, DMA_BIT_MASK(dev->pdata->dma_bit_mask)); mutex_init(&dev->mfc_mutex); + mutex_init(&dev->mfc_migrate_mutex); ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) @@ -437,6 +819,24 @@ static int mfc_probe(struct platform_device *pdev) } /* end of node setting*/ + /* instance migration worker */ + dev->migration_wq = alloc_workqueue("mfc/inst_migration", WQ_UNBOUND + | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!dev->migration_wq) { + dev_err(&pdev->dev, "failed to create workqueue for migration wq\n"); + goto err_migration_work; + } + INIT_WORK(&dev->migration_work, mfc_rm_migration_worker); + + /* main butler worker */ + dev->butler_wq = alloc_workqueue("mfc/butler", WQ_UNBOUND + | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1); + if (!dev->butler_wq) { + dev_err(&pdev->dev, "failed to create workqueue for butler\n"); + goto err_butler_wq; + } + INIT_WORK(&dev->butler_work, mfc_butler_worker); + /* for DVA reservation */ if (dev->pdata->reserved_start) { dev->domain = iommu_get_domain_for_dev(dev->device); @@ -461,6 +861,10 @@ static int mfc_probe(struct platform_device *pdev) /* Deinit MFC if probe had failed */ err_iova_reserve: + destroy_workqueue(dev->butler_wq); +err_butler_wq: + destroy_workqueue(dev->migration_wq); +err_migration_work: video_unregister_device(dev->vfd_enc); alloc_vdev_enc: video_unregister_device(dev->vfd_dec); @@ -468,6 +872,7 @@ static int mfc_probe(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); err_v4l2_dev: mutex_destroy(&dev->mfc_mutex); + mutex_destroy(&dev->mfc_migrate_mutex); err_res_mem: return ret; } @@ -482,6 +887,10 @@ static void mfc_remove(struct platform_device *pdev) platform_driver_unregister(&mfc_core_driver); mfc_unconfigure_dma_memory(dev); of_reserved_mem_device_release(dev->device); + flush_workqueue(dev->butler_wq); + destroy_workqueue(dev->butler_wq); + flush_workqueue(dev->migration_wq); + destroy_workqueue(dev->migration_wq); mfc_deinit_debugfs(dev); video_unregister_device(dev->vfd_enc); @@ -665,6 +1074,7 @@ static const struct dev_pm_ops mfc_pm_ops = { struct mfc_ctx_buf_size mfc_ctx_buf_size = { .dev_ctx = PAGE_ALIGN(0x7800), /* 30KB */ + .h264_dec_ctx = PAGE_ALIGN(0x200000), /* 1.6MB */ .dbg_info_buf = PAGE_ALIGN(0x1000), /* 4KB for DEBUG INFO */ }; diff --git a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c b/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c index 5baa76a6b405..59cd6f3945ff 100644 --- a/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c +++ b/drivers/media/platform/samsung/exynos-mfc/mfc_debugfs.c @@ -14,18 +14,34 @@ #include "mfc_debugfs.h" +#include "base/mfc_queue.h" + static int __mfc_info_show(struct seq_file *s, void *unused) { struct mfc_dev *dev = s->private; struct mfc_core *core = NULL; + struct mfc_ctx *ctx = NULL; struct mfc_core_ctx *core_ctx = NULL; int i, j; + char *codec_name = NULL, *fmt_name = NULL; seq_puts(s, ">>> MFC common device information\n"); seq_printf(s, " [DEBUG MODE] dt: %s sysfs: %s\n", dev->pdata->debug_mode ? "enabled" : "disabled", dev->debugfs.debug_mode_en ? "enabled" : "disabled"); - + seq_printf(s, " [FEATURES] skype: %d(0x%x), black_bar: %d(0x%x)\n", + dev->pdata->skype.support, dev->pdata->skype.version, + dev->pdata->black_bar.support, + dev->pdata->black_bar.version); + seq_printf(s, " color_aspect_dec: %d(0x%x)\n", + dev->pdata->color_aspect_dec.support, + dev->pdata->color_aspect_dec.version); + seq_printf(s, " static_info_dec: %d(0x%x)\n", + dev->pdata->static_info_dec.support, + dev->pdata->static_info_dec.version); + seq_printf(s, " [FORMATS] 10bit: %s, 422: %s\n", + dev->pdata->support_10bit ? "supported" : "not supported", + dev->pdata->support_422 ? "supported" : "not supported"); seq_printf(s, " [LOWMEM] is_low_mem: %d\n", IS_LOW_MEM); for (j = 0; j < dev->num_core; j++) { @@ -54,9 +70,46 @@ static int __mfc_info_show(struct seq_file *s, void *unused) for (i = 0; i < MFC_NUM_CONTEXTS; i++) { core_ctx = core->core_ctx[i]; if (core_ctx) { - seq_printf(s, " [CORECTX:%d] state: %d\n", - i, core_ctx->state); + seq_printf(s, " [CORECTX:%d] state: %d, queue(src: %d, dst: %d)\n", + i, core_ctx->state, + mfc_get_queue_count(&core_ctx->ctx->buf_queue_lock, + &core_ctx->src_buf_queue), + mfc_get_queue_count(&core_ctx->ctx->buf_queue_lock, + &core_ctx->dst_buf_queue)); + } + } + } + seq_puts(s, ">>> MFC instance information\n"); + for (i = 0; i < MFC_NUM_CONTEXTS; i++) { + ctx = dev->ctx[i]; + if (ctx) { + if (ctx->type == MFCINST_DECODER) { + codec_name = ctx->src_fmt->name; + fmt_name = ctx->dst_fmt->name; + } else { + codec_name = ctx->dst_fmt->name; + fmt_name = ctx->src_fmt->name; } + + seq_printf(s, " [CTX:%d] %s%s %s, size: %dx%d@%ldfps(ts: %lufps, tmu: %dfps, op: %lufps), crop: %d %d %d %d\n", + ctx->num, codec_name, ctx->multi_view_enable ? "(MV-HEVC)" : "", + fmt_name, + ctx->img_width, ctx->img_height, + ctx->framerate / 1000, + ctx->last_framerate / 1000, + ctx->dev->tmu_fps, + ctx->operating_framerate, + ctx->crop_width, ctx->crop_height, + ctx->crop_left, ctx->crop_top); + seq_printf(s, " main core: %d, op_mode: %d(stream: %d), idle_mode: %d, prio %d, rt %d, queue(src: %d, dst: %d, ref: %d)\n", + ctx->op_core_num[MFC_CORE_MAIN], + ctx->op_mode, ctx->stream_op_mode, ctx->idle_mode, + ctx->prio, ctx->rt, + mfc_get_queue_count(&ctx->buf_queue_lock, + &ctx->src_buf_ready_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->dst_buf_queue), + mfc_get_queue_count(&ctx->buf_queue_lock, &ctx->ref_buf_queue)); + seq_puts(s, "\n"); } } @@ -175,6 +228,7 @@ void mfc_init_debugfs(struct mfc_dev *dev) debugfs_create_u32("sfr_dump", 0644, debugfs->root, &dev->debugfs.sfr_dump); debugfs_create_u32("feature_option", 0644, debugfs->root, &dev->debugfs.feature_option); + debugfs_create_u32("core_balance", 0644, debugfs->root, &dev->debugfs.core_balance); debugfs_create_u32("logging_option", 0644, debugfs->root, &dev->debugfs.logging_option); debugfs_create_u32("sched_perf_disable", 0644, debugfs->root, &dev->debugfs.sched_perf_disable); -- 2.34.1
