/* * drivers/media/platform/exynos/mfc/mfc.c * * Copyright (c) 2016 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_EXYNOS_THERMAL_V2) #include #include #endif #include "mfc_dec_v4l2.h" #include "mfc_dec_internal.h" #include "mfc_enc_v4l2.h" #include "mfc_enc_internal.h" #include "mfc_rm.h" #include "mfc_core_hwlock.h" #include "mfc_core_run.h" #include "mfc_core_otf.h" #include "mfc_debugfs.h" #include "mfc_sync.h" #include "mfc_core_pm.h" #include "mfc_core_hw_reg_api.h" #include "mfc_llc.h" #include "base/mfc_qos.h" #include "base/mfc_common.h" #include "base/mfc_meminfo.h" #include "base/mfc_memlog.h" #include "base/mfc_queue.h" #include "base/mfc_utils.h" #include "base/mfc_buf.h" #include "base/mfc_mem.h" #define MFC_NAME "s5p-mfc" #define MFC_DEC_NAME "s5p-mfc-dec" #define MFC_ENC_NAME "s5p-mfc-enc" #define MFC_DEC_DRM_NAME "s5p-mfc-dec-secure" #define MFC_ENC_DRM_NAME "s5p-mfc-enc-secure" #define MFC_ENC_OTF_NAME "s5p-mfc-enc-otf" #define MFC_ENC_OTF_DRM_NAME "s5p-mfc-enc-otf-secure" 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]; struct mfc_dev *g_mfc_dev; #if IS_ENABLED(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION) static struct proc_dir_entry *mfc_proc_entry; #define MFC_PROC_ROOT "mfc" #define MFC_PROC_INSTANCE_NUMBER "instance_number" #define MFC_PROC_DRM_INSTANCE_NUMBER "drm_instance_number" #define MFC_PROC_FW_STATUS "fw_status" #endif 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) { i = __ffs(dev->multi_core_inst_bits); ctx = dev->ctx[i]; if (!ctx) { mfc_dev_err("[RM] There is no ctx\n"); return; } if (!IS_MULTI_MODE(ctx)) 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; mfc_cleanup_iovmm(ctx); mfc_delete_queue(&ctx->src_buf_ready_queue); mfc_delete_queue(&ctx->dst_buf_queue); mfc_delete_queue(&ctx->src_buf_nal_queue); mfc_delete_queue(&ctx->dst_buf_nal_queue); mfc_delete_queue(&ctx->err_buf_queue); mfc_delete_queue(&ctx->meminfo_inbuf_q); mfc_dec_defer_delete_timer(ctx); mfc_mem_cleanup_user_shared_handle(ctx, &dec->sh_handle_dpb); mfc_mem_cleanup_user_shared_handle(ctx, &dec->sh_handle_hdr); mfc_mem_cleanup_user_shared_handle(ctx, &dec->sh_handle_av1_film_grain); if (dec->ref_info) vfree(dec->ref_info); if (dec->hdr10_plus_info) vfree(dec->hdr10_plus_info); if (dec->av1_film_grain_info) vfree(dec->av1_film_grain_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(struct mfc_dec), GFP_KERNEL); if (!dec) { mfc_ctx_err("failed to allocate decoder private data\n"); return -ENOMEM; } ctx->dec_priv = dec; ctx->subcore_inst_no = MFC_NO_INSTANCE_SET; ctx->curr_src_index = -1; mfc_create_queue(&ctx->src_buf_ready_queue); mfc_create_queue(&ctx->dst_buf_queue); mfc_create_queue(&ctx->src_buf_nal_queue); mfc_create_queue(&ctx->dst_buf_nal_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]); } ctx->src_ctrls_avail = 0; ctx->dst_ctrls_avail = 0; 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_qos_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_res_change = 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); dec->defer_dec = 0; dec->defer_frame_cnt = 0; spin_lock_init(&dec->defer_dec_lock); timer_setup(&ctx->src_buf_timer, mfc_dec_defer_src_checker, 0); timer_setup(&ctx->dst_buf_timer, mfc_dec_defer_dst_checker, 0); 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; dec->sh_handle_hdr.fd = -1; /* 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 = sizeof(struct mfc_buf); ctx->vq_src.io_modes = VB2_USERPTR | VB2_DMABUF; ctx->vq_src.ops = &mfc_dec_qops; 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 = sizeof(struct mfc_buf); ctx->vq_dst.io_modes = VB2_USERPTR | VB2_DMABUF; ctx->vq_dst.ops = &mfc_dec_qops; 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; } static void __mfc_deinit_enc_ctx(struct mfc_ctx *ctx) { struct mfc_enc *enc = ctx->enc_priv; mfc_delete_queue(&ctx->src_buf_ready_queue); mfc_delete_queue(&ctx->dst_buf_queue); mfc_delete_queue(&ctx->src_buf_nal_queue); mfc_delete_queue(&ctx->dst_buf_nal_queue); mfc_delete_queue(&ctx->ref_buf_queue); mfc_delete_queue(&ctx->err_buf_queue); mfc_delete_queue(&ctx->meminfo_inbuf_q); mfc_delete_queue(&ctx->meminfo_outbuf_q); mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_svc); mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_roi); mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_hdr); if (enc->sh_handle_hdr10_plus_stat.fd != -1) mfc_put_iovmm_from_fd(ctx, &enc->hdr10_plus_stat_info_buf, enc->sh_handle_hdr10_plus_stat.fd); mfc_mem_cleanup_user_shared_handle(ctx, &enc->sh_handle_hdr10_plus_stat); kfree(enc); } static int __mfc_init_enc_ctx(struct mfc_ctx *ctx) { struct mfc_enc *enc; struct mfc_enc_params *p; int ret = 0; int i; enc = kzalloc(sizeof(struct mfc_enc), GFP_KERNEL); if (!enc) { mfc_ctx_err("failed to allocate encoder private data\n"); return -ENOMEM; } ctx->enc_priv = enc; mfc_create_queue(&ctx->src_buf_ready_queue); mfc_create_queue(&ctx->dst_buf_queue); mfc_create_queue(&ctx->src_buf_nal_queue); mfc_create_queue(&ctx->dst_buf_nal_queue); mfc_create_queue(&ctx->ref_buf_queue); mfc_create_queue(&ctx->err_buf_queue); mfc_create_queue(&ctx->meminfo_inbuf_q); mfc_create_queue(&ctx->meminfo_outbuf_q); for (i = 0; i < MFC_MAX_BUFFERS; i++) { INIT_LIST_HEAD(&ctx->src_ctrls[i]); INIT_LIST_HEAD(&ctx->dst_ctrls[i]); } ctx->src_ctrls_avail = 0; ctx->dst_ctrls_avail = 0; ctx->type = MFCINST_ENCODER; ctx->c_ops = &mfc_ctrls_ops; ctx->b_ops = &mfc_bufs_ops; mfc_enc_set_default_format(ctx); mfc_qos_reset_framerate(ctx); ctx->qos_ratio = 100; /* disable IVF header by default (VP8, VP9) */ p = &enc->params; p->ivf_header_disable = 1; INIT_LIST_HEAD(&ctx->bitrate_list); INIT_LIST_HEAD(&ctx->src_ts.ts_list); enc->sh_handle_svc.fd = -1; enc->sh_handle_roi.fd = -1; enc->sh_handle_hdr.fd = -1; enc->sh_handle_hdr10_plus_stat.fd = -1; enc->sh_handle_svc.data_size = sizeof(struct temporal_layer_info); enc->sh_handle_roi.data_size = sizeof(struct mfc_enc_roi_info); enc->sh_handle_hdr.data_size = sizeof(struct hdr10_plus_meta) * MFC_MAX_BUFFERS; enc->sh_handle_hdr10_plus_stat.data_size = sizeof(struct hdr10_plus_stat_info) * MFC_MAX_BUFFERS; /* 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 = sizeof(struct mfc_buf); ctx->vq_src.io_modes = VB2_USERPTR | VB2_DMABUF; ctx->vq_src.ops = &mfc_enc_qops; 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_enc_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 = sizeof(struct mfc_buf); ctx->vq_dst.io_modes = VB2_USERPTR | VB2_DMABUF; ctx->vq_dst.ops = &mfc_enc_qops; 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_enc_init; } return 0; fail_enc_init: __mfc_deinit_enc_ctx(ctx); return 0; } /* Open an MFC node */ static int mfc_open(struct file *file) { struct mfc_ctx *ctx = NULL; struct mfc_dev *dev = video_drvdata(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"); goto err_no_device; } mfc_dev_debug(2, "mfc driver open called\n"); if (mutex_lock_interruptible(&dev->mfc_mutex)) return -ERESTARTSYS; /* mfc_open() of spec over is failed */ 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_dev_debug(3, "-ctx[%d] %s %s %s %dx%d %dfps (mb: %lld) core%d op_mode%d\n", dev->ctx[i]->num, dev->ctx[i]->type == MFCINST_DECODER ? "DEC" : "ENC", dev->ctx[i]->src_fmt->name, dev->ctx[i]->dst_fmt->name, dev->ctx[i]->crop_width, dev->ctx[i]->crop_height, dev->ctx[i]->framerate / 1000, dev->ctx[i]->weighted_mb, dev->ctx[i]->op_core_num[MFC_CORE_MAIN], dev->ctx[i]->op_mode); } if (total_mb >= max_hw_mb) { mfc_dev_info("[RM] now MFC work with full spec(mb: %d / %d)\n", total_mb, max_hw_mb); for (i = 0; i < MFC_NUM_CONTEXTS; i++) { if (!dev->ctx[i]) continue; mfc_dev_info("-ctx[%d] %s %s %s %dx%d %dfps (mb: %lld) core%d op_mode%d\n", dev->ctx[i]->num, dev->ctx[i]->type == MFCINST_DECODER ? "DEC" : "ENC", dev->ctx[i]->src_fmt->name, dev->ctx[i]->dst_fmt->name, dev->ctx[i]->crop_width, dev->ctx[i]->crop_height, dev->ctx[i]->framerate / 1000, dev->ctx[i]->weighted_mb, dev->ctx[i]->op_core_num[MFC_CORE_MAIN], dev->ctx[i]->op_mode); } } node = mfc_get_node_type(file); if (node == MFCNODE_INVALID) { mfc_dev_err("cannot specify node type\n"); ret = -ENOENT; goto err_node_type; } dev->num_inst++; /* It is guarded by mfc_mutex in vfd */ /* Allocate memory for context */ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { mfc_dev_err("Not enough memory\n"); ret = -ENOMEM; goto err_ctx_alloc; } switch (node) { case MFCNODE_DECODER: vdev = dev->vfd_dec; break; case MFCNODE_ENCODER: vdev = dev->vfd_enc; break; case MFCNODE_DECODER_DRM: vdev = dev->vfd_dec_drm; break; case MFCNODE_ENCODER_DRM: vdev = dev->vfd_enc_drm; break; case MFCNODE_ENCODER_OTF: vdev = dev->vfd_enc_otf; break; case MFCNODE_ENCODER_OTF_DRM: vdev = dev->vfd_enc_otf_drm; break; default: mfc_dev_err("Invalid node(%d)\n", node); break; } if (!vdev) goto err_vdev; v4l2_fh_init(&ctx->fh, vdev); file->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); ctx->dev = dev; /* Get context number */ ctx->num = 0; while (dev->ctx[ctx->num]) { ctx->num++; if (ctx->num >= MFC_NUM_CONTEXTS) { mfc_ctx_err("Too many open contexts\n"); mfc_ctx_err("Print information to check if there was an error or not\n"); call_dop(dev, dump_info_context, dev); ret = -EBUSY; goto err_ctx_num; } } 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->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->src_q_ts.ts_list); mfc_ctx_change_idle_mode(ctx, MFC_IDLE_MODE_NONE); if (mfc_is_decoder_node(node)) { ret = __mfc_init_dec_ctx(ctx); dev->num_dec_inst++; } else { ret = __mfc_init_enc_ctx(ctx); dev->num_enc_inst++; } if (ret) goto err_ctx_init; if (dev->num_inst == 1) { /* regression test val */ if (dev->debugfs.regression_option) { dev->regression_val = vmalloc(SZ_1M); if (!dev->regression_val) mfc_ctx_err("[MFCREGRESSION] failed to allocate regression result data\n"); } /* all of the ctx list */ INIT_LIST_HEAD(&dev->ctx_list); spin_lock_init(&dev->ctx_list_lock); /* idle mode */ spin_lock_init(&dev->idle_bits_lock); } ret = call_cop(ctx, init_ctx_ctrls, ctx); if (ret) { mfc_ctx_err("failed in init_ctx_ctrls\n"); goto err_ctx_ctrls; } #if IS_ENABLED(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION) if (mfc_is_drm_node(node)) { if (dev->num_drm_inst < MFC_MAX_DRM_CTX) { dev->num_drm_inst++; ctx->is_drm = 1; mfc_ctx_info("DRM %s instance is opened [%d:%d]\n", ctx->type == MFCINST_DECODER ? "Decoder" : "Encoder", dev->num_drm_inst, dev->num_inst); } else { mfc_ctx_err("Too many instance are opened for DRM\n"); mfc_ctx_err("Print information to check if there was an error or not\n"); call_dop(dev, dump_info_context, dev); ret = -EINVAL; goto err_drm_start; } } else { mfc_ctx_info("NORMAL %s instance is opened [%d:%d]\n", ctx->type == MFCINST_DECODER ? "Decoder" : "Encoder", dev->num_drm_inst, dev->num_inst); } #endif /* Mark context as idle */ dev->ctx[ctx->num] = ctx; for (i = 0; i < MFC_NUM_CORE; i++) ctx->op_core_num[i] = MFC_CORE_INVALID; ret = mfc_rm_instance_init(dev, ctx); if (ret) { mfc_ctx_err("rm_instance_init failed\n"); goto err_drm_start; } #if IS_ENABLED(CONFIG_VIDEO_EXYNOS_REPEATER) if (mfc_is_encoder_otf_node(node)) { ret = mfc_core_otf_create(ctx); if (ret) mfc_ctx_err("[OTF] otf_create failed\n"); } #endif mfc_ctx_info("MFC open completed [%d:%d] version = %d\n", dev->num_drm_inst, dev->num_inst, MFC_DRIVER_INFO); MFC_TRACE_CTX_LT("[INFO] %s %s opened (ctx:%d, total:%d)\n", ctx->is_drm ? "DRM" : "Normal", 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 occured */ err_drm_start: call_cop(ctx, cleanup_ctx_ctrls, ctx); err_ctx_ctrls: vfree(dev->regression_val); err_ctx_init: if (mfc_is_decoder_node(node)) dev->num_dec_inst--; else dev->num_enc_inst--; dev->ctx[ctx->num] = 0; err_ctx_num: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); err_vdev: kfree(ctx); err_ctx_alloc: dev->num_inst--; err_node_type: mfc_dev_err("MFC driver open is failed [%d:%d]\n", dev->num_drm_inst, dev->num_inst); mutex_unlock(&dev->mfc_mutex); err_no_device: return ret; } /* Release MFC context */ 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; mutex_lock(&dev->mfc_mutex); mutex_lock(&dev->mfc_migrate_mutex); mfc_ctx_info("%s %s instance release is called [%d:%d], is_drm(%d)\n", ctx->is_drm ? "DRM" : "NORMAL", ctx->type == MFCINST_DECODER ? "Decoder" : "Encoder", dev->num_drm_inst, dev->num_inst, ctx->is_drm); MFC_TRACE_CTX_LT("[INFO] release is called (ctx:%d, total:%d)\n", ctx->num, dev->num_inst); /* Free resources */ v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); /* * mfc_release() can be called without a streamoff * when the application is forcibly terminated. * At that time, stop_streaming() is called by vb2_queue_release. * So, we need to performed stop_streaming * before instance de-init(CLOSE_INSTANCE). */ 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) { mfc_dev_err("failed to rm_instance_deinit\n"); goto end_release; } if (ctx->is_drm) dev->num_drm_inst--; dev->num_inst--; dev->regression_cnt = 0; if (IS_MULTI_CORE_DEVICE(dev)) mfc_rm_load_balancing(ctx, MFC_RM_LOAD_DELETE_UPDATE); mfc_meminfo_cleanup_inbuf_q(ctx); if (ctx->type == MFCINST_ENCODER) mfc_meminfo_cleanup_outbuf_q(ctx); if (dev->num_inst == 0) if (dev->regression_val) vfree(dev->regression_val); if (ctx->type == MFCINST_DECODER) { __mfc_deinit_dec_ctx(ctx); dev->num_dec_inst--; } else if (ctx->type == MFCINST_ENCODER) { __mfc_deinit_enc_ctx(ctx); dev->num_enc_inst--; } #if IS_ENABLED(CONFIG_VIDEO_EXYNOS_REPEATER) if (ctx->otf_handle) { mfc_core_otf_deinit(ctx); mfc_core_otf_destroy(ctx); } #endif 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; } } dev->ctx[ctx->num] = NULL; kfree(ctx); mfc_dev_info("mfc driver release finished [%d:%d]\n", dev->num_drm_inst, 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_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_debug(2, "wait source buffer\n"); ret = vb2_poll(&ctx->vq_src, file, wait); } else if (req_events & (POLLIN | POLLRDNORM)) { mfc_debug(2, "wait destination buffer\n"); ret = vb2_poll(&ctx->vq_dst, file, wait); } mfc_debug_leave(); return ret; } /* Mmap */ static int mfc_mmap(struct file *file, struct vm_area_struct *vma) { struct mfc_ctx *ctx = fh_to_mfc_ctx(file->private_data); unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; int ret; mfc_debug_enter(); if (offset < DST_QUEUE_OFF_BASE) { mfc_debug(2, "mmaping source\n"); ret = vb2_mmap(&ctx->vq_src, vma); } else { /* capture */ mfc_debug(2, "mmaping destination\n"); vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); ret = vb2_mmap(&ctx->vq_dst, vma); } mfc_debug_leave(); return ret; } /* v4l2 ops */ static const struct v4l2_file_operations mfc_fops = { .owner = THIS_MODULE, .open = mfc_open, .release = mfc_release, .poll = mfc_poll, .unlocked_ioctl = video_ioctl2, .mmap = mfc_mmap, }; static void __mfc_create_bitrate_table(struct mfc_dev *dev) { struct mfc_platdata *pdata = dev->pdata; int i; int max_freq, freq_ratio; max_freq = pdata->mfc_freqs[pdata->num_mfc_freq - 1]; dev->bps_ratio = pdata->max_Kbps[0] / dev->pdata->max_Kbps[1]; for (i = 0; i < pdata->num_mfc_freq; i++) { freq_ratio = pdata->mfc_freqs[i] * 100 / max_freq; dev->bitrate_table[i].bps_interval = pdata->max_Kbps[0] * freq_ratio / 100; dev->bitrate_table[i].mfc_freq = pdata->mfc_freqs[i]; dev_info(dev->device, "[QoS] bitrate table[%d] %dKHz: ~ %dKbps\n", i, dev->bitrate_table[i].mfc_freq, dev->bitrate_table[i].bps_interval); } } static int __mfc_parse_dt(struct device_node *np, struct mfc_dev *mfc) { struct mfc_platdata *pdata = mfc->pdata; if (!np) { dev_err(mfc->device, "there is no device node\n"); return -EINVAL; } /* MFC version */ of_property_read_u32(np, "ip_ver", &pdata->ip_ver); /* Debug mode */ of_property_read_u32(np, "debug_mode", &pdata->debug_mode); /* NAL-Q size */ of_property_read_u32(np, "nal_q_entry_size", &pdata->nal_q_entry_size); of_property_read_u32(np, "nal_q_dump_size", &pdata->nal_q_dump_size); /* Features */ of_property_read_u32_array(np, "nal_q", &pdata->nal_q.support, 2); 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, "color_aspect_enc", &pdata->color_aspect_enc.support, 2); of_property_read_u32_array(np, "static_info_enc", &pdata->static_info_enc.support, 2); of_property_read_u32_array(np, "hdr10_plus", &pdata->hdr10_plus.support, 2); of_property_read_u32_array(np, "vp9_stride_align", &pdata->vp9_stride_align.support, 2); of_property_read_u32_array(np, "sbwc_uncomp", &pdata->sbwc_uncomp.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, "wait_nalq_status", &pdata->wait_nalq_status.support, 2); of_property_read_u32_array(np, "drm_switch_predict", &pdata->drm_switch_predict.support, 2); of_property_read_u32_array(np, "sbwc_enc_src_ctrl", &pdata->sbwc_enc_src_ctrl.support, 2); of_property_read_u32_array(np, "metadata_interface", &pdata->metadata_interface.support, 2); of_property_read_u32_array(np, "hdr10_plus_full", &pdata->hdr10_plus_full.support, 2); of_property_read_u32_array(np, "average_qp", &pdata->average_qp.support, 2); of_property_read_u32_array(np, "mv_search_mode", &pdata->mv_search_mode.support, 2); of_property_read_u32_array(np, "hdr10_plus_stat_info", &pdata->hdr10_plus_stat_info.support, 2); of_property_read_u32_array(np, "enc_idr_flag", &pdata->enc_idr_flag.support, 2); of_property_read_u32_array(np, "min_quality_mode", &pdata->min_quality_mode.support, 2); of_property_read_u32_array(np, "enc_ts_delta", &pdata->enc_ts_delta.support, 2); of_property_read_u32_array(np, "wfd_rc_mode", &pdata->wfd_rc_mode.support, 2); /* Determine whether to enable AV1 decoder */ of_property_read_u32(np, "support_av1_dec", &pdata->support_av1_dec); of_property_read_u32_array(np, "av1_film_grain", &pdata->av1_film_grain.support, 2); /* Override value if AV1 decoder is not supported. */ if (!pdata->support_av1_dec) pdata->av1_film_grain.support = 0; /* Default 10bit format for decoding and dithering for display */ of_property_read_u32(np, "P010_decoding", &pdata->P010_decoding); of_property_read_u32(np, "dithering_enable", &pdata->dithering_enable); of_property_read_u32(np, "stride_align", &pdata->stride_align); of_property_read_u32(np, "stride_type", &pdata->stride_type); /* Formats */ of_property_read_u32(np, "support_10bit", &pdata->support_10bit); of_property_read_u32(np, "support_422", &pdata->support_422); of_property_read_u32(np, "support_rgb", &pdata->support_rgb); /* SBWC */ of_property_read_u32(np, "support_sbwc", &pdata->support_sbwc); of_property_read_u32(np, "support_sbwcl", &pdata->support_sbwcl); of_property_read_u32(np, "support_sbwcl40", &pdata->support_sbwcl40); /* SBWC */ of_property_read_u32(np, "sbwc_dec_max_width", &pdata->sbwc_dec_max_width); of_property_read_u32(np, "sbwc_dec_max_height", &pdata->sbwc_dec_max_height); of_property_read_u32(np, "sbwc_dec_max_inst_num", &pdata->sbwc_dec_max_inst_num); of_property_read_u32(np, "sbwc_dec_max_framerate", &pdata->sbwc_dec_max_framerate); of_property_read_u32(np, "sbwc_dec_hdr10_off", &pdata->sbwc_dec_hdr10_off); /* HDR10+ num max window */ of_property_read_u32(np, "max_hdr_win", &pdata->max_hdr_win); /* Default HDR10+ Profile for SEI */ of_property_read_u32(np, "hdr10_plus_profile", &pdata->hdr10_plus_profile); /* HDR10+ num max window */ of_property_read_u32(np, "display_err_type", &pdata->display_err_type); /* security ctrl */ of_property_read_u32(np, "security_ctrl", &pdata->security_ctrl); /* output buffer Q framerate */ of_property_read_u32(np, "display_framerate", &pdata->display_framerate); /* Encoder default parameter */ of_property_read_u32(np, "enc_param_num", &pdata->enc_param_num); if (pdata->enc_param_num) { of_property_read_u32_array(np, "enc_param_addr", pdata->enc_param_addr, pdata->enc_param_num); of_property_read_u32_array(np, "enc_param_val", pdata->enc_param_val, pdata->enc_param_num); } /* MFC bandwidth information */ of_property_read_u32_array(np, "bw_enc_h264", &pdata->mfc_bw_info.bw_enc_h264.peak, 3); of_property_read_u32_array(np, "bw_enc_hevc", &pdata->mfc_bw_info.bw_enc_hevc.peak, 3); of_property_read_u32_array(np, "bw_enc_hevc_10bit", &pdata->mfc_bw_info.bw_enc_hevc_10bit.peak, 3); of_property_read_u32_array(np, "bw_enc_vp8", &pdata->mfc_bw_info.bw_enc_vp8.peak, 3); of_property_read_u32_array(np, "bw_enc_vp9", &pdata->mfc_bw_info.bw_enc_vp9.peak, 3); of_property_read_u32_array(np, "bw_enc_vp9_10bit", &pdata->mfc_bw_info.bw_enc_vp9_10bit.peak, 3); of_property_read_u32_array(np, "bw_enc_mpeg4", &pdata->mfc_bw_info.bw_enc_mpeg4.peak, 3); of_property_read_u32_array(np, "bw_dec_h264", &pdata->mfc_bw_info.bw_dec_h264.peak, 3); of_property_read_u32_array(np, "bw_dec_hevc", &pdata->mfc_bw_info.bw_dec_hevc.peak, 3); of_property_read_u32_array(np, "bw_dec_hevc_10bit", &pdata->mfc_bw_info.bw_dec_hevc_10bit.peak, 3); of_property_read_u32_array(np, "bw_dec_vp8", &pdata->mfc_bw_info.bw_dec_vp8.peak, 3); of_property_read_u32_array(np, "bw_dec_vp9", &pdata->mfc_bw_info.bw_dec_vp9.peak, 3); of_property_read_u32_array(np, "bw_dec_vp9_10bit", &pdata->mfc_bw_info.bw_dec_vp9_10bit.peak, 3); of_property_read_u32_array(np, "bw_dec_av1", &pdata->mfc_bw_info.bw_dec_av1.peak, 3); of_property_read_u32_array(np, "bw_dec_av1_10bit", &pdata->mfc_bw_info.bw_dec_av1_10bit.peak, 3); of_property_read_u32_array(np, "bw_dec_mpeg4", &pdata->mfc_bw_info.bw_dec_mpeg4.peak, 3); if (pdata->support_sbwc) { of_property_read_u32_array(np, "sbwc_bw_enc_h264", &pdata->mfc_bw_info_sbwc.bw_enc_h264.peak, 3); of_property_read_u32_array(np, "sbwc_bw_enc_hevc", &pdata->mfc_bw_info_sbwc.bw_enc_hevc.peak, 3); of_property_read_u32_array(np, "sbwc_bw_enc_hevc_10bit", &pdata->mfc_bw_info_sbwc.bw_enc_hevc_10bit.peak, 3); of_property_read_u32_array(np, "sbwc_bw_enc_vp8", &pdata->mfc_bw_info_sbwc.bw_enc_vp8.peak, 3); of_property_read_u32_array(np, "sbwc_bw_enc_vp9", &pdata->mfc_bw_info_sbwc.bw_enc_vp9.peak, 3); of_property_read_u32_array(np, "sbwc_bw_enc_vp9_10bit", &pdata->mfc_bw_info_sbwc.bw_enc_vp9_10bit.peak, 3); of_property_read_u32_array(np, "sbwc_bw_enc_mpeg4", &pdata->mfc_bw_info_sbwc.bw_enc_mpeg4.peak, 3); of_property_read_u32_array(np, "sbwc_bw_dec_h264", &pdata->mfc_bw_info_sbwc.bw_dec_h264.peak, 3); of_property_read_u32_array(np, "sbwc_bw_dec_hevc", &pdata->mfc_bw_info_sbwc.bw_dec_hevc.peak, 3); of_property_read_u32_array(np, "sbwc_bw_dec_hevc_10bit", &pdata->mfc_bw_info_sbwc.bw_dec_hevc_10bit.peak, 3); of_property_read_u32_array(np, "sbwc_bw_dec_vp8", &pdata->mfc_bw_info_sbwc.bw_dec_vp8.peak, 3); of_property_read_u32_array(np, "sbwc_bw_dec_vp9", &pdata->mfc_bw_info_sbwc.bw_dec_vp9.peak, 3); of_property_read_u32_array(np, "sbwc_bw_dec_vp9_10bit", &pdata->mfc_bw_info_sbwc.bw_dec_vp9_10bit.peak, 3); of_property_read_u32_array(np, "sbwc_bw_dec_mpeg4", &pdata->mfc_bw_info_sbwc.bw_dec_mpeg4.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_vp8_vp9", &pdata->qos_weight.weight_vp8_vp9); of_property_read_u32(np, "qos_weight_av1", &pdata->qos_weight.weight_av1); of_property_read_u32(np, "qos_weight_other_codec", &pdata->qos_weight.weight_other_codec); of_property_read_u32(np, "qos_weight_3plane", &pdata->qos_weight.weight_3plane); of_property_read_u32(np, "qos_weight_10bit", &pdata->qos_weight.weight_10bit); of_property_read_u32(np, "qos_weight_422", &pdata->qos_weight.weight_422); of_property_read_u32(np, "qos_weight_bframe", &pdata->qos_weight.weight_bframe); of_property_read_u32(np, "qos_weight_num_of_ref", &pdata->qos_weight.weight_num_of_ref); of_property_read_u32(np, "qos_weight_gpb", &pdata->qos_weight.weight_gpb); of_property_read_u32(np, "qos_weight_num_of_tile", &pdata->qos_weight.weight_num_of_tile); of_property_read_u32(np, "qos_weight_super64_bframe", &pdata->qos_weight.weight_super64_bframe); 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); of_property_read_u32_array(np, "max_Kbps", pdata->max_Kbps, MAX_NUM_MFC_BPS); __mfc_create_bitrate_table(mfc); /* 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); /* QoS INT level for verification firmware */ of_property_read_u32(np, "int_freq_fw_verify", &pdata->int_freq_fw_verify); /* Memlog size */ of_property_read_u32(np, "memlog_size", &pdata->memlog_size); of_property_read_u32(np, "memlog_sfr_size", &pdata->memlog_sfr_size); /* Encoder timing info disable */ of_property_read_u32(np, "enc_timing_dis", &pdata->enc_timing_dis); return 0; } static void *__mfc_get_drv_data(struct platform_device *pdev); static struct video_device *__mfc_video_device_register(struct mfc_dev *dev, char *name, int node_num) { struct video_device *vfd; int ret = 0; vfd = video_device_alloc(); if (!vfd) { v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); return NULL; } strncpy(vfd->name, name, sizeof(vfd->name) - 1); vfd->fops = &mfc_fops; 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 = mfc_get_enc_v4l2_ioctl_ops(); vfd->lock = &dev->mfc_mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->vfl_dir = VFL_DIR_M2M; set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING; ret = video_register_device(vfd, VFL_TYPE_VIDEO, node_num); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device /dev/video%d\n", node_num); video_device_release(vfd); return NULL; } v4l2_info(&dev->v4l2_dev, "video device registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); return vfd; } #if IS_ENABLED(CONFIG_EXYNOS_THERMAL_V2) #define TMU_UNLIMITED_FPS 60 static int __mfc_tmu_notifier(struct notifier_block *nb, unsigned long state, void *nb_data) { struct mfc_dev *dev; int fps = 0; dev = container_of(nb, struct mfc_dev, tmu_nb); if (state == ISP_THROTTLING) { fps = isp_cooling_get_fps(0, *(unsigned long *)nb_data); if (fps >= TMU_UNLIMITED_FPS) { dev->tmu_fps = 0; mfc_dev_info("[TMU] THROTTLING: Unlimited FPS (%d)\n", fps); } else if (fps > 0) { dev->tmu_fps = fps * 1000; mfc_dev_info("[TMU] THROTTLING: Limited %d FPS\n", fps); } else { dev->tmu_fps = 0; mfc_dev_err("[TMU] THROTTLING: Wrong %d FPS\n", fps); } } else { mfc_dev_err("[TMU] Wrong TMU state %d\n", state); } return 0; } #endif /* MFC probe function */ static int mfc_probe(struct platform_device *pdev) { struct device *device = &pdev->dev; struct device_node *np = device->of_node; struct mfc_dev *dev; int ret = -ENOENT; dev_info(&pdev->dev, "%s is called\n", __func__); dev = devm_kzalloc(&pdev->dev, sizeof(struct mfc_dev), GFP_KERNEL); if (!dev) { dev_err(&pdev->dev, "Not enough memory for MFC device\n"); return -ENOMEM; } /* empty device for CPU cache flush with dma_sync_* API */ dev->cache_op_dev = devm_kzalloc(&pdev->dev, sizeof(struct device), GFP_KERNEL); device_initialize(dev->cache_op_dev); dma_coerce_mask_and_coherent(dev->cache_op_dev, DMA_BIT_MASK(36)); dev->device = &pdev->dev; dev->variant = __mfc_get_drv_data(pdev); platform_set_drvdata(pdev, dev); dev->pdata = devm_kzalloc(&pdev->dev, sizeof(struct mfc_platdata), GFP_KERNEL); if (!dev->pdata) { dev_err(&pdev->dev, "no memory for state\n"); ret = -ENOMEM; goto err_res_mem; } ret = __mfc_parse_dt(dev->device->of_node, dev); if (ret) goto err_res_mem; mfc_dev_init_memlog(pdev); atomic_set(&dev->trace_ref, 0); atomic_set(&dev->trace_ref_longterm, 0); dev->mfc_trace = g_mfc_trace; dev->mfc_trace_rm = g_mfc_trace_rm; dev->mfc_trace_longterm = g_mfc_trace_longterm; dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); mutex_init(&dev->mfc_mutex); mutex_init(&dev->mfc_migrate_mutex); ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) goto err_v4l2_dev; /* decoder */ dev->vfd_dec = __mfc_video_device_register(dev, MFC_DEC_NAME, EXYNOS_VIDEONODE_MFC_DEC); if (!dev->vfd_dec) { ret = -ENOMEM; goto alloc_vdev_dec; } /* encoder */ dev->vfd_enc = __mfc_video_device_register(dev, MFC_ENC_NAME, EXYNOS_VIDEONODE_MFC_ENC); if (!dev->vfd_enc) { ret = -ENOMEM; goto alloc_vdev_enc; } /* secure decoder */ dev->vfd_dec_drm = __mfc_video_device_register(dev, MFC_DEC_DRM_NAME, EXYNOS_VIDEONODE_MFC_DEC_DRM); if (!dev->vfd_dec_drm) { ret = -ENOMEM; goto alloc_vdev_dec_drm; } /* secure encoder */ dev->vfd_enc_drm = __mfc_video_device_register(dev, MFC_ENC_DRM_NAME, EXYNOS_VIDEONODE_MFC_ENC_DRM); if (!dev->vfd_enc_drm) { ret = -ENOMEM; goto alloc_vdev_enc_drm; } /* OTF encoder */ dev->vfd_enc_otf = __mfc_video_device_register(dev, MFC_ENC_OTF_NAME, EXYNOS_VIDEONODE_MFC_ENC_OTF); if (!dev->vfd_enc_otf) { ret = -ENOMEM; goto alloc_vdev_enc_otf; } /* OTF secure encoder */ dev->vfd_enc_otf_drm = __mfc_video_device_register(dev, MFC_ENC_OTF_DRM_NAME, EXYNOS_VIDEONODE_MFC_ENC_OTF_DRM); if (!dev->vfd_enc_otf_drm) { ret = -ENOMEM; goto alloc_vdev_enc_otf_drm; } /* 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 == NULL) { 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 == NULL) { dev_err(&pdev->dev, "failed to create workqueue for butler\n"); goto err_butler_wq; } INIT_WORK(&dev->butler_work, mfc_butler_worker); ret = of_reserved_mem_device_init(dev->device); if (ret) mfc_dev_err("Failed to get reserved memory region (%d)\n", ret); /* dump information call-back function */ dev->dump_ops = &mfc_dump_ops; g_mfc_dev = dev; mfc_init_debugfs(dev); #if IS_ENABLED(CONFIG_EXYNOS_THERMAL_V2) dev->tmu_nb.notifier_call = __mfc_tmu_notifier; exynos_tmu_isp_add_notifier(&dev->tmu_nb); #endif __platform_driver_register(&mfc_core_driver, THIS_MODULE); of_platform_populate(np, NULL, NULL, device); dev_info(&pdev->dev, "%s is completed\n", __func__); return 0; /* Deinit MFC if probe had failed */ err_butler_wq: destroy_workqueue(dev->migration_wq); err_migration_work: video_unregister_device(dev->vfd_enc_otf_drm); alloc_vdev_enc_otf_drm: video_unregister_device(dev->vfd_enc_otf); alloc_vdev_enc_otf: video_unregister_device(dev->vfd_enc_drm); alloc_vdev_enc_drm: video_unregister_device(dev->vfd_dec_drm); alloc_vdev_dec_drm: video_unregister_device(dev->vfd_enc); alloc_vdev_enc: video_unregister_device(dev->vfd_dec); alloc_vdev_dec: v4l2_device_unregister(&dev->v4l2_dev); err_v4l2_dev: mutex_destroy(&dev->mfc_mutex); mutex_destroy(&dev->mfc_migrate_mutex); err_res_mem: mfc_dev_deinit_memlog(dev); return ret; } /* Remove the driver */ static int mfc_remove(struct platform_device *pdev) { struct mfc_dev *dev = platform_get_drvdata(pdev); dev_dbg(&pdev->dev, "%s++\n", __func__); v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name); 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); video_unregister_device(dev->vfd_dec); video_unregister_device(dev->vfd_enc_otf); video_unregister_device(dev->vfd_enc_otf_drm); v4l2_device_unregister(&dev->v4l2_dev); #if IS_ENABLED(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION) remove_proc_entry(MFC_PROC_FW_STATUS, mfc_proc_entry); remove_proc_entry(MFC_PROC_DRM_INSTANCE_NUMBER, mfc_proc_entry); remove_proc_entry(MFC_PROC_INSTANCE_NUMBER, mfc_proc_entry); remove_proc_entry(MFC_PROC_ROOT, NULL); #endif mfc_dev_deinit_memlog(dev); mfc_dev_debug(2, "Will now deinit HW\n"); dev_dbg(&pdev->dev, "%s--\n", __func__); return 0; } static void mfc_shutdown(struct platform_device *pdev) { struct mfc_dev *dev = platform_get_drvdata(pdev); struct mfc_core *core; int i; for (i = 0; i < dev->num_core; i++) { core = dev->core[i]; if (!core) { mfc_dev_debug(2, "There is no core[%d]\n", i); continue; } if (!core->shutdown) { mfc_core_risc_off(core); core->shutdown = 1; mfc_clear_all_bits(&core->work_bits); mfc_core_err("core forcibly shutdown\n"); } } mfc_dev_info("MFC shutdown is completed\n"); } #if IS_ENABLED(CONFIG_PM_SLEEP) static int mfc_suspend(struct device *device) { struct mfc_dev *dev = platform_get_drvdata(to_platform_device(device)); struct mfc_core *core[MFC_NUM_CORE]; int i, ret; if (!dev) { dev_err(device, "no mfc device to run\n"); return -EINVAL; } for (i = 0; i < dev->num_core; i++) { core[i] = dev->core[i]; if (!core[i]) { dev_err(device, "no mfc core%d device to run\n", i); return -EINVAL; } if (core[i]->state == MFCCORE_ERROR) { dev_info(device, "[MSR] Couldn't sleep. It's Error state\n"); return 0; } } /* * Multi core mode instance can send sleep command * when there are no H/W operation both two core. */ for (i = 0; i < dev->num_core; i++) { core[i] = dev->core[i]; if (!core[i]) { dev_err(device, "no mfc core%d device to run\n", i); return -EINVAL; } if (core[i]->num_inst == 0) { core[i] = NULL; continue; } mfc_dev_info("MFC%d will suspend\n", i); ret = mfc_core_get_hwlock_dev(core[i]); if (ret < 0) { mfc_dev_err("Failed to get hwlock for MFC%d\n", i); mfc_dev_err("dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n", core[i]->hwlock.dev, core[i]->hwlock.bits, core[i]->hwlock.owned_by_irq, core[i]->hwlock.wl_count, core[i]->hwlock.transfer_owner); return -EBUSY; } if (!mfc_core_pm_get_pwr_ref_cnt(core[i])) { mfc_dev_info("MFC%d power has not been turned on yet\n", i); mfc_core_release_hwlock_dev(core[i]); core[i] = NULL; continue; } } for (i = 0; i < dev->num_core; i++) { if (core[i]) { ret = mfc_core_run_sleep(core[i]); if (ret) { mfc_dev_err("Failed core_run_sleep for MFC%d\n", i); return -EFAULT; } if (core[i]->has_llc && core[i]->llc_on_status) { mfc_llc_flush(core[i]); mfc_llc_disable(core[i]); } mfc_core_release_hwlock_dev(core[i]); mfc_dev_info("MFC%d suspend is completed\n", i); } } return 0; } static int mfc_resume(struct device *device) { struct mfc_dev *dev = platform_get_drvdata(to_platform_device(device)); struct mfc_core *core; struct mfc_core_ctx *core_ctx; int i, ret; if (!dev) { dev_err(device, "no mfc device to run\n"); return -EINVAL; } for (i = 0; i < dev->num_core; i++) { core = dev->core[i]; if (!core) { dev_err(device, "no mfc core%d device to run\n", i); return -EINVAL; } if (core->state == MFCCORE_ERROR) { mfc_core_info("[MSR] Couldn't wakeup. It's Error state\n"); return 0; } } for (i = 0; i < dev->num_core; i++) { core = dev->core[i]; if (!core) { dev_err(device, "no mfc core%d device to run\n", i); return -EINVAL; } if (core->num_inst == 0) continue; mfc_dev_info("MFC%d will resume\n", i); ret = mfc_core_get_hwlock_dev(core); if (ret < 0) { mfc_dev_err("Failed to get hwlock for MFC%d\n", i); mfc_dev_err("dev:0x%lx, bits:0x%lx, owned:%d, wl:%d, trans:%d\n", core->hwlock.dev, core->hwlock.bits, core->hwlock.owned_by_irq, core->hwlock.wl_count, core->hwlock.transfer_owner); return -EBUSY; } if (core->has_llc && (core->llc_on_status == 0)) mfc_llc_enable(core); core_ctx = core->core_ctx[core->curr_core_ctx]; if (core_ctx) mfc_llc_handle_resol(core, core_ctx->ctx); ret = mfc_core_run_wakeup(core); if (ret) { mfc_dev_err("Failed core_run_wakeup for MFC%d\n", i); return -EFAULT; } mfc_core_release_hwlock_dev(core); mfc_dev_info("MFC%d resume is completed\n", i); } return 0; } #endif #if IS_ENABLED(CONFIG_PM) static int mfc_runtime_suspend(struct device *device) { struct mfc_dev *dev = platform_get_drvdata(to_platform_device(device)); mfc_dev_debug(3, "mfc runtime suspend\n"); return 0; } static int mfc_runtime_idle(struct device *dev) { return 0; } static int mfc_runtime_resume(struct device *device) { struct mfc_dev *dev = platform_get_drvdata(to_platform_device(device)); mfc_dev_debug(3, "mfc runtime resume\n"); return 0; } #endif /* Power management */ static const struct dev_pm_ops mfc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mfc_suspend, mfc_resume) SET_RUNTIME_PM_OPS( mfc_runtime_suspend, mfc_runtime_resume, mfc_runtime_idle ) }; struct mfc_ctx_buf_size mfc_ctx_buf_size = { .dev_ctx = PAGE_ALIGN(0x7800), /* 30KB */ .h264_dec_ctx = PAGE_ALIGN(0x200000), /* 1.6MB */ .other_dec_ctx = PAGE_ALIGN(0xF000), /* 60KB */ .h264_enc_ctx = PAGE_ALIGN(0x19000), /* 100KB */ .hevc_enc_ctx = PAGE_ALIGN(0xC800), /* 50KB */ .other_enc_ctx = PAGE_ALIGN(0xC800), /* 50KB */ .dbg_info_buf = PAGE_ALIGN(0x1000), /* 4KB for DEBUG INFO */ }; struct mfc_buf_size mfc_buf_size = { .firmware_code = PAGE_ALIGN(0x100000), /* 1MB */ .cpb_buf = PAGE_ALIGN(0x300000), /* 3MB */ .ctx_buf = &mfc_ctx_buf_size, }; static struct mfc_variant mfc_drvdata = { .buf_size = &mfc_buf_size, .num_entities = 2, }; static const struct of_device_id exynos_mfc_match[] = { { .compatible = "samsung,exynos-mfc", .data = &mfc_drvdata, }, {}, }; MODULE_DEVICE_TABLE(of, exynos_mfc_match); static void *__mfc_get_drv_data(struct platform_device *pdev) { struct mfc_variant *driver_data = NULL; if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(of_match_ptr(exynos_mfc_match), pdev->dev.of_node); if (match) driver_data = (struct mfc_variant *)match->data; } else { driver_data = (struct mfc_variant *) platform_get_device_id(pdev)->driver_data; } return driver_data; } static struct platform_driver mfc_driver = { .probe = mfc_probe, .remove = mfc_remove, .shutdown = mfc_shutdown, .driver = { .name = MFC_NAME, .owner = THIS_MODULE, .pm = &mfc_pm_ops, .of_match_table = exynos_mfc_match, .suppress_bind_attrs = true, }, }; module_platform_driver(mfc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kamil Debski ");