// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen * Tiffany Lin */ #include #include #include #include #include #include #include #include "mtk_vcodec_fence.h" #include "mtk_vcodec_drv.h" #include "mtk_vcodec_util.h" #include "mtk_vcu.h" #include "mtk_vcodec_dec.h" #include "mtk_vcodec_enc.h" #include "vdec_drv_if.h" #include "venc_drv_if.h" #define LOG_PARAM_INFO_SIZE 64 #define MAX_SUPPORTED_LOG_PARAMS_COUNT 12 char mtk_vdec_tmp_log[LOG_PROPERTY_SIZE]; char mtk_venc_tmp_log[LOG_PROPERTY_SIZE]; char mtk_vdec_tmp_prop[LOG_PROPERTY_SIZE]; char mtk_venc_tmp_prop[LOG_PROPERTY_SIZE]; void mtk_vcodec_check_alive(struct timer_list *t) { struct dvfs_params *params; struct mtk_vcodec_dev *dev; /* Only support vdec check alive now */ if (mtk_vcodec_vcp & (1 << MTK_INST_DECODER)) { params = from_timer(params, t, vdec_active_checker); dev = container_of(params, struct mtk_vcodec_dev, vdec_dvfs_params); dev->check_alive_work.dev = dev; dev->check_alive_work.ctx = NULL; queue_work(dev->check_alive_workqueue, &dev->check_alive_work.work); /*retrigger timer for next check*/ params->vdec_active_checker.expires = jiffies + msecs_to_jiffies(MTK_VDEC_CHECK_ACTIVE_INTERVAL); add_timer(¶ms->vdec_active_checker); } } EXPORT_SYMBOL_GPL(mtk_vcodec_check_alive); void mtk_vcodec_alive_checker_init(struct mtk_vcodec_dev *dev) { #if IS_ENABLED(CONFIG_MTK_TINYSYS_VCP_SUPPORT) #ifdef VDEC_CHECK_ALIVE int vdec_inst_count = 0; struct mtk_vcodec_ctx *ctx; /* Only support vdec check alive now */ if (mtk_vcodec_vcp & (1 << MTK_INST_DECODER)) { list_for_each_entry(ctx, &dev->ctx_list, list) { if (ctx->type == MTK_INST_DECODER) vdec_inst_count++; } if (vdec_inst_count == 1 && !dev->vdec_dvfs_params.has_timer) { mtk_v4l2_debug(0, "[VDVFS][VDEC] init vdec alive checker..."); timer_setup(&dev->vdec_dvfs_params.vdec_active_checker, mtk_vcodec_check_alive, 0); dev->vdec_dvfs_params.vdec_active_checker.expires = jiffies + msecs_to_jiffies(MTK_VDEC_CHECK_ACTIVE_INTERVAL); add_timer(&dev->vdec_dvfs_params.vdec_active_checker); dev->vdec_dvfs_params.has_timer = 1; } } #endif #endif } void mtk_vcodec_alive_checker_deinit(struct mtk_vcodec_dev *dev) { #if IS_ENABLED(CONFIG_MTK_TINYSYS_VCP_SUPPORT) #ifdef VDEC_CHECK_ALIVE int vdec_inst_count = 0; struct mtk_vcodec_ctx *ctx; /* Only support vdec check alive now */ if (mtk_vcodec_vcp & (1 << MTK_INST_DECODER)) { list_for_each_entry(ctx, &dev->ctx_list, list) { if (ctx->type == MTK_INST_DECODER) vdec_inst_count++; } if (vdec_inst_count == 0 && dev->vdec_dvfs_params.has_timer) { del_timer_sync(&dev->vdec_dvfs_params.vdec_active_checker); dev->vdec_dvfs_params.has_timer = 0; mutex_unlock(&dev->ctx_mutex); flush_workqueue(dev->check_alive_workqueue); mtk_v4l2_debug(0, "[VDVFS][VDEC] deinit vdec alive checker..."); mutex_lock(&dev->ctx_mutex); } } #endif #endif } /* For encoder, this will enable logs in venc/*/ bool mtk_vcodec_dbg; EXPORT_SYMBOL_GPL(mtk_vcodec_dbg); /* For vcodec performance measure */ bool mtk_vcodec_perf; EXPORT_SYMBOL_GPL(mtk_vcodec_perf); /* The log level of v4l2 encoder or decoder driver. * That is, files under mtk-vcodec/. */ int mtk_v4l2_dbg_level; EXPORT_SYMBOL_GPL(mtk_v4l2_dbg_level); /* For vcodec vcp debug */ int mtk_vcodec_vcp; EXPORT_SYMBOL_GPL(mtk_vcodec_vcp); /* For vdec set property */ char *mtk_vdec_property = ""; EXPORT_SYMBOL(mtk_vdec_property); /* For venc set property */ char *mtk_venc_property = ""; EXPORT_SYMBOL(mtk_venc_property); /* For vdec vcp log info */ char *mtk_vdec_vcp_log = ""; EXPORT_SYMBOL(mtk_vdec_vcp_log); /* For venc vcp log info */ char *mtk_venc_vcp_log = ""; EXPORT_SYMBOL(mtk_venc_vcp_log); /* For venc slb cb info */ struct VENC_SLB_CB_T mtk_venc_slb_cb = {0}; EXPORT_SYMBOL(mtk_venc_slb_cb); /* For vcp vdec align mode force setting limit grouping count */ int mtk_vdec_align_limit; EXPORT_SYMBOL_GPL(mtk_vdec_align_limit); struct vcu_v4l2_func vcu_func = { NULL }; EXPORT_SYMBOL_GPL(vcu_func); bool mtk_vcodec_is_vcp(int type) { if (type > MTK_INST_ENCODER || type < MTK_INST_DECODER) return false; return (mtk_vcodec_vcp & (1 << type)); } EXPORT_SYMBOL_GPL(mtk_vcodec_is_vcp); int support_svp_region; EXPORT_SYMBOL_GPL(support_svp_region); int support_wfd_region; EXPORT_SYMBOL_GPL(support_wfd_region); /* VCODEC FTRACE */ #if VCODEC_TRACE unsigned long vcodec_get_tracing_mark(void) { static unsigned long __read_mostly tracing_mark_write_addr; if (unlikely(tracing_mark_write_addr == 0)) tracing_mark_write_addr = kallsyms_lookup_name("tracing_mark_write"); return tracing_mark_write_addr; } EXPORT_SYMBOL(vcodec_get_tracing_mark); #endif void __iomem *mtk_vcodec_get_dec_reg_addr(struct mtk_vcodec_ctx *data, unsigned int reg_idx) { struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; if (!data || reg_idx >= NUM_MAX_VDEC_REG_BASE) { mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); return NULL; } return ctx->dev->dec_reg_base[reg_idx]; } EXPORT_SYMBOL_GPL(mtk_vcodec_get_dec_reg_addr); void __iomem *mtk_vcodec_get_enc_reg_addr(struct mtk_vcodec_ctx *data, unsigned int reg_idx) { struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; if (!data || reg_idx >= NUM_MAX_VENC_REG_BASE) { mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); return NULL; } return ctx->dev->enc_reg_base[reg_idx]; } EXPORT_SYMBOL_GPL(mtk_vcodec_get_enc_reg_addr); int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, struct mtk_vcodec_mem *mem) { unsigned long size; struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; struct device *dev; if (data == NULL || mem == NULL) { mtk_v4l2_err("Invalid arguments, data=0x%x, mem=0x%x", data, mem); return -EINVAL; } size = mem->size; dev = &ctx->dev->plat_dev->dev; mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL); if (!mem->va) { mtk_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev), size); return -ENOMEM; } memset(mem->va, 0, size); mtk_v4l2_debug(4, "[%d] - va = %p", ctx->id, mem->va); mtk_v4l2_debug(4, "[%d] - dma = 0x%lx", ctx->id, (unsigned long)mem->dma_addr); mtk_v4l2_debug(4, "[%d] size = 0x%lx", ctx->id, size); return 0; } EXPORT_SYMBOL_GPL(mtk_vcodec_mem_alloc); void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, struct mtk_vcodec_mem *mem) { unsigned long size; struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; struct device *dev; if (data == NULL || mem == NULL) { mtk_v4l2_err("Invalid arguments, data=0x%x, mem=0x%x", data, mem); return; } size = mem->size; dev = &ctx->dev->plat_dev->dev; if (!mem->va) { mtk_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev), size); return; } mtk_v4l2_debug(4, "[%d] - va = %p", ctx->id, mem->va); mtk_v4l2_debug(4, "[%d] - dma = 0x%lx", ctx->id, (unsigned long)mem->dma_addr); mtk_v4l2_debug(4, "[%d] size = 0x%lx", ctx->id, size); dma_free_coherent(dev, size, mem->va, mem->dma_addr); mem->va = NULL; mem->dma_addr = 0; mem->size = 0; } EXPORT_SYMBOL_GPL(mtk_vcodec_mem_free); void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *dev, struct mtk_vcodec_ctx *ctx, unsigned int hw_id) { unsigned long flags; if (dev == NULL || hw_id >= MTK_VDEC_HW_NUM) { mtk_v4l2_err("Invalid arguments, dev=0x%x, ctx=0x%x, hw_id=%d", dev, ctx, hw_id); return; } spin_lock_irqsave(&dev->irqlock, flags); dev->curr_dec_ctx[hw_id] = ctx; spin_unlock_irqrestore(&dev->irqlock, flags); } EXPORT_SYMBOL_GPL(mtk_vcodec_set_curr_ctx); struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *dev, unsigned int hw_id) { unsigned long flags; struct mtk_vcodec_ctx *ctx; if (!dev || hw_id >= MTK_VDEC_HW_NUM) { mtk_v4l2_err("Invalid arguments, dev=0x%x, hw_id=%d", dev, hw_id); return NULL; } spin_lock_irqsave(&dev->irqlock, flags); ctx = dev->curr_dec_ctx[hw_id]; spin_unlock_irqrestore(&dev->irqlock, flags); return ctx; } EXPORT_SYMBOL_GPL(mtk_vcodec_get_curr_ctx); void mtk_vcodec_add_ctx_list(struct mtk_vcodec_ctx *ctx) { if (ctx != NULL) { mutex_lock(&ctx->dev->ctx_mutex); if (!ctx->add_list_cnt) list_add(&ctx->list, &ctx->dev->ctx_list); ctx->add_list_cnt++; mtk_vcodec_alive_checker_init(ctx->dev); mutex_unlock(&ctx->dev->ctx_mutex); } } EXPORT_SYMBOL_GPL(mtk_vcodec_add_ctx_list); void mtk_vcodec_del_ctx_list(struct mtk_vcodec_ctx *ctx) { if (ctx != NULL) { mutex_lock(&ctx->dev->ctx_mutex); ctx->add_list_cnt--; if (!ctx->add_list_cnt) list_del_init(&ctx->list); mtk_vcodec_alive_checker_deinit(ctx->dev); mutex_unlock(&ctx->dev->ctx_mutex); } } EXPORT_SYMBOL_GPL(mtk_vcodec_del_ctx_list); void mtk_vcodec_init_slice_info(struct mtk_vcodec_ctx *ctx, struct mtk_video_dec_buf *dst_buf_info) { struct vb2_v4l2_buffer *dst_buf; struct vdec_fb *pfb; struct dma_buf *dbuf; struct dma_fence *fence; if (ctx == NULL || dst_buf_info == NULL) { mtk_v4l2_err("Invalid arguments, ctx=0x%x, dst_buf_info=0x%x", ctx, dst_buf_info); return; } dst_buf = &dst_buf_info->vb; pfb = &dst_buf_info->frame_buffer; dbuf = dst_buf->vb2_buf.planes[0].dbuf; pfb->slice_done_count = 0; fence = mtk_vcodec_create_fence(ctx->dec_params.slice_count); if (fence) { dma_resv_lock(dbuf->resv, NULL); dma_resv_add_excl_fence(dbuf->resv, fence); dma_resv_unlock(dbuf->resv); dma_fence_put(fence); // make dbuf->resv the only owner } } EXPORT_SYMBOL(mtk_vcodec_init_slice_info); struct vdec_fb *mtk_vcodec_get_fb(struct mtk_vcodec_ctx *ctx) { struct vb2_buffer *dst_buf = NULL; struct vdec_fb *pfb; struct mtk_video_dec_buf *dst_buf_info; struct vb2_v4l2_buffer *dst_vb2_v4l2; int i; if (!ctx) { mtk_v4l2_err("Ctx is NULL!"); return NULL; } mtk_v4l2_debug_enter(); dst_vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); if (dst_vb2_v4l2 != NULL) dst_buf = &dst_vb2_v4l2->vb2_buf; if (dst_buf != NULL) { dst_buf_info = container_of( dst_vb2_v4l2, struct mtk_video_dec_buf, vb); pfb = &dst_buf_info->frame_buffer; pfb->num_planes = dst_vb2_v4l2->vb2_buf.num_planes; pfb->index = dst_buf->index; mutex_lock(&ctx->buf_lock); for (i = 0; i < dst_vb2_v4l2->vb2_buf.num_planes; i++) { if (mtk_v4l2_dbg_level > 0) pfb->fb_base[i].va = vb2_plane_vaddr(dst_buf, i); pfb->fb_base[i].dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, i); pfb->fb_base[i].size = ctx->picinfo.fb_sz[i]; pfb->fb_base[i].length = dst_buf->planes[i].length; pfb->fb_base[i].dmabuf = dst_buf->planes[i].dbuf; if (dst_buf_info->used == false) { get_file(dst_buf->planes[i].dbuf->file); mtk_v4l2_debug(4, "[Ref cnt] id=%d Ref get dma %p", dst_buf->index, dst_buf->planes[i].dbuf); } } pfb->status = FB_ST_INIT; dst_buf_info->used = true; mtk_vcodec_init_slice_info(ctx, dst_buf_info); ctx->fb_list[pfb->index + 1] = (uintptr_t)pfb; mtk_v4l2_debug(1, "[%d] id=%d pfb=0x%p %llx VA=%p dma_addr[0]=%lx dma_addr[1]=%lx Size=%zx fd:%x, dma_general_buf = %p, general_buf_fd = %d", ctx->id, dst_buf->index, pfb, (unsigned long long)pfb, pfb->fb_base[0].va, (unsigned long)pfb->fb_base[0].dma_addr, (unsigned long)pfb->fb_base[1].dma_addr, pfb->fb_base[0].size, dst_buf->planes[0].m.fd, pfb->dma_general_buf, pfb->general_buf_fd); dst_vb2_v4l2 = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); (*ctx->dst_cnt) = v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx); if (dst_vb2_v4l2 != NULL) dst_buf = &dst_vb2_v4l2->vb2_buf; mtk_v4l2_debug(8, "[%d] index=%d, num_rdy_bufs=%d\n", ctx->id, dst_buf->index, v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)); mutex_unlock(&ctx->buf_lock); } else { mtk_v4l2_debug(8, "[%d] No free framebuffer in v4l2!!\n", ctx->id); pfb = NULL; } mtk_v4l2_debug_leave(); return pfb; } EXPORT_SYMBOL_GPL(mtk_vcodec_get_fb); struct mtk_vcodec_mem *mtk_vcodec_get_bs(struct mtk_vcodec_ctx *ctx) { struct vb2_buffer *dst_buf; struct mtk_vcodec_mem *pbs_buf; struct mtk_video_enc_buf *dst_buf_info; struct vb2_v4l2_buffer *dst_vb2_v4l2; if (!ctx) { mtk_v4l2_err("Ctx is NULL!"); return NULL; } mtk_v4l2_debug_enter(); dst_vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); if (dst_vb2_v4l2 == NULL) { mtk_v4l2_err("[%s][%d]dst_buf empty!!", __func__, ctx->id); return NULL; } dst_buf = &dst_vb2_v4l2->vb2_buf; if (dst_buf != NULL) { dst_buf_info = container_of(dst_vb2_v4l2, struct mtk_video_enc_buf, vb); pbs_buf = &dst_buf_info->bs_buf; if (!ctx->enc_params.svp_mode && mtk_v4l2_dbg_level > 0) pbs_buf->va = vb2_plane_vaddr(dst_buf, 0); pbs_buf->dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); pbs_buf->size = (size_t)dst_buf->planes[0].length; pbs_buf->dmabuf = dst_buf->planes[0].dbuf; dst_vb2_v4l2 = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); dst_buf = &dst_vb2_v4l2->vb2_buf; if (dst_buf != NULL) { mtk_v4l2_debug(8, "[%d] index=%d, num_rdy_bufs=%d\n", ctx->id, dst_buf->index, v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)); } } else { mtk_v4l2_err("[%d] No free framebuffer in v4l2!!\n", ctx->id); pbs_buf = NULL; } mtk_v4l2_debug_leave(); return pbs_buf; } EXPORT_SYMBOL(mtk_vcodec_get_bs); int v4l2_m2m_buf_queue_check(struct v4l2_m2m_ctx *m2m_ctx, void *vbuf) { struct v4l2_m2m_buffer *b; if (vbuf == NULL) { mtk_v4l2_err("Invalid arguments, m2m_ctx=0x%x, vbuf=0x%x", m2m_ctx, vbuf); return -1; } b = container_of(vbuf, struct v4l2_m2m_buffer, vb); mtk_v4l2_debug(8, "[Debug] b %p b->list.next %p prev %p %p %p\n", b, b->list.next, b->list.prev, LIST_POISON1, LIST_POISON2); if (WARN_ON(IS_ERR_OR_NULL(m2m_ctx) || (b->list.next != LIST_POISON1 && b->list.next) || (b->list.prev != LIST_POISON2 && b->list.prev))) { v4l2_aee_print("b %p next %p prev %p already in rdyq %p %p\n", b, b->list.next, b->list.prev, LIST_POISON1, LIST_POISON2); return -1; } v4l2_m2m_buf_queue(m2m_ctx, vbuf); return 0; } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue_check); int mtk_dma_sync_sg_range(const struct sg_table *sgt, struct device *dev, unsigned int size, enum dma_data_direction direction) { struct sg_table *sgt_tmp; struct scatterlist *s_sgl, *d_sgl; unsigned int contig_size = 0; int ret, i; if (sgt == NULL || dev == NULL) { mtk_v4l2_err("sgt 0x%x dev 0x%x size %d invalid", sgt, dev, size); return -1; } if (size == 0) { mtk_v4l2_debug(1, "size %d no need to cache sync", size); return 0; } sgt_tmp = kzalloc(sizeof(*sgt_tmp), GFP_KERNEL); if (!sgt_tmp) return -1; ret = sg_alloc_table(sgt_tmp, sgt->orig_nents, GFP_KERNEL); if (ret) { mtk_v4l2_debug(0, "sg alloc table failed %d.\n", ret); kfree(sgt_tmp); return -1; } sgt_tmp->nents = 0; d_sgl = sgt_tmp->sgl; for_each_sg(sgt->sgl, s_sgl, sgt->orig_nents, i) { mtk_v4l2_debug(4, "%d contig_size %d bytesused %d.\n", i, contig_size, size); if (contig_size >= size) break; memcpy(d_sgl, s_sgl, sizeof(*s_sgl)); contig_size += s_sgl->length; d_sgl = sg_next(d_sgl); sgt_tmp->nents++; } if (direction == DMA_TO_DEVICE) { dma_sync_sg_for_device(dev, sgt_tmp->sgl, sgt_tmp->nents, direction); } else if (direction == DMA_FROM_DEVICE) { dma_sync_sg_for_cpu(dev, sgt_tmp->sgl, sgt_tmp->nents, direction); } else { mtk_v4l2_debug(0, "direction %d not correct\n", direction); return -1; } mtk_v4l2_debug(4, "flush nents %d total nents %d\n", sgt_tmp->nents, sgt->orig_nents); sg_free_table(sgt_tmp); kfree(sgt_tmp); return 0; } EXPORT_SYMBOL(mtk_dma_sync_sg_range); void v4l_fill_mtk_fmtdesc(struct v4l2_fmtdesc *fmt) { const char *descr = NULL; if (fmt == NULL) { mtk_v4l2_err("Invalid arguments, fmt=0x%x", fmt); return; } switch (fmt->pixelformat) { case V4L2_PIX_FMT_H265: descr = "H.265"; break; case V4L2_PIX_FMT_HEIF: descr = "HEIF"; break; case V4L2_PIX_FMT_WMV1: descr = "WMV1"; break; case V4L2_PIX_FMT_WMV2: descr = "WMV2"; break; case V4L2_PIX_FMT_WMV3: descr = "WMV3"; break; case V4L2_PIX_FMT_WVC1: descr = "WVC1"; break; case V4L2_PIX_FMT_WMVA: descr = "WMVA"; break; case V4L2_PIX_FMT_RV30: descr = "RealVideo 8"; break; case V4L2_PIX_FMT_RV40: descr = "RealVideo 9/10"; break; case V4L2_PIX_FMT_AV1: descr = "AV1"; break; case V4L2_PIX_FMT_MT10S: descr = "MTK 10-bit compressed single"; break; case V4L2_PIX_FMT_MT10: descr = "MTK 10-bit compressed"; break; case V4L2_PIX_FMT_P010S: descr = "10-bit P010 LSB 6-bit single"; break; case V4L2_PIX_FMT_P010M: descr = "10-bit P010 LSB 6-bit"; break; case V4L2_PIX_FMT_NV12_AFBC: descr = "AFBC NV12"; break; case V4L2_PIX_FMT_NV21_AFBC: descr = "AFBC NV21"; break; case V4L2_PIX_FMT_NV12_10B_AFBC: descr = "10-bit AFBC NV12"; break; case V4L2_PIX_FMT_RGB32_AFBC: descr = "32-bit AFBC A/XRGB 8-8-8-8"; break; case V4L2_PIX_FMT_BGR32_AFBC: descr = "32-bit AFBC A/XBGR 8-8-8-8"; break; case V4L2_PIX_FMT_RGBA1010102_AFBC: descr = "10-bit AFBC RGB 2-bit for A"; break; case V4L2_PIX_FMT_BGRA1010102_AFBC: descr = "10-bit AFBC BGR 2-bit for A"; break; case V4L2_PIX_FMT_ARGB1010102: case V4L2_PIX_FMT_ABGR1010102: case V4L2_PIX_FMT_RGBA1010102: case V4L2_PIX_FMT_BGRA1010102: descr = "10-bit for RGB, 2-bit for A"; break; case V4L2_PIX_FMT_NV12_HYFBC: descr = "8-bit yuv 420 HyFBC"; break; case V4L2_PIX_FMT_P010_HYFBC: descr = "10-bit yuv 420 HyFBC"; break; case V4L2_PIX_FMT_MT21: case V4L2_PIX_FMT_MT2110T: case V4L2_PIX_FMT_MT2110R: case V4L2_PIX_FMT_MT21C10T: case V4L2_PIX_FMT_MT21C10R: case V4L2_PIX_FMT_MT21CS: case V4L2_PIX_FMT_MT21S: case V4L2_PIX_FMT_MT21S10T: case V4L2_PIX_FMT_MT21S10R: case V4L2_PIX_FMT_MT21CS10T: case V4L2_PIX_FMT_MT21CS10R: case V4L2_PIX_FMT_MT21CSA: case V4L2_PIX_FMT_MT21S10TJ: case V4L2_PIX_FMT_MT21S10RJ: case V4L2_PIX_FMT_MT21CS10TJ: case V4L2_PIX_FMT_MT21CS10RJ: descr = "Mediatek Video Block Format"; break; default: mtk_v4l2_debug(8, "MTK Unknown pixelformat 0x%08x\n", fmt->pixelformat); break; } if (descr) WARN_ON(strscpy(fmt->description, descr, sizeof(fmt->description)) < 0); } EXPORT_SYMBOL_GPL(v4l_fill_mtk_fmtdesc); #if IS_ENABLED(CONFIG_MTK_TINYSYS_VCP_SUPPORT) #define VCP_CACHE_LINE 128 int mtk_vcodec_alloc_mem(struct vcodec_mem_obj *mem, struct device *dev, struct dma_buf_attachment **attach, struct sg_table **sgt) { struct dma_heap *dma_heap; struct dma_buf *dbuf; __u32 alloc_len; if (mem == NULL || dev == NULL || attach == NULL || sgt == NULL) { mtk_v4l2_err("Invalid arguments, mem=0x%x, dev=0x%x, attach=0x%x, sgt=0x%x", mem, dev, attach, sgt); return -EINVAL; } alloc_len = mem->len; mem->iova = 0; if (dev == NULL) { mtk_v4l2_err("dev null when type %u\n", mem->type); return -EPERM; } if (mem->len > CODEC_ALLOCATE_MAX_BUFFER_SIZE || mem->len == 0U) { mtk_v4l2_err("buffer len = %u invalid", mem->len); return -EPERM; } if (mem->type == MEM_TYPE_FOR_SW || mem->type == MEM_TYPE_FOR_HW || mem->type == MEM_TYPE_FOR_UBE_HW) { dma_heap = dma_heap_find("mtk_mm"); } else if (mem->type == MEM_TYPE_FOR_SEC_SW || mem->type == MEM_TYPE_FOR_SEC_HW || mem->type == MEM_TYPE_FOR_SEC_UBE_HW) { if (support_svp_region) dma_heap = dma_heap_find("mtk_svp_region-aligned"); else dma_heap = dma_heap_find("mtk_svp_page-uncached"); } else if (mem->type == MEM_TYPE_FOR_SEC_WFD_HW) { if (support_wfd_region) dma_heap = dma_heap_find("mtk_wfd_region-aligned"); else dma_heap = dma_heap_find("mtk_wfd_page-uncached"); } else { mtk_v4l2_err("wrong type %u\n", mem->type); return -EPERM; } if (!dma_heap) { mtk_v4l2_err("heap find fail\n"); return -EPERM; } if (mem->type == MEM_TYPE_FOR_SW || mem->type == MEM_TYPE_FOR_SEC_SW) { alloc_len = (ROUND_N(mem->len, VCP_CACHE_LINE) + VCP_CACHE_LINE); } dbuf = dma_heap_buffer_alloc(dma_heap, alloc_len, O_RDWR | O_CLOEXEC, DMA_HEAP_VALID_HEAP_FLAGS); if (IS_ERR_OR_NULL(dbuf)) { mtk_v4l2_err("buffer alloc fail\n"); return PTR_ERR(dbuf); } *attach = dma_buf_attach(dbuf, dev); if (IS_ERR_OR_NULL(*attach)) { mtk_v4l2_err("attach fail, return\n"); dma_heap_buffer_free(dbuf); return PTR_ERR(*attach); } *sgt = dma_buf_map_attachment(*attach, DMA_BIDIRECTIONAL); if (IS_ERR_OR_NULL(*sgt)) { mtk_v4l2_err("map failed, detach and return\n"); dma_buf_detach(dbuf, *attach); dma_heap_buffer_free(dbuf); return PTR_ERR(*sgt); } mem->va = (__u64)dbuf; mem->pa = (__u64)sg_dma_address((*sgt)->sgl); mem->iova = (__u64)mem->pa; if (mem->va == (__u64)NULL || mem->pa == (__u64)NULL) { mtk_v4l2_err("alloc failed, va 0x%llx pa 0x%llx iova 0x%llx len %d type %u\n", mem->va, mem->pa, mem->iova, mem->len, mem->type); return -EPERM; } mtk_v4l2_debug(8, "va 0x%llx pa 0x%llx iova 0x%llx len %d type %u\n", mem->va, mem->pa, mem->iova, mem->len, mem->type); return 0; } EXPORT_SYMBOL_GPL(mtk_vcodec_alloc_mem); int mtk_vcodec_free_mem(struct vcodec_mem_obj *mem, struct device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt) { if (mem == NULL || dev == NULL || attach == NULL || sgt == NULL) { mtk_v4l2_err("Invalid arguments, mem=0x%x, dev=0x%x, attach=0x%x, sgt=0x%x", mem, dev, attach, sgt); return -EINVAL; } if (mem->type == MEM_TYPE_FOR_SW || mem->type == MEM_TYPE_FOR_HW || mem->type == MEM_TYPE_FOR_UBE_HW || mem->type == MEM_TYPE_FOR_SEC_SW || mem->type == MEM_TYPE_FOR_SEC_HW || mem->type == MEM_TYPE_FOR_SEC_UBE_HW || mem->type == MEM_TYPE_FOR_SEC_WFD_HW) { dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); dma_buf_detach((struct dma_buf *)mem->va, attach); dma_heap_buffer_free((struct dma_buf *)mem->va); } else { mtk_v4l2_err("wrong type %d\n", mem->type); return -EPERM; } mtk_v4l2_debug(8, "va 0x%llx pa 0x%llx iova 0x%llx len %d\n", mem->va, mem->pa, mem->iova, mem->len); return 0; } EXPORT_SYMBOL_GPL(mtk_vcodec_free_mem); #endif static void mtk_vcodec_sync_log(struct mtk_vcodec_dev *dev, const char *param_key, const char *param_val, enum mtk_vcodec_log_index index) { struct mtk_vcodec_log_param *pram, *tmp; struct list_head *plist; struct mutex *plist_mutex; if (index == MTK_VCODEC_LOG_INDEX_LOG) { plist = &dev->log_param_list; plist_mutex = &dev->log_param_mutex; } else if (index == MTK_VCODEC_LOG_INDEX_PROP) { plist = &dev->prop_param_list; plist_mutex = &dev->prop_param_mutex; } else { mtk_v4l2_err("invalid index: %d", index); return; } mutex_lock(plist_mutex); list_for_each_entry(pram, plist, list) { // find existed param, replace its value if (strcmp(pram->param_key, param_key) == 0) { mtk_v4l2_debug(8, "replace old key: %s, value: %s -> %s\n", pram->param_key, pram->param_val, param_val); memset(pram->param_val, 0x00, LOG_PARAM_INFO_SIZE); strncpy(pram->param_val, param_val, LOG_PARAM_INFO_SIZE - 1); mutex_unlock(plist_mutex); return; } } // cannot find, add new pram = kzalloc(sizeof(*pram), GFP_KERNEL); strncpy(pram->param_key, param_key, LOG_PARAM_INFO_SIZE - 1); strncpy(pram->param_val, param_val, LOG_PARAM_INFO_SIZE - 1); mtk_v4l2_debug(8, "add new key: %s, value: %s\n", pram->param_key, pram->param_val); list_add(&pram->list, plist); // remove disabled log param from list if value is empty list_for_each_entry_safe(pram, tmp, plist, list) { pram->param_val[LOG_PARAM_INFO_SIZE - 1] = 0; if (strlen(pram->param_val) == 0) { mtk_v4l2_debug(8, "remove deprecated key: %s, value: %s\n", pram->param_key, pram->param_val); list_del_init(&pram->list); kfree(pram); } } mutex_unlock(plist_mutex); } static void mtk_vcodec_build_log_string(struct mtk_vcodec_dev *dev, enum mtk_vcodec_log_index log_index) { struct mtk_vcodec_log_param *pram; struct list_head *plist; struct mutex *plist_mutex; char *vdec_temp_str; char *venc_temp_str; int sl = 0, sl_temp = 0; if (log_index == MTK_VCODEC_LOG_INDEX_LOG) { plist = &dev->log_param_list; plist_mutex = &dev->log_param_mutex; vdec_temp_str = mtk_vdec_tmp_log; venc_temp_str = mtk_venc_tmp_log; } else if (log_index == MTK_VCODEC_LOG_INDEX_PROP) { plist = &dev->prop_param_list; plist_mutex = &dev->prop_param_mutex; vdec_temp_str = mtk_vdec_tmp_prop; venc_temp_str = mtk_venc_tmp_prop; } else { mtk_v4l2_err("invalid log_index: %d", log_index); return; } mutex_lock(plist_mutex); if (dev->vfd_dec) { memset(vdec_temp_str, 0x00, LOG_PROPERTY_SIZE); sl = 0; list_for_each_entry(pram, plist, list) { mtk_v4l2_debug(8, "existed log param %s: %s\n", pram->param_key, pram->param_val); if (sl >= 0 && sl < LOG_PROPERTY_SIZE && LOG_PROPERTY_SIZE - sl > 0) { sl_temp = snprintf(vdec_temp_str + sl, LOG_PROPERTY_SIZE - sl, "%s %s ", pram->param_key, pram->param_val); if (sl_temp) sl += sl_temp; else mtk_v4l2_err("venc_temp_str err usage:sl_temp: %d", sl_temp); } else mtk_v4l2_err("venc_temp_str err usage:sl: %d", sl); } if (log_index == MTK_VCODEC_LOG_INDEX_LOG) { mtk_vdec_vcp_log = vdec_temp_str; mtk_v4l2_debug(8, "build mtk_vdec_vcp_log: %s\n", mtk_vdec_vcp_log); } else if (log_index == MTK_VCODEC_LOG_INDEX_PROP) { mtk_vdec_property = vdec_temp_str; mtk_v4l2_debug(8, "build mtk_vdec_property: %s\n", mtk_vdec_property); } } else { memset(venc_temp_str, 0x00, LOG_PROPERTY_SIZE); sl = 0; list_for_each_entry(pram, plist, list) { mtk_v4l2_debug(8, "existed log param %s: %s\n", pram->param_key, pram->param_val); if (sl >= 0 && sl < LOG_PROPERTY_SIZE && LOG_PROPERTY_SIZE - sl > 0) { sl_temp = snprintf(venc_temp_str + sl, LOG_PROPERTY_SIZE - sl, "%s %s ", pram->param_key, pram->param_val); if (sl_temp) sl += sl_temp; else mtk_v4l2_err("venc_temp_str err usage:sl_temp: %d", sl_temp); } else mtk_v4l2_err("venc_temp_str err usage: %d", sl); } if (log_index == MTK_VCODEC_LOG_INDEX_LOG) { mtk_venc_vcp_log = venc_temp_str; mtk_v4l2_debug(8, "build mtk_venc_vcp_log: %s\n", mtk_venc_vcp_log); } else if (log_index == MTK_VCODEC_LOG_INDEX_PROP) { mtk_venc_property = venc_temp_str; mtk_v4l2_debug(8, "build mtk_venc_property: %s\n", mtk_venc_property); } } mutex_unlock(plist_mutex); } void mtk_vcodec_set_log(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_dev *dev, const char *val, enum mtk_vcodec_log_index log_index, void (*set_vcu_vpud_log)(struct mtk_vcodec_ctx *ctx, void *in)) { int i, argc = 0; char (*argv)[LOG_PARAM_INFO_SIZE] = NULL; char *temp = NULL; char *token = NULL; long temp_val = 0; char *log = NULL; char vcu_log[128] = {0}; int ret; if (dev == NULL || val == NULL || strlen(val) == 0) return; mtk_v4l2_debug(0, "val: %s, log_index: %d", val, log_index); argv = kzalloc(MAX_SUPPORTED_LOG_PARAMS_COUNT * 2 * LOG_PARAM_INFO_SIZE, GFP_KERNEL); if (!argv) return; log = kzalloc(LOG_PROPERTY_SIZE, GFP_KERNEL); if (!log) { kfree(argv); return; } strncpy(log, val, LOG_PROPERTY_SIZE - 1); temp = log; for (token = strsep(&temp, "\n\r "); token != NULL && argc < MAX_SUPPORTED_LOG_PARAMS_COUNT * 2; token = strsep(&temp, "\n\r ")) { if (strlen(token) == 0) continue; strncpy(argv[argc], token, LOG_PARAM_INFO_SIZE); argv[argc][LOG_PARAM_INFO_SIZE - 1] = '\0'; argc++; } for (i = 0; i < argc-1; i += 2) { if (strlen(argv[i]) == 0) continue; if (strcmp("-mtk_vcodec_dbg", argv[i]) == 0) { if (kstrtol(argv[i+1], 0, &temp_val) == 0) { mtk_vcodec_dbg = temp_val; mtk_v4l2_debug(0, "mtk_vcodec_dbg: %d\n", mtk_vcodec_dbg); } } else if (strcmp("-mtk_vcodec_perf", argv[i]) == 0) { if (kstrtol(argv[i+1], 0, &temp_val) == 0) { mtk_vcodec_perf = temp_val; mtk_v4l2_debug(0, "mtk_vcodec_perf: %d\n", mtk_vcodec_perf); } } else if (strcmp("-mtk_v4l2_dbg_level", argv[i]) == 0) { if (kstrtol(argv[i+1], 0, &temp_val) == 0) { mtk_v4l2_dbg_level = temp_val; mtk_v4l2_debug(0, "mtk_v4l2_dbg_level: %d\n", mtk_v4l2_dbg_level); } } else { // uP path if ((dev->vfd_dec && mtk_vcodec_is_vcp(MTK_INST_DECODER)) || (dev->vfd_enc && mtk_vcodec_is_vcp(MTK_INST_ENCODER))) { mtk_vcodec_sync_log(dev, argv[i], argv[i+1], log_index); } else { // vcu path if (ctx == NULL) { mtk_v4l2_err("ctx is null, cannot set log to vpud"); return; } if (log_index != MTK_VCODEC_LOG_INDEX_LOG) { mtk_v4l2_err( "invalid index: %d, only support set log on vcu path", log_index); return; } memset(vcu_log, 0x00, sizeof(vcu_log)); ret = snprintf(vcu_log, sizeof(vcu_log) - 1, "%s %s", argv[i], argv[i+1]); if (ret < 0) mtk_v4l2_err("Error in snprintf at %s line: %d ret %d", __func__, __LINE__, ret); if (set_vcu_vpud_log != NULL) set_vcu_vpud_log(ctx, vcu_log); } } } // uP path if (mtk_vcodec_is_vcp(MTK_INST_DECODER) || mtk_vcodec_is_vcp(MTK_INST_ENCODER)) mtk_vcodec_build_log_string(dev, log_index); kfree(argv); kfree(log); } EXPORT_SYMBOL_GPL(mtk_vcodec_set_log); void mtk_vcodec_get_log(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_dev *dev, char *val, enum mtk_vcodec_log_index log_index, void (*get_vcu_vpud_log)(struct mtk_vcodec_ctx *ctx, void *out)) { int len = 0; int ret = 0; if (!dev || !val) { mtk_v4l2_err("Invalid arguments, dev=0x%x, val=0x%x", dev, val); return; } memset(val, 0x00, LOG_PROPERTY_SIZE); if ((dev->vfd_dec && mtk_vcodec_is_vcp(MTK_INST_DECODER)) || (dev->vfd_enc && mtk_vcodec_is_vcp(MTK_INST_ENCODER))) { // uP path if (dev->vfd_dec) { if (log_index == MTK_VCODEC_LOG_INDEX_LOG) { strncpy(val, mtk_vdec_vcp_log, LOG_PROPERTY_SIZE - 1); } else if (log_index == MTK_VCODEC_LOG_INDEX_PROP) { strncpy(val, mtk_vdec_property, LOG_PROPERTY_SIZE - 1); } else { mtk_v4l2_err("invalid index: %d", log_index); return; } } else { if (log_index == MTK_VCODEC_LOG_INDEX_LOG) { strncpy(val, mtk_venc_vcp_log, LOG_PROPERTY_SIZE - 1); } else if (log_index == MTK_VCODEC_LOG_INDEX_PROP) { strncpy(val, mtk_venc_property, LOG_PROPERTY_SIZE - 1); } else { mtk_v4l2_err("invalid index: %d", log_index); return; } } } else { // vcu path if (ctx == NULL) { mtk_v4l2_err("ctx is null, cannot set log to vpud"); return; } if (log_index != MTK_VCODEC_LOG_INDEX_LOG) { mtk_v4l2_err( "invalid index: %d, only support get log on vcu path", log_index); return; } if (get_vcu_vpud_log != NULL) get_vcu_vpud_log(ctx, val); } // join kernel log level if (log_index == MTK_VCODEC_LOG_INDEX_LOG) { len = strlen(val); if (len < LOG_PROPERTY_SIZE) { ret = snprintf(val + len, LOG_PROPERTY_SIZE - 1 - len, " %s %d", "-mtk_vcodec_dbg", mtk_vcodec_dbg); if (ret < 0) mtk_v4l2_err("Error in snprintf at %s line: %d ret %d", __func__, __LINE__, ret); } len = strlen(val); if (len < LOG_PROPERTY_SIZE) { ret = snprintf(val + len, LOG_PROPERTY_SIZE - 1 - len, " %s %d", "-mtk_vcodec_perf", mtk_vcodec_perf); if (ret < 0) mtk_v4l2_err("Error in snprintf at %s line: %d ret %d", __func__, __LINE__, ret); } len = strlen(val); if (len < LOG_PROPERTY_SIZE) { ret = snprintf(val + len, LOG_PROPERTY_SIZE - 1 - len, " %s %d", "-mtk_v4l2_dbg_level", mtk_v4l2_dbg_level); if (ret < 0) mtk_v4l2_err("Error in snprintf at %s line: %d ret %d", __func__, __LINE__, ret); } } mtk_v4l2_debug(0, "val: %s, log_index: %d", val, log_index); } EXPORT_SYMBOL_GPL(mtk_vcodec_get_log); MODULE_LICENSE("GPL v2");