kernel-brax3-ubuntu-touch/drivers/media/platform/mtk-vcodec/vdec/vdec_vcp_if.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

1886 lines
53 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 MediaTek Inc.
* Author: Chia-Mao Hung<chia-mao.hung@mediatek.com>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/mediatek/mtk_tinysys_ipi.h>
#include <linux/mtk_vcu_controls.h>
#include <linux/delay.h>
#include <soc/mediatek/smi.h>
#include "vdec_drv_base.h"
#include "mtk_vcodec_dec.h"
#include "mtk_vcodec_drv.h"
#include "vdec_drv_if.h"
#include "vcp_ipi_pin.h"
#include "vcp_mbox_layout.h" /* for IPI mbox size */
#include "vcp_helper.h"
// TODO: need remove ISR ipis
#include "mtk_vcodec_intr.h"
#include "mtk_vcodec_dec_pm.h"
#ifdef CONFIG_MTK_ENG_BUILD
#define IPI_TIMEOUT_MS (10000U)
#else
#define IPI_TIMEOUT_MS (5000U + ((mtk_vcodec_dbg | mtk_v4l2_dbg_level) ? 5000U : 0U))
#endif
#define IPI_FIRST_DEC_START_TIMEOUT_MS (60000U)
#define IPI_POLLING_INTERVAL_US 10
struct vcp_dec_mem_list {
struct vcodec_mem_obj mem;
struct dma_buf_attachment *attach;
struct sg_table *sgt;
struct list_head list;
};
static void put_fb_to_free(struct vdec_inst *inst, struct vdec_fb *fb)
{
struct ring_fb_list *list;
if (fb != NULL) {
list = &inst->vsi->list_free;
if (list->count == DEC_MAX_FB_NUM) {
mtk_vcodec_err(inst, "[FB] put fb free_list full");
return;
}
mtk_vcodec_debug(inst,
"[FB] put fb into free_list @(%p, %llx)",
fb->fb_base[0].va,
(u64)fb->fb_base[1].dma_addr);
list->fb_list[list->write_idx].vdec_fb_va = (u64)(uintptr_t)fb;
list->write_idx = (list->write_idx == DEC_MAX_FB_NUM - 1U) ?
0U : list->write_idx + 1U;
list->count++;
}
}
static void get_pic_info(struct vdec_inst *inst,
struct vdec_pic_info *pic)
{
if (inst == NULL)
return;
if (inst->vsi == NULL)
return;
memcpy(pic, &inst->vsi->pic, sizeof(struct vdec_pic_info));
mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d), bitdepth = %d, fourcc = %d\n",
pic->pic_w, pic->pic_h, pic->buf_w, pic->buf_h,
pic->bitdepth, pic->fourcc);
mtk_vcodec_debug(inst, "Y/C(%d, %d)", pic->fb_sz[0], pic->fb_sz[1]);
}
static void get_crop_info(struct vdec_inst *inst, struct v4l2_rect *r)
{
if (inst == NULL)
return;
if (inst->vsi == NULL)
return;
r->left = inst->vsi->crop.left;
r->top = inst->vsi->crop.top;
r->width = inst->vsi->crop.width;
r->height = inst->vsi->crop.height;
mtk_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d",
r->left, r->top, r->width, r->height);
}
static void get_dpb_size(struct vdec_inst *inst, unsigned int *dpb_sz)
{
if (inst == NULL)
return;
if (inst->vsi == NULL)
return;
*dpb_sz = inst->vsi->dec.dpb_sz + 1U;
mtk_vcodec_debug(inst, "sz=%d", *dpb_sz);
}
static void get_dvfs_data(struct mtk_vcodec_dev *dev, unsigned int need)
{
dev->vdec_dvfs_params.frame_need_update = need;
}
static int vdec_vcp_ipi_send(struct vdec_inst *inst, void *msg, int len,
bool is_ack, bool need_wait_suspend, bool has_lock_dvfs)
{
int ret, ipi_size;
unsigned long timeout = 0;
struct share_obj obj;
unsigned int suspend_block_cnt = 0;
struct mutex *msg_mutex;
unsigned int *msg_signaled;
wait_queue_head_t *msg_wq;
bool is_res = false;
int ipi_wait_type = IPI_SEND_WAIT;
if (preempt_count())
ipi_wait_type = IPI_SEND_POLLING;
if (*(__u32 *)msg == AP_IPIMSG_DEC_FRAME_BUFFER) {
is_res = true;
msg_mutex = &inst->ctx->dev->ipi_mutex_res;
} else
msg_mutex = &inst->ctx->dev->ipi_mutex;
if (!is_ack) {
mutex_lock(msg_mutex);
if (need_wait_suspend) {
while (inst->ctx->dev->is_codec_suspending == 1) {
mutex_unlock(msg_mutex);
if (has_lock_dvfs)
mutex_unlock(&inst->ctx->dev->dec_dvfs_mutex);
suspend_block_cnt++;
if (suspend_block_cnt > SUSPEND_TIMEOUT_CNT) {
mtk_v4l2_debug(0, "VDEC blocked by suspend\n");
suspend_block_cnt = 0;
}
usleep_range(10000, 20000);
if (has_lock_dvfs)
mutex_lock(&inst->ctx->dev->dec_dvfs_mutex);
mutex_lock(msg_mutex);
}
}
}
if (inst->vcu.abort || inst->vcu.daemon_pid != get_vcp_generation())
goto ipi_err_unlock;
while (!is_vcp_ready(VCP_A_ID)) {
mtk_v4l2_debug((((timeout % 20) == 10) ? 0 : 4), "[VCP] wait ready %d ms", timeout);
mdelay(1);
timeout++;
if (timeout > VCP_SYNC_TIMEOUT_MS) {
mtk_vcodec_err(inst, "VCP_A_ID not ready");
mtk_smi_dbg_hang_detect("VDEC VCP");
#if IS_ENABLED(CONFIG_MTK_EMI)
mtk_emidbg_dump();
#endif
//BUG_ON(1);
goto ipi_err_unlock;
}
}
if (len > (sizeof(struct share_obj) - sizeof(int32_t) - sizeof(uint32_t))) {
mtk_vcodec_err(inst, "ipi data size wrong %d > %d", len, sizeof(struct share_obj));
goto ipi_err_unlock;
}
memset(&obj, 0, sizeof(obj));
memcpy(obj.share_buf, msg, len);
if (*(__u32 *)msg == AP_IPIMSG_DEC_FRAME_BUFFER) {
obj.id = IPI_VDEC_RESOURCE;
msg_signaled = &inst->vcu.signaled_res;
msg_wq = &inst->vcu.wq_res;
} else {
obj.id = inst->vcu.id;
msg_signaled = &inst->vcu.signaled;
msg_wq = &inst->vcu.wq;
}
obj.len = len;
ipi_size = ((sizeof(u32) * 2) + len + 3) / 4;
if (!is_ack) {
*msg_signaled = false;
if (!is_res)
inst->vcu.failure = 0;
inst->ctx->dev->codec_stop_done = false;
}
inst->ctx->err_msg = 0;
mtk_v4l2_debug(2, "id %d len %d msg 0x%x is_ack %d %d", obj.id, obj.len, *(u32 *)msg,
is_ack, *msg_signaled);
ret = mtk_ipi_send(&vcp_ipidev, IPI_OUT_VDEC_1, ipi_wait_type, &obj,
ipi_size, IPI_TIMEOUT_MS);
if (is_ack)
return 0;
if (ret != IPI_ACTION_DONE) {
mtk_vcodec_err(inst, "mtk_ipi_send %X fail %d", *(u32 *)msg, ret);
goto ipi_err_wait_and_unlock;
}
if (!is_ack) {
wait_ack:
/* wait for VCP's ACK */
if (*(__u32 *)msg == AP_IPIMSG_DEC_START && inst->ctx->state == MTK_STATE_INIT) {
timeout = msecs_to_jiffies(IPI_FIRST_DEC_START_TIMEOUT_MS);
} else {
timeout = msecs_to_jiffies(IPI_TIMEOUT_MS);
}
if (ipi_wait_type == IPI_SEND_POLLING) {
ret = IPI_TIMEOUT_MS * 1000;
while ((*msg_signaled) == false) {
udelay(IPI_POLLING_INTERVAL_US);
ret -= IPI_POLLING_INTERVAL_US;
if (ret < 0) {
ret = 0;
break;
}
}
} else
ret = wait_event_timeout(*msg_wq, *msg_signaled, timeout);
*msg_signaled = false;
if (ret == 0) {
mtk_vcodec_err(inst, "wait vcp ipi %X ack time out! %d %d",
*(u32 *)msg, ret, inst->vcu.failure);
goto ipi_err_wait_and_unlock;
} else if (-ERESTARTSYS == ret) {
mtk_vcodec_err(inst, "wait vcp ipi %X ack ret %d RESTARTSYS retry! (%d)",
*(u32 *)msg, ret, inst->vcu.failure);
goto wait_ack;
} else if (ret < 0) {
mtk_vcodec_err(inst, "wait vcp ipi %X ack fail ret %d! (%d)",
*(u32 *)msg, ret, inst->vcu.failure);
}
}
mutex_unlock(msg_mutex);
if (!is_ack && !is_res)
return inst->vcu.failure;
return 0;
ipi_err_wait_and_unlock:
timeout = 0;
if (inst->vcu.daemon_pid == get_vcp_generation()) {
trigger_vcp_halt(VCP_A_ID);
while (inst->vcu.daemon_pid == get_vcp_generation() ||
!inst->ctx->dev->codec_stop_done) {
if (timeout > VCP_SYNC_TIMEOUT_MS) {
mtk_v4l2_debug(0, "halt restart timeout %x\n",
inst->vcu.daemon_pid);
break;
}
usleep_range(10000, 20000);
timeout += 10;
}
}
inst->ctx->dev->codec_stop_done = false;
inst->vcu.failure = VDEC_IPI_MSG_STATUS_FAIL;
inst->ctx->err_msg = *(__u32 *)msg;
ipi_err_unlock:
inst->vcu.abort = 1;
if (!is_ack)
mutex_unlock(msg_mutex);
return -EIO;
}
static void handle_init_ack_msg(struct mtk_vcodec_dev *dev, struct vdec_vcu_ipi_init_ack *msg)
{
struct vdec_vcu_inst *vcu = (struct vdec_vcu_inst *)
(unsigned long)msg->ap_inst_addr;
__u64 shmem_pa_start = (__u64)vcp_get_reserve_mem_phys(VDEC_MEM_ID);
__u64 inst_offset = ((msg->vcu_inst_addr & 0x0FFFFFFF) - (shmem_pa_start & 0x0FFFFFFF));
if (vcu == NULL)
return;
mtk_vcodec_debug(vcu, "+ ap_inst_addr = 0x%lx",
(uintptr_t)msg->ap_inst_addr);
vcu->vsi = (void *)((__u64)vcp_get_reserve_mem_virt(VDEC_MEM_ID) + inst_offset);
vcu->inst_addr = msg->vcu_inst_addr;
dev->tf_info = (struct mtk_tf_info *)
((__u64)vcp_get_reserve_mem_virt(VDEC_MEM_ID) + VDEC_TF_INFO_OFFSET);
mtk_vcodec_debug(vcu, "- vcu_inst_addr = 0x%llx", vcu->inst_addr);
}
static void handle_query_cap_ack_msg(struct vdec_vcu_ipi_query_cap_ack *msg)
{
struct vdec_vcu_inst *vcu = (struct vdec_vcu_inst *)msg->ap_inst_addr;
void *data;
int size = 0;
__u64 shmem_pa_start = (__u64)vcp_get_reserve_mem_phys(VDEC_MEM_ID);
__u64 data_offset = ((msg->vcu_data_addr & 0x0FFFFFFF) - (shmem_pa_start & 0x0FFFFFFF));
if (vcu == NULL)
return;
mtk_vcodec_debug(vcu, "+ ap_inst_addr = 0x%lx, vcu_data_addr = 0x%llx, id = %d",
(uintptr_t)msg->ap_inst_addr, msg->vcu_data_addr, msg->id);
/* mapping VCU address to kernel virtual address */
data = (void *)((__u64)vcp_get_reserve_mem_virt(VDEC_MEM_ID) + data_offset);
if (data == NULL)
return;
switch (msg->id) {
case GET_PARAM_VDEC_CAP_SUPPORTED_FORMATS:
size = sizeof(struct mtk_video_fmt);
memcpy((void *)msg->ap_data_addr, data,
size * MTK_MAX_DEC_CODECS_SUPPORT);
break;
case GET_PARAM_VDEC_CAP_FRAME_SIZES:
size = sizeof(struct mtk_codec_framesizes);
memcpy((void *)msg->ap_data_addr, data,
size * MTK_MAX_DEC_CODECS_SUPPORT);
break;
default:
break;
}
mtk_vcodec_debug(vcu, "- vcu_inst_addr = 0x%llx", vcu->inst_addr);
}
static struct device *get_dev_by_mem_type(struct vdec_inst *inst, struct vcodec_mem_obj *mem)
{
if (inst->ctx->dec_params.svp_mode) {
if (mem->type == MEM_TYPE_FOR_SW)
mem->type = MEM_TYPE_FOR_SEC_SW;
else if (mem->type == MEM_TYPE_FOR_HW)
mem->type = MEM_TYPE_FOR_SEC_HW;
else if (mem->type == MEM_TYPE_FOR_UBE_HW)
mem->type = MEM_TYPE_FOR_SEC_UBE_HW;
}
if (mem->type == MEM_TYPE_FOR_SW) {
if (inst->ctx->id & 1)
return vcp_get_io_device(VCP_IOMMU_256MB1);
else
return vcp_get_io_device(VCP_IOMMU_WORK_256MB2);
} else if (mem->type == MEM_TYPE_FOR_SEC_SW)
return vcp_get_io_device(VCP_IOMMU_SEC);
else if (mem->type == MEM_TYPE_FOR_HW || mem->type == MEM_TYPE_FOR_SEC_HW)
return &inst->vcu.ctx->dev->plat_dev->dev;
else if (mem->type == MEM_TYPE_FOR_UBE_HW || mem->type == MEM_TYPE_FOR_SEC_UBE_HW) {
if (vcp_get_io_device(VCP_IOMMU_UBE_LAT) != NULL)
return vcp_get_io_device(VCP_IOMMU_UBE_LAT);
else if (vcp_get_io_device(VCP_IOMMU_UBE_CORE) != NULL)
return vcp_get_io_device(VCP_IOMMU_UBE_CORE);
else
return &inst->vcu.ctx->dev->plat_dev->dev;
} else
return NULL;
}
static void handle_vdec_mem_alloc(struct vdec_vcu_ipi_mem_op *msg)
{
struct vdec_vcu_inst *vcu = (struct vdec_vcu_inst *)msg->ap_inst_addr;
struct vdec_inst *inst = NULL;
struct device *dev = NULL;
struct vcp_dec_mem_list *tmp = NULL;
struct dma_buf_attachment *attach = NULL;
struct sg_table *sgt = NULL;
if (msg->mem.type == MEM_TYPE_FOR_SHM) {
msg->status = 0;
msg->mem.va = (__u64)vcp_get_reserve_mem_virt(VDEC_MEM_ID);
msg->mem.pa = (__u64)vcp_get_reserve_mem_phys(VDEC_MEM_ID);
msg->mem.len = (__u64)vcp_get_reserve_mem_size(VDEC_MEM_ID);
msg->mem.iova = msg->mem.pa;
mtk_v4l2_debug(4, "va 0x%llx pa 0x%llx iova 0x%llx len %d type %d size of %d %d\n",
msg->mem.va, msg->mem.pa, msg->mem.iova, msg->mem.len, msg->mem.type,
sizeof(msg->mem), sizeof(*msg));
} else {
if (IS_ERR_OR_NULL(vcu))
return;
inst = container_of(vcu, struct vdec_inst, vcu);
dev = get_dev_by_mem_type(inst, &msg->mem);
msg->status = mtk_vcodec_alloc_mem(&msg->mem, dev, &attach, &sgt);
mtk_vcodec_debug(vcu, "va 0x%llx pa 0x%llx iova 0x%llx len %d type %d\n",
msg->mem.va, msg->mem.pa, msg->mem.iova, msg->mem.len, msg->mem.type);
}
if (msg->status) {
mtk_vcodec_err(vcu, "fail %d, va 0x%llx pa 0x%llx iova 0x%llx len %d type %d",
msg->status, msg->mem.va, msg->mem.pa,
msg->mem.iova, msg->mem.len, msg->mem.type);
/* reset prevent VCP TF */
msg->mem.pa = 0;
msg->mem.iova = 0;
} else if (msg->mem.type != MEM_TYPE_FOR_SHM) {
tmp = kmalloc(sizeof(struct vcp_dec_mem_list), GFP_KERNEL);
if (tmp) {
mutex_lock(vcu->ctx_ipi_lock);
tmp->attach = attach;
tmp->sgt = sgt;
tmp->mem = msg->mem;
list_add_tail(&tmp->list, &vcu->bufs);
mutex_unlock(vcu->ctx_ipi_lock);
}
}
}
static void handle_vdec_mem_free(struct vdec_vcu_ipi_mem_op *msg)
{
struct vdec_vcu_inst *vcu = (struct vdec_vcu_inst *)msg->ap_inst_addr;
struct vdec_inst *inst = NULL;
struct device *dev = NULL;
struct vcp_dec_mem_list *tmp = NULL;
struct list_head *p, *q;
bool found = 0;
if (IS_ERR_OR_NULL(vcu))
return;
mutex_lock(vcu->ctx_ipi_lock);
inst = container_of(vcu, struct vdec_inst, vcu);
dev = get_dev_by_mem_type(inst, &msg->mem);
list_for_each_safe(p, q, &vcu->bufs) {
tmp = list_entry(p, struct vcp_dec_mem_list, list);
if (!memcmp(&tmp->mem, &msg->mem, sizeof(struct vcodec_mem_obj))) {
found = 1;
list_del(p);
break;
}
}
mutex_unlock(vcu->ctx_ipi_lock);
if (!found) {
mtk_vcodec_err(vcu, "not found %d, va 0x%llx pa 0x%llx iova 0x%llx len %d type %d",
msg->status, msg->mem.va, msg->mem.pa,
msg->mem.iova, msg->mem.len, msg->mem.type);
return;
}
mtk_vcodec_debug(vcu, "va 0x%llx pa 0x%llx iova 0x%llx len %d type %d\n",
msg->mem.va, msg->mem.pa, msg->mem.iova, msg->mem.len, msg->mem.type);
msg->status = mtk_vcodec_free_mem(&msg->mem, dev, tmp->attach, tmp->sgt);
kfree(tmp);
if (msg->status)
mtk_vcodec_err(vcu, "fail %d, va 0x%llx pa 0x%llx iova 0x%llx len %d type %d",
msg->status, msg->mem.va, msg->mem.pa,
msg->mem.iova, msg->mem.len, msg->mem.type);
}
void vdec_dump_mem_buf(unsigned long h_vdec)
{
struct vdec_inst *inst = (struct vdec_inst *)h_vdec;
struct list_head *list_ptr, *tmp;
struct vcp_dec_mem_list *mem_list = NULL;
unsigned int bs_fourcc;
char codec_fourcc[5] = {0};
bs_fourcc = inst->ctx->q_data[MTK_Q_DATA_SRC].fmt->fourcc;
codec_fourcc[0] = bs_fourcc & 0xFF;
codec_fourcc[1] = (bs_fourcc >> 8) & 0xFF;
codec_fourcc[2] = (bs_fourcc >> 16) & 0xFF;
codec_fourcc[3] = (bs_fourcc >> 24) & 0xFF;
mutex_lock(inst->vcu.ctx_ipi_lock);
list_for_each_safe(list_ptr, tmp, &inst->vcu.bufs) {
mem_list = list_entry(list_ptr, struct vcp_dec_mem_list, list);
mtk_v4l2_err("[%d] %s working buffer va 0x%llx pa 0x%llx iova 0x%llx len %d type %d",
inst->ctx->id, codec_fourcc, mem_list->mem.va, mem_list->mem.pa,
mem_list->mem.iova, mem_list->mem.len, mem_list->mem.type);
}
mutex_unlock(inst->vcu.ctx_ipi_lock);
}
static int check_codec_id(struct vdec_vcu_ipi_ack *msg, unsigned int fmt, unsigned int svp)
{
int codec_id = 0, ret = 0;
switch (fmt) {
case V4L2_PIX_FMT_H264:
codec_id = VDEC_H264;
break;
case V4L2_PIX_FMT_H265:
codec_id = VDEC_H265;
break;
case V4L2_PIX_FMT_HEIF:
codec_id = VDEC_HEIF;
break;
case V4L2_PIX_FMT_VP8:
codec_id = VDEC_VP8;
break;
case V4L2_PIX_FMT_VP9:
codec_id = VDEC_VP9;
break;
case V4L2_PIX_FMT_MPEG4:
codec_id = VDEC_MPEG4;
break;
case V4L2_PIX_FMT_H263:
codec_id = VDEC_H263;
break;
case V4L2_PIX_FMT_MPEG1:
case V4L2_PIX_FMT_MPEG2:
codec_id = VDEC_MPEG12;
break;
case V4L2_PIX_FMT_WMV1:
case V4L2_PIX_FMT_WMV2:
case V4L2_PIX_FMT_WMV3:
case V4L2_PIX_FMT_WMVA:
case V4L2_PIX_FMT_WVC1:
codec_id = VDEC_WMV;
break;
case V4L2_PIX_FMT_RV30:
codec_id = VDEC_RV30;
break;
case V4L2_PIX_FMT_RV40:
codec_id = VDEC_RV40;
break;
case V4L2_PIX_FMT_AV1:
codec_id = VDEC_AV1;
break;
default:
pr_info("%s no fourcc", __func__);
break;
}
if (codec_id == 0) {
pr_info("[error] vdec unsupported fourcc\n");
ret = -1;
} else if (msg->codec_id == codec_id && msg->status == svp) {
pr_info("%s ipi id %d svp %d is correct\n", __func__, msg->codec_id, msg->status);
ret = 0;
} else {
mtk_v4l2_debug(2, "[Info] ipi id %d svp %d is incorrect\n",
msg->codec_id, msg->status);
ret = -1;
}
return ret;
}
static void vdec_vcp_free_mq_node(struct mtk_vcodec_dev *dev,
struct mtk_vcodec_msg_node *mq_node)
{
unsigned long flags;
spin_lock_irqsave(&dev->mq.lock, flags);
list_add(&mq_node->list, &dev->mq.nodes);
spin_unlock_irqrestore(&dev->mq.lock, flags);
}
int vcp_dec_ipi_handler(void *arg)
{
struct mtk_vcodec_dev *dev = (struct mtk_vcodec_dev *)arg;
struct vdec_vcu_ipi_ack *msg = NULL;
struct vdec_vcu_inst *vcu = NULL;
struct vdec_inst *inst = NULL;
struct share_obj *obj;
struct vdec_vsi *vsi;
int ret = 0;
struct mtk_vcodec_msg_node *mq_node;
struct vdec_vcu_ipi_mem_op *shem_msg;
unsigned long flags;
struct list_head *p, *q;
struct mtk_vcodec_ctx *temp_ctx;
int msg_valid = 0;
mtk_v4l2_debug_enter();
BUILD_BUG_ON(sizeof(struct vdec_ap_ipi_cmd) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_ap_ipi_init) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_ap_ipi_dec_start) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_ap_ipi_set_param) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_ap_ipi_query_cap) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_vcu_ipi_ack) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_vcu_ipi_init_ack) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_vcu_ipi_query_cap_ack) > SHARE_BUF_SIZE);
BUILD_BUG_ON(sizeof(struct vdec_vcu_ipi_mem_op) > SHARE_BUF_SIZE);
do {
ret = wait_event_interruptible(dev->mq.wq, atomic_read(&dev->mq.cnt) > 0);
if (ret < 0) {
mtk_v4l2_debug(0, "wait event return %d (suspending %d)\n",
ret, atomic_read(&dev->mq.cnt));
continue;
}
spin_lock_irqsave(&dev->mq.lock, flags);
mq_node = list_entry(dev->mq.head.next, struct mtk_vcodec_msg_node, list);
list_del(&(mq_node->list));
atomic_dec(&dev->mq.cnt);
spin_unlock_irqrestore(&dev->mq.lock, flags);
obj = &mq_node->ipi_data;
msg = (struct vdec_vcu_ipi_ack *)obj->share_buf;
if (msg == NULL || (struct vdec_vcu_inst *)msg->ap_inst_addr == NULL) {
mtk_v4l2_err(" msg invalid %lx\n", msg);
vdec_vcp_free_mq_node(dev, mq_node);
continue;
}
/* handling VSI (shared memory) preparation when VCP init service without inst*/
if (msg->msg_id == VCU_IPIMSG_DEC_MEM_ALLOC) {
shem_msg = (struct vdec_vcu_ipi_mem_op *)obj->share_buf;
if (shem_msg->mem.type == MEM_TYPE_FOR_SHM) {
handle_vdec_mem_alloc((void *)shem_msg);
shem_msg->vcp_addr[0] = (__u32)VCP_PACK_IOVA(
vcp_get_reserve_mem_phys(VDEC_SET_PROP_MEM_ID));
shem_msg->vcp_addr[1] = (__u32)VCP_PACK_IOVA(
vcp_get_reserve_mem_phys(VDEC_VCP_LOG_INFO_ID));
shem_msg->msg_id = AP_IPIMSG_DEC_MEM_ALLOC_DONE;
ret = mtk_ipi_send(&vcp_ipidev, IPI_OUT_VDEC_1, IPI_SEND_WAIT, obj,
PIN_OUT_SIZE_VDEC, 100);
if (ret != IPI_ACTION_DONE)
mtk_v4l2_err("mtk_ipi_send (msg_id %X) fail %d",
msg->msg_id, ret);
vdec_vcp_free_mq_node(dev, mq_node);
continue;
}
}
vcu = (struct vdec_vcu_inst *)(unsigned long)msg->ap_inst_addr;
/* Check IPI inst is valid */
mutex_lock(&dev->ctx_mutex);
msg_valid = 0;
list_for_each_safe(p, q, &dev->ctx_list) {
temp_ctx = list_entry(p, struct mtk_vcodec_ctx, list);
inst = (struct vdec_inst *)temp_ctx->drv_handle;
if (inst != NULL && vcu == &inst->vcu && vcu->ctx == temp_ctx) {
msg_valid = 1;
break;
}
}
if (!msg_valid) {
mtk_v4l2_err(" msg msg_id %X vcu not exist %p\n", msg->msg_id, vcu);
mutex_unlock(&dev->ctx_mutex);
vdec_vcp_free_mq_node(dev, mq_node);
continue;
}
if (vcu->abort || vcu->daemon_pid != get_vcp_generation()) {
mtk_v4l2_err(" [%d] msg msg_id %X vcu abort %d %d\n",
msg->msg_id, vcu->ctx->id, vcu->daemon_pid, get_vcp_generation());
mutex_unlock(&dev->ctx_mutex);
vdec_vcp_free_mq_node(dev, mq_node);
continue;
}
mtk_v4l2_debug(2, "+ pop msg_id %X ml_cnt %d, vcu %lx, status %d",
msg->msg_id, atomic_read(&dev->mq.cnt), (unsigned long)vcu, msg->status);
vsi = (struct vdec_vsi *)vcu->vsi;
inst = container_of(vcu, struct vdec_inst, vcu);
if (msg->msg_id == VCU_IPIMSG_DEC_CHECK_CODEC_ID) {
if (check_codec_id(msg, vcu->ctx->q_data[MTK_Q_DATA_SRC].fmt->fourcc,
vcu->ctx->dec_params.svp_mode) == 0)
msg->status = 0;
else
msg->status = -1;
msg->msg_id = AP_IPIMSG_DEC_CHECK_CODEC_ID_DONE;
msg->ctx_id = inst->ctx->id;
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
} else if (msg->status == VDEC_IPI_MSG_STATUS_OK) {
if ((msg->msg_id & 0xF000) == VCU_IPIMSG_VDEC_SEND_BASE)
msg->ctx_id = inst->ctx->id;
switch (msg->msg_id) {
case VCU_IPIMSG_DEC_DONE:
vcu->signaled_res = true;
wake_up(&vcu->wq_res);
break;
case VCU_IPIMSG_DEC_INIT_DONE:
handle_init_ack_msg(dev, (void *)obj->share_buf);
vcu->ctx->state = MTK_STATE_INIT;
case VCU_IPIMSG_DEC_START_DONE:
case VCU_IPIMSG_DEC_DEINIT_DONE:
case VCU_IPIMSG_DEC_RESET_DONE:
case VCU_IPIMSG_DEC_SET_PARAM_DONE:
case VCU_IPIMSG_DEC_BACKUP_DONE:
vcu->signaled = true;
wake_up(&vcu->wq);
break;
case VCU_IPIMSG_DEC_QUERY_CAP_DONE:
handle_query_cap_ack_msg((void *)obj->share_buf);
vcu->signaled = true;
wake_up(&vcu->wq);
break;
case VCU_IPIMSG_DEC_PUT_FRAME_BUFFER:
mtk_vdec_put_fb(vcu->ctx, PUT_BUFFER_CALLBACK,
msg->no_need_put != 0);
msg->msg_id = AP_IPIMSG_DEC_PUT_FRAME_BUFFER_DONE;
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
break;
case VCU_IPIMSG_DEC_MEM_ALLOC:
handle_vdec_mem_alloc((void *)obj->share_buf);
msg->msg_id = AP_IPIMSG_DEC_MEM_ALLOC_DONE;
vdec_vcp_ipi_send(inst, msg,
sizeof(struct vdec_vcu_ipi_mem_op), true, false, false);
break;
case VCU_IPIMSG_DEC_MEM_FREE:
handle_vdec_mem_free((void *)obj->share_buf);
msg->msg_id = AP_IPIMSG_DEC_MEM_FREE_DONE;
vdec_vcp_ipi_send(inst, msg,
sizeof(struct vdec_vcu_ipi_mem_op), true, false, false);
break;
// TODO: need remove HW locks /power & ISR ipis
case VCU_IPIMSG_DEC_LOCK_LAT:
get_dvfs_data(vcu->ctx->dev, msg->no_need_put);
atomic_set(&dev->dec_hw_active[MTK_VDEC_LAT], 1);
vdec_decode_prepare(vcu->ctx, MTK_VDEC_LAT);
msg->msg_id = AP_IPIMSG_DEC_LOCK_LAT_DONE;
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
break;
case VCU_IPIMSG_DEC_UNLOCK_LAT:
get_dvfs_data(vcu->ctx->dev, msg->no_need_put);
atomic_set(&dev->dec_hw_active[MTK_VDEC_LAT], 0);
vdec_decode_unprepare(vcu->ctx, MTK_VDEC_LAT);
msg->msg_id = AP_IPIMSG_DEC_UNLOCK_LAT_DONE;
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
break;
case VCU_IPIMSG_DEC_LOCK_CORE:
get_dvfs_data(vcu->ctx->dev, msg->no_need_put);
atomic_set(&dev->dec_hw_active[MTK_VDEC_CORE], 1);
vdec_decode_prepare(vcu->ctx, MTK_VDEC_CORE);
msg->msg_id = AP_IPIMSG_DEC_LOCK_CORE_DONE;
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
break;
case VCU_IPIMSG_DEC_UNLOCK_CORE:
get_dvfs_data(vcu->ctx->dev, msg->no_need_put);
atomic_set(&dev->dec_hw_active[MTK_VDEC_CORE], 0);
vdec_decode_unprepare(vcu->ctx, MTK_VDEC_CORE);
msg->msg_id = AP_IPIMSG_DEC_UNLOCK_CORE_DONE;
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
break;
case VCU_IPIMSG_DEC_WAITISR:
vcodec_trace_count("VDEC_HW_CORE", 2);
/* wait decoder done interrupt */
ret = mtk_vcodec_wait_for_done_ctx(vcu->ctx,
msg->status,
MTK_INST_IRQ_RECEIVED,
WAIT_INTR_TIMEOUT_MS);
msg->msg_id = AP_IPIMSG_DEC_WAITISR_DONE;
msg->status = ret;
vcodec_trace_count("VDEC_HW_CORE", 1);
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
break;
case VCU_IPIMSG_DEC_GET_FRAME_BUFFER:
mtk_vcodec_err(vcu, "GET_FRAME_BUFFER not support", msg->msg_id);
break;
default:
mtk_vcodec_err(vcu, "invalid msg=%X", msg->msg_id);
break;
}
} else {
switch (msg->msg_id) {
case VCU_IPIMSG_DEC_DONE:
vcu->signaled_res = true;
wake_up(&vcu->wq_res);
break;
case VCU_IPIMSG_DEC_INIT_DONE:
case VCU_IPIMSG_DEC_START_DONE:
case VCU_IPIMSG_DEC_DEINIT_DONE:
case VCU_IPIMSG_DEC_RESET_DONE:
case VCU_IPIMSG_DEC_SET_PARAM_DONE:
case VCU_IPIMSG_DEC_QUERY_CAP_DONE:
case VCU_IPIMSG_DEC_BACKUP_DONE:
vcu->failure = msg->status;
vcu->signaled = true;
wake_up(&vcu->wq);
break;
case VCU_IPIMSG_DEC_WAITISR:
vcodec_trace_count("VDEC_HW_LAT", 2);
/* wait decoder done interrupt */
ret = mtk_vcodec_wait_for_done_ctx(vcu->ctx,
msg->status,
MTK_INST_IRQ_RECEIVED,
WAIT_INTR_TIMEOUT_MS);
msg->msg_id = AP_IPIMSG_DEC_WAITISR_DONE;
msg->ctx_id = inst->ctx->id;
msg->status = ret;
vdec_vcp_ipi_send(inst, msg, sizeof(*msg), true, false, false);
vcodec_trace_count("VDEC_HW_LAT", 1);
break;
default:
mtk_vcodec_err(vcu, "invalid msg=%X", msg->msg_id);
break;
}
}
mtk_vcodec_debug(vcu, "- id=%X", msg->msg_id);
mutex_unlock(&dev->ctx_mutex);
vdec_vcp_free_mq_node(dev, mq_node);
} while (!kthread_should_stop());
mtk_v4l2_debug_leave();
return ret;
}
static int vdec_vcp_ipi_isr(unsigned int id, void *prdata, void *data, unsigned int len)
{
struct mtk_vcodec_dev *dev = (struct mtk_vcodec_dev *)prdata;
struct vdec_vcu_ipi_ack *msg = NULL;
struct share_obj *obj = (struct share_obj *)data;
struct mtk_vcodec_msg_node *mq_node;
unsigned long flags;
msg = (struct vdec_vcu_ipi_ack *)obj->share_buf;
// add to ipi msg list
spin_lock_irqsave(&dev->mq.lock, flags);
if (!list_empty(&dev->mq.nodes)) {
mq_node = list_entry(dev->mq.nodes.next, struct mtk_vcodec_msg_node, list);
memcpy(&mq_node->ipi_data, obj, sizeof(struct share_obj));
list_move_tail(&mq_node->list, &dev->mq.head);
atomic_inc(&dev->mq.cnt);
spin_unlock_irqrestore(&dev->mq.lock, flags);
mtk_v4l2_debug(8, "push ipi_id %x msg_id %x, ml_cnt %d",
obj->id, msg->msg_id, atomic_read(&dev->mq.cnt));
wake_up(&dev->mq.wq);
} else {
spin_unlock_irqrestore(&dev->mq.lock, flags);
mtk_v4l2_err("mq no free nodes\n");
}
return 0;
}
static struct mtk_vcodec_ctx *get_valid_ctx(struct mtk_vcodec_dev *dev)
{
struct list_head *p, *q;
struct mtk_vcodec_ctx *tmp_ctx;
if (!is_vcp_ready(VCP_A_ID))
return NULL;
mutex_lock(&dev->ctx_mutex);
if (list_empty(&dev->ctx_list)) {
mutex_unlock(&dev->ctx_mutex);
return NULL;
}
list_for_each_safe(p, q, &dev->ctx_list) {
tmp_ctx = list_entry(p, struct mtk_vcodec_ctx, list);
if (tmp_ctx != NULL && tmp_ctx->drv_handle != 0 &&
tmp_ctx->state < MTK_STATE_ABORT && tmp_ctx->state > MTK_STATE_FREE) {
mutex_unlock(&dev->ctx_mutex);
return tmp_ctx;
}
}
mutex_unlock(&dev->ctx_mutex);
return NULL;
}
static int vdec_vcp_backup(struct mtk_vcodec_dev *dev)
{
struct mtk_vcodec_ctx *ctx;
struct vdec_inst *inst;
struct vdec_ap_ipi_cmd msg;
int err = 0;
ctx = get_valid_ctx(dev);
if (!ctx) {
mtk_v4l2_debug(2, "no valid inst need backup");
return err;
}
inst = (struct vdec_inst *)ctx->drv_handle;
mtk_v4l2_debug(1, "backup by ctx %d", ctx->id);
mtk_vcodec_debug_enter(inst);
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_BACKUP;
msg.ctx_id = ctx->id;
msg.vcu_inst_addr = inst->vcu.inst_addr;
err = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, false, false);
mtk_vcodec_debug(inst, "- ret=%d", err);
return err;
}
static void vdec_vcp_mmdvfs_resume(struct mtk_vcodec_ctx *ctx)
{
struct vdec_inst *inst = (struct vdec_inst *)ctx->drv_handle;
struct vdec_ap_ipi_set_param msg;
int err = 0;
mtk_vcodec_debug_enter(inst);
if (!inst)
return;
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_SET_PARAM;
msg.id = SET_PARAM_MMDVFS;
msg.ctx_id = inst->ctx->id;
msg.vcu_inst_addr = inst->vcu.inst_addr;
msg.data[0] = MTK_INST_RESUME;
err = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, false, true);
mtk_vcodec_debug(inst, "- ret=%d", err);
}
static int vcp_vdec_notify_callback(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct mtk_vcodec_dev *dev;
struct list_head *p, *q;
struct mtk_vcodec_ctx *ctx = NULL;
int timeout = 0;
struct vdec_inst *inst = NULL;
int val, wait_cnt, i;
if (!mtk_vcodec_is_vcp(MTK_INST_DECODER))
return 0;
dev = container_of(this, struct mtk_vcodec_dev, vcp_notify);
switch (event) {
case VCP_EVENT_STOP:
while (atomic_read(&dev->mq.cnt)) {
timeout++;
mdelay(1);
if (timeout > VCP_SYNC_TIMEOUT_MS) {
mtk_v4l2_debug(0, "VCP_EVENT_STOP timeout\n");
break;
}
}
if (is_vcp_ready(VCP_A_ID)) {
// vcp ready case STOP from vcp_disable_pm_clk pwclkcnt == 0
vdec_vcp_backup(dev);
} else {
// vcp not ready case STOP from vcp_sys_reset_ws
mutex_lock(&dev->ctx_mutex);
// check release all ctx lock
list_for_each_safe(p, q, &dev->ctx_list) {
ctx = list_entry(p, struct mtk_vcodec_ctx, list);
if (ctx != NULL && ctx->state != MTK_STATE_ABORT) {
inst = (struct vdec_inst *)(ctx->drv_handle);
if (inst != NULL) {
inst->vcu.failure = VDEC_IPI_MSG_STATUS_FAIL;
inst->vcu.abort = 1;
}
mtk_vdec_error_handle(ctx, "STOP");
}
}
mutex_unlock(&dev->ctx_mutex);
dev->codec_stop_done = true;
}
break;
case VCP_EVENT_PRE_SUSPEND:
mutex_lock(&dev->dec_dvfs_mutex);
dev->is_codec_suspending = 1;
mutex_unlock(&dev->dec_dvfs_mutex);
mtk_vcodec_alive_checker_suspend(dev);
break;
case VCP_EVENT_SUSPEND:
// check no more ipi in progress
mutex_lock(&dev->ipi_mutex);
mutex_lock(&dev->ipi_mutex_res);
mutex_unlock(&dev->ipi_mutex_res);
mutex_unlock(&dev->ipi_mutex);
// send backup ipi to vcp by one of any instances
vdec_vcp_backup(dev);
ctx = get_valid_ctx(dev);
if (ctx) {
mtk_v4l2_debug(0, "[%d] backup (dvfs freq %d, high %d)(pw ref %d, %d %d)(hw active %d %d)",
ctx->id, dev->vdec_dvfs_params.target_freq,
dev->vdec_dvfs_params.high_loading_scenario,
atomic_read(&dev->dec_larb_ref_cnt),
atomic_read(&dev->dec_clk_ref_cnt[MTK_VDEC_LAT]),
atomic_read(&dev->dec_clk_ref_cnt[MTK_VDEC_CORE]),
atomic_read(&dev->dec_hw_active[MTK_VDEC_LAT]),
atomic_read(&dev->dec_hw_active[MTK_VDEC_CORE]));
}
mutex_lock(&dev->dec_dvfs_mutex);
// if power always on, put pw ref cnt before suspend
if (mtk_vdec_dvfs_is_pw_always_on(dev))
mtk_vcodec_dec_pw_off(&dev->pm);
dev->dvfs_is_suspend_off = true;
mutex_unlock(&dev->dec_dvfs_mutex);
vdec_suspend_power(dev);
// check all hw lock is released
for (i = 0; i < MTK_VDEC_HW_NUM; i++) {
val = down_trylock(&dev->dec_sem[i]);
for (wait_cnt = 0; val == 1 && wait_cnt < 5; wait_cnt++) {
usleep_range(10000, 20000);
val = down_trylock(&dev->dec_sem[i]);
}
if (val == 1)
mtk_v4l2_err("waiting hw_id %d relase lock fail", i);
else
up(&dev->dec_sem[i]);
}
// wait msg q of ipi_recv all done
while (atomic_read(&dev->mq.cnt)) {
timeout += 20;
usleep_range(10000, 20000);
if (timeout > VCP_SYNC_TIMEOUT_MS) {
mtk_v4l2_err("VCP_EVENT_SUSPEND timeout\n");
break;
}
}
break;
case VCP_EVENT_RESUME:
vdec_resume_power(dev);
ctx = get_valid_ctx(dev);
mtk_vcodec_alive_checker_resume(dev);
mutex_lock(&dev->dec_dvfs_mutex);
// if power always on before suspend, get pw ref cnt after resume
if (mtk_vdec_dvfs_is_pw_always_on(dev))
mtk_vcodec_dec_pw_on(&dev->pm);
if (ctx)
vdec_vcp_mmdvfs_resume(ctx);
dev->dvfs_is_suspend_off = false;
mutex_unlock(&dev->dec_dvfs_mutex);
dev->is_codec_suspending = 0;
break;
}
return NOTIFY_DONE;
}
void vdec_vcp_probe(struct mtk_vcodec_dev *dev)
{
int ret, i;
struct mtk_vcodec_msg_node *mq_node;
mtk_v4l2_debug_enter();
INIT_LIST_HEAD(&dev->mq.head);
spin_lock_init(&dev->mq.lock);
init_waitqueue_head(&dev->mq.wq);
atomic_set(&dev->mq.cnt, 0);
INIT_LIST_HEAD(&dev->mq.nodes);
for (i = 0; i < MTK_VCODEC_MAX_MQ_NODE_CNT; i++) {
mq_node = kmalloc(sizeof(struct mtk_vcodec_msg_node), GFP_DMA | GFP_ATOMIC);
list_add(&mq_node->list, &dev->mq.nodes);
}
if (!VCU_FPTR(vcu_load_firmware))
mtk_vcodec_vcp |= 1 << MTK_INST_DECODER;
ret = mtk_ipi_register(&vcp_ipidev, IPI_IN_VDEC_1,
vdec_vcp_ipi_isr, dev, &dev->dec_ipi_data);
if (ret)
mtk_v4l2_debug(0, " ipi_register, ret %d\n", ret);
kthread_run(vcp_dec_ipi_handler, dev, "vdec_ipi_recv");
dev->vcp_notify.notifier_call = vcp_vdec_notify_callback;
vcp_A_register_notify(&dev->vcp_notify);
mtk_v4l2_debug_leave();
}
void vdec_vcp_remove(struct mtk_vcodec_dev *dev)
{
int timeout = 0;
struct mtk_vcodec_msg_node *mq_node, *next;
unsigned long flags;
while (atomic_read(&dev->mq.cnt)) {
timeout++;
mdelay(1);
if (timeout > VCP_SYNC_TIMEOUT_MS) {
mtk_v4l2_err("wait msgq empty timeout\n");
break;
}
}
spin_lock_irqsave(&dev->mq.lock, flags);
list_for_each_entry_safe(mq_node, next, &dev->mq.nodes, list) {
list_del(&(mq_node->list));
kfree(mq_node);
}
spin_unlock_irqrestore(&dev->mq.lock, flags);
}
static int vdec_vcp_init(struct mtk_vcodec_ctx *ctx, unsigned long *h_vdec)
{
int err = 0;
struct vdec_ap_ipi_init msg;
struct vdec_inst *inst = NULL;
__u32 fourcc;
inst = kzalloc(sizeof(*inst), GFP_KERNEL);
if (!inst)
return -ENOMEM;
if (!ctx) {
err = -ENOMEM;
goto error_free_inst;
}
inst->ctx = ctx;
fourcc = ctx->q_data[MTK_Q_DATA_SRC].fmt->fourcc;
inst->vcu.id = IPI_VDEC_COMMON;
inst->vcu.ctx = ctx;
init_waitqueue_head(&inst->vcu.wq);
init_waitqueue_head(&inst->vcu.wq_res);
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_INIT;
msg.ctx_id = inst->ctx->id;
msg.ap_inst_addr = (unsigned long)&inst->vcu;
inst->vcu.ctx_ipi_lock = kzalloc(sizeof(struct mutex),
GFP_KERNEL);
if (!inst->vcu.ctx_ipi_lock)
goto error_free_inst;
mutex_init(inst->vcu.ctx_ipi_lock);
INIT_LIST_HEAD(&inst->vcu.bufs);
mtk_vcodec_debug(inst, "vdec_inst=%p svp_mode=%d",
&inst->vcu, ctx->dec_params.svp_mode);
*h_vdec = (unsigned long)inst;
inst->vcu.daemon_pid = get_vcp_generation();
mtk_vcodec_add_ctx_list(ctx);
err = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
if (err != 0) {
mtk_vcodec_err(inst, "%s err=%d", __func__, err);
goto error_free_inst_and_list;
}
inst->vsi = (struct vdec_vsi *)inst->vcu.vsi;
inst->vcu.signaled = false;
inst->vcu.signaled_res = false;
ctx->input_driven = inst->vsi->input_driven;
ctx->ipi_blocked = &inst->vsi->ipi_blocked;
*(ctx->ipi_blocked) = 0;
ctx->align_mode = inst->vsi->align_mode;
ctx->wait_align = &inst->vsi->wait_align;
*(ctx->wait_align) = 0;
atomic_set(&ctx->align_type, 0);
ctx->src_cnt = &inst->vsi->src_cnt;
ctx->dst_cnt = &inst->vsi->dst_cnt;
mtk_v4l2_debug(0, "[%d] %c%c%c%c(%d) Decoder Instance >> %p, ap_inst_addr %llx",
ctx->id, fourcc & 0xFF, (fourcc >> 8) & 0xFF,
(fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF,
inst->vcu.id, inst, msg.ap_inst_addr);
return 0;
error_free_inst_and_list:
mtk_vcodec_del_ctx_list(ctx);
error_free_inst:
kfree(inst->vcu.ctx_ipi_lock);
kfree(inst);
*h_vdec = (unsigned long)NULL;
return err;
}
static void vdec_vcp_deinit(unsigned long h_vdec)
{
struct vdec_inst *inst = (struct vdec_inst *)h_vdec;
struct vdec_ap_ipi_cmd msg;
struct vcp_dec_mem_list *tmp = NULL;
struct list_head *p, *q;
struct device *dev = NULL;
int err = 0;
mtk_vcodec_debug_enter(inst);
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_DEINIT;
msg.ctx_id = inst->ctx->id;
msg.vcu_inst_addr = inst->vcu.inst_addr;
err = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
mtk_vcodec_debug(inst, "- ret=%d", err);
mtk_vcodec_del_ctx_list(inst->ctx);
mutex_lock(inst->vcu.ctx_ipi_lock);
list_for_each_safe(p, q, &inst->vcu.bufs) {
tmp = list_entry(p, struct vcp_dec_mem_list, list);
dev = get_dev_by_mem_type(inst, &tmp->mem);
mtk_vcodec_free_mem(&tmp->mem, dev, tmp->attach, tmp->sgt);
mtk_v4l2_debug(0, "[%d] leak free va 0x%llx pa 0x%llx iova 0x%llx len %d type %d",
inst->ctx->id, tmp->mem.va, tmp->mem.pa,
tmp->mem.iova, tmp->mem.len, tmp->mem.type);
list_del(p);
kfree(tmp);
}
mutex_unlock(inst->vcu.ctx_ipi_lock);
mutex_destroy(inst->vcu.ctx_ipi_lock);
kfree(inst->vcu.ctx_ipi_lock);
kfree(inst);
}
int vdec_vcp_reset(struct vdec_inst *inst, enum vdec_reset_type drain_type)
{
struct vdec_ap_ipi_cmd msg;
int err = 0;
mtk_vcodec_debug(inst, "drain_type %d +", drain_type);
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_RESET;
msg.ctx_id = inst->ctx->id;
msg.vcu_inst_addr = inst->vcu.inst_addr;
msg.drain_type = drain_type;
err = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
mtk_vcodec_debug(inst, "- ret=%d", err);
return err;
}
static int vdec_vcp_decode(unsigned long h_vdec, struct mtk_vcodec_mem *bs,
struct vdec_fb *fb, unsigned int *src_chg)
{
int ret = 0;
struct vdec_inst *inst = (struct vdec_inst *)h_vdec;
struct vdec_vcu_inst *vcu = &inst->vcu;
struct vdec_ap_ipi_dec_start msg;
uint64_t vdec_fb_va;
uint64_t fb_dma[VIDEO_MAX_PLANES] = { 0 };
uint32_t num_planes;
unsigned int i = 0;
unsigned int bs_fourcc = inst->ctx->q_data[MTK_Q_DATA_SRC].fmt->fourcc;
unsigned int fm_fourcc = inst->ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc;
unsigned int *errormap_info = &inst->ctx->errormap_info[0];
num_planes = fb ? inst->vsi->dec.fb_num_planes : 0U;
for (i = 0; i < num_planes; i++)
fb_dma[i] = (u64)fb->fb_base[i].dma_addr;
vdec_fb_va = (u64)(uintptr_t)fb;
mtk_vcodec_debug(inst, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p num_planes %d",
inst->num_nalu, fb_dma[0], fb_dma[1], fb, num_planes);
/* bs == NULL means reset decoder */
if (bs == NULL) {
if (fb == NULL)
return vdec_vcp_reset(inst, VDEC_FLUSH); // flush (0)
else if (fb->status == 0)
return vdec_vcp_reset(inst, VDEC_DRAIN); // drain (1)
else
return vdec_vcp_reset(inst, VDEC_DRAIN_EOS); // drain & return EOS frame (2)
}
mtk_vcodec_debug(inst, "+ BS dma=0x%llx dmabuf=%p format=%c%c%c%c",
(uint64_t)bs->dma_addr, bs->dmabuf, bs_fourcc & 0xFF,
(bs_fourcc >> 8) & 0xFF, (bs_fourcc >> 16) & 0xFF,
(bs_fourcc >> 24) & 0xFF);
inst->vsi->dec.vdec_bs_va = (u64)(uintptr_t)bs;
inst->vsi->dec.bs_dma = (uint64_t)bs->dma_addr;
for (i = 0; i < num_planes; i++)
inst->vsi->dec.fb_dma[i] = fb_dma[i];
if (inst->vsi->input_driven == NON_INPUT_DRIVEN) {
inst->vsi->dec.vdec_fb_va = (u64)(uintptr_t)NULL;
inst->vsi->dec.index = 0xFF;
}
if (fb != NULL) {
inst->vsi->dec.vdec_fb_va = vdec_fb_va;
inst->vsi->dec.index = fb->index;
if (fb->dma_general_buf != 0) {
inst->vsi->general_buf_fd = fb->general_buf_fd;
inst->vsi->general_buf_size = fb->dma_general_buf->size;
inst->vsi->general_buf_dma = fb->dma_general_addr;
mtk_vcodec_debug(inst, "dma_general_buf dma_buf=%p fd=%d dma=%llx size=%lu",
fb->dma_general_buf, inst->vsi->general_buf_fd,
inst->vsi->general_buf_dma,
fb->dma_general_buf->size);
} else {
fb->general_buf_fd = -1;
inst->vsi->general_buf_fd = -1;
inst->vsi->general_buf_size = 0;
mtk_vcodec_debug(inst, "no general buf dmabuf");
}
}
inst->vsi->dec.queued_frame_buf_count =
inst->ctx->dec_params.queued_frame_buf_count;
inst->vsi->dec.timestamp =
inst->ctx->dec_params.timestamp;
mtk_vcodec_debug(inst, "+ FB y_fd=%llx c_fd=%llx BS fd=%llx format=%c%c%c%c",
inst->vsi->dec.fb_fd[0], inst->vsi->dec.fb_fd[1],
inst->vsi->dec.bs_fd, fm_fourcc & 0xFF,
(fm_fourcc >> 8) & 0xFF, (fm_fourcc >> 16) & 0xFF,
(fm_fourcc >> 24) & 0xFF);
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_START;
msg.ctx_id = inst->ctx->id;
msg.vcu_inst_addr = vcu->inst_addr;
msg.data[0] = (unsigned int)bs->size;
msg.data[1] = (unsigned int)bs->length;
msg.data[2] = (unsigned int)bs->flags;
ret = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
*src_chg = inst->vsi->dec.vdec_changed_info;
*(errormap_info + bs->index % VB2_MAX_FRAME) =
inst->vsi->dec.error_map;
if ((*src_chg & VDEC_NEED_SEQ_HEADER) != 0U)
mtk_vcodec_debug(inst, "- need first seq header -");
else if ((*src_chg & VDEC_RES_CHANGE) != 0U)
mtk_vcodec_debug(inst, "- resolution changed -");
else if ((*src_chg & VDEC_HW_NOT_SUPPORT) != 0U)
mtk_vcodec_err(inst, "- unsupported -");
/*ack timeout means vpud has crashed*/
if (ret != IPI_ACTION_DONE) {
mtk_vcodec_err(inst, "- IPI msg ack fail %d -", ret);
*src_chg = *src_chg | VDEC_HW_NOT_SUPPORT;
}
if (ret < 0 || ((*src_chg & VDEC_HW_NOT_SUPPORT) != 0U)
|| ((*src_chg & VDEC_NEED_SEQ_HEADER) != 0U))
goto err_free_fb_out;
inst->ctx->input_driven = inst->vsi->input_driven;
inst->ctx->align_mode = inst->vsi->align_mode > 0;
inst->num_nalu++;
return ret;
err_free_fb_out:
put_fb_to_free(inst, fb);
mtk_vcodec_debug(inst, "\n - NALU[%d] err=%d -\n", inst->num_nalu, ret);
return ret;
}
void set_vdec_vcp_data(struct vdec_inst *inst, enum vcp_reserve_mem_id_t id, void *string)
{
struct vdec_ap_ipi_set_param msg;
void *string_va = (void *)(__u64)vcp_get_reserve_mem_virt(id);
void *string_pa = (void *)(__u64)vcp_get_reserve_mem_phys(id);
__u64 mem_size = (__u64)vcp_get_reserve_mem_size(id);
int string_len = strlen((char *)string);
mtk_vcodec_debug(inst, "mem_size 0x%llx, string_va 0x%llx, string_pa 0x%llx\n",
mem_size, string_va, string_pa);
mtk_vcodec_debug(inst, "string: %s\n", (char *)string);
mtk_vcodec_debug(inst, "string_len:%d\n", string_len);
if (string_len <= (mem_size-1))
memcpy(string_va, (char *)string, string_len + 1);
inst->vcu.ctx = inst->ctx;
inst->vcu.id = (inst->vcu.id == IPI_VCU_INIT) ? IPI_VDEC_COMMON : inst->vcu.id;
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_SET_PARAM;
if (id == VDEC_SET_PROP_MEM_ID)
msg.id = SET_PARAM_VDEC_PROPERTY;
else if (id == VDEC_VCP_LOG_INFO_ID)
msg.id = SET_PARAM_VDEC_VCP_LOG_INFO;
else
mtk_vcodec_err(inst, "unknown id (%d)", msg.id);
msg.ctx_id = inst->ctx->id;
msg.vcu_inst_addr = (uintptr_t)&inst->vcu;
msg.data[0] = (__u32)((__u64)string_pa & 0xFFFFFFFF);
msg.data[1] = (__u32)((__u64)string_pa >> 32);
inst->vcu.daemon_pid = get_vcp_generation();
mtk_vcodec_debug(inst, "msg.id %d msg.data[0]:0x%08x, msg.data[1]:0x%08x\n",
msg.id, msg.data[0], msg.data[1]);
}
int vdec_vcp_set_frame_buffer(struct vdec_inst *inst, void *fb)
{
int err = 0;
struct vdec_ap_ipi_set_param msg;
struct mtk_video_dec_buf *dst_buf_info = fb;
struct vdec_ipi_fb ipi_fb;
struct vdec_fb *pfb = NULL;
bool dst_not_get = true;
mtk_vcodec_debug(inst, "+ id=%X", AP_IPIMSG_DEC_FRAME_BUFFER);
memset(&msg, 0, sizeof(msg));
memset(&ipi_fb, 0, sizeof(ipi_fb));
msg.msg_id = AP_IPIMSG_DEC_FRAME_BUFFER;
msg.ctx_id = inst->ctx->id;
msg.id = 0;
msg.vcu_inst_addr = inst->vcu.inst_addr;
do {
if (fb == NULL) {
mtk_vcodec_debug(inst, "send flush");
} else {
pfb = mtk_vcodec_get_fb(inst->ctx);
if (pfb == &dst_buf_info->frame_buffer)
dst_not_get = false;
ipi_fb.vdec_fb_va = (u64)pfb;
}
if (pfb != NULL) {
ipi_fb.reserved = pfb->index;
ipi_fb.y_fb_dma = (u64)pfb->fb_base[0].dma_addr;
if (pfb->num_planes > 1)
ipi_fb.c_fb_dma = (u64)pfb->fb_base[1].dma_addr;
if (pfb->dma_general_buf != 0) {
ipi_fb.dma_general_addr = pfb->dma_general_addr;
ipi_fb.general_size = pfb->dma_general_buf->size;
mtk_vcodec_debug(inst, "FB id=%d dma_addr (%llx,%llx) dma_general_buf %p size %lu dma %lu",
pfb->index, ipi_fb.y_fb_dma, ipi_fb.c_fb_dma,
pfb->dma_general_buf, pfb->dma_general_buf->size,
pfb->dma_general_addr);
} else {
ipi_fb.dma_general_addr = -1;
mtk_vcodec_debug(inst, "FB id=%d dma_addr (%llx,%llx) dma_general_buf %p no general buf dmabuf",
pfb->index, ipi_fb.y_fb_dma, ipi_fb.c_fb_dma,
pfb->dma_general_buf);
}
}
if (pfb != NULL || fb == NULL) {
memcpy(msg.data, &ipi_fb, sizeof(struct vdec_ipi_fb));
err = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
if (err < 0)
break;
}
} while (pfb != NULL);
if (fb != NULL && dst_not_get) {
mtk_vcodec_debug(inst, "warning: dst_buf_info->frame_buffer id=%d %p %llx not get",
dst_buf_info->frame_buffer.index,
&dst_buf_info->frame_buffer,
(u64)&dst_buf_info->frame_buffer);
}
if (err < 0)
mtk_vcodec_err(inst, "- id=%X ret=%d", AP_IPIMSG_DEC_FRAME_BUFFER, err);
else
mtk_vcodec_debug(inst, "- id=%X ret=%d", AP_IPIMSG_DEC_FRAME_BUFFER, err);
return err;
}
static void vdec_get_bs(struct vdec_inst *inst,
struct ring_bs_list *list,
struct mtk_vcodec_mem **out_bs)
{
unsigned long vdec_bs_va;
struct mtk_vcodec_mem *bs;
if (list->count == 0) {
mtk_vcodec_debug(inst, "[BS] there is no bs");
*out_bs = NULL;
return;
}
vdec_bs_va = (unsigned long)list->vdec_bs_va_list[list->read_idx];
bs = (struct mtk_vcodec_mem *)vdec_bs_va;
*out_bs = bs;
mtk_vcodec_debug(inst, "[BS] get free bs %lx", vdec_bs_va);
list->read_idx = (list->read_idx == DEC_MAX_BS_NUM - 1) ?
0 : list->read_idx + 1;
list->count--;
}
static void vdec_get_fb(struct vdec_inst *inst,
struct ring_fb_list *list,
bool disp_list, struct vdec_fb **out_fb)
{
unsigned long vdec_fb_va;
struct vdec_fb *fb;
if (list->count >= DEC_MAX_FB_NUM) {
mtk_vcodec_err(inst, "list count %d invalid ! (write_idx %d, read_idx %d)",
list->count, list->write_idx, list->read_idx);
if (list->write_idx >= DEC_MAX_FB_NUM || list->read_idx >= DEC_MAX_FB_NUM)
list->write_idx = list->read_idx = 0;
if (list->write_idx >= list->read_idx)
list->count = list->write_idx - list->read_idx;
else
list->count = list->write_idx + DEC_MAX_FB_NUM - list->read_idx;
}
if (list->count == 0) {
mtk_vcodec_debug(inst, "[FB] there is no %s fb",
disp_list ? "disp" : "free");
*out_fb = NULL;
return;
}
vdec_fb_va = (unsigned long)list->fb_list[list->read_idx].vdec_fb_va;
fb = (struct vdec_fb *)vdec_fb_va;
if (fb == NULL)
return;
fb->timestamp = list->fb_list[list->read_idx].timestamp;
if (disp_list) {
fb->status |= FB_ST_DISPLAY;
if (list->fb_list[list->read_idx].reserved)
fb->status |= FB_ST_NO_GENERATED;
} else {
fb->status |= FB_ST_FREE;
if (list->fb_list[list->read_idx].reserved)
fb->status |= FB_ST_EOS;
}
*out_fb = fb;
mtk_vcodec_debug(inst, "[FB] get %s fb st=%x id=%d ts=%llu %llx gbuf fd %d dma %p",
disp_list ? "disp" : "free", fb->status, list->read_idx,
list->fb_list[list->read_idx].timestamp,
list->fb_list[list->read_idx].vdec_fb_va,
fb->general_buf_fd, fb->dma_general_buf);
list->read_idx = (list->read_idx == DEC_MAX_FB_NUM - 1U) ?
0U : list->read_idx + 1U;
list->count--;
}
static int vdec_vcp_set_param(unsigned long h_vdec,
enum vdec_set_param_type type, void *in)
{
struct vdec_ap_ipi_set_param msg;
int ret = 0;
struct vdec_inst *inst = (struct vdec_inst *)h_vdec;
uint64_t size;
unsigned long *param_ptr = (unsigned long *)in;
if (inst == NULL)
return -EINVAL;
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_SET_PARAM;
msg.ctx_id = inst->ctx->id;
msg.id = type;
msg.vcu_inst_addr = inst->vcu.inst_addr;
switch (type) {
case SET_PARAM_FRAME_BUFFER:
ret = vdec_vcp_set_frame_buffer(inst, in);
break;
case SET_PARAM_FRAME_SIZE:
case SET_PARAM_SET_FIXED_MAX_OUTPUT_BUFFER:
msg.data[0] = (__u32)(*param_ptr);
msg.data[1] = (__u32)(*(param_ptr + 1));
vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
break;
case SET_PARAM_DECODE_MODE:
case SET_PARAM_NAL_SIZE_LENGTH:
case SET_PARAM_WAIT_KEY_FRAME:
case SET_PARAM_DECODE_ERROR_HANDLE_MODE:
case SET_PARAM_OPERATING_RATE:
case SET_PARAM_TOTAL_FRAME_BUFQ_COUNT:
msg.data[0] = (__u32)(*param_ptr);
vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
break;
case SET_PARAM_UFO_MODE:
break;
case SET_PARAM_CRC_PATH:
if (inst->vsi == NULL)
return -EINVAL;
size = strlen((char *) *(uintptr_t *)in);
memcpy(inst->vsi->crc_path, (void *) *(uintptr_t *)in, size);
break;
case SET_PARAM_GOLDEN_PATH:
if (inst->vsi == NULL)
return -EINVAL;
size = strlen((char *) *(uintptr_t *)in);
memcpy(inst->vsi->golden_path, (void *) *(uintptr_t *)in, size);
break;
case SET_PARAM_FB_NUM_PLANES:
if (inst->vsi == NULL)
return -EINVAL;
inst->vsi->dec.fb_num_planes = *(unsigned int *)in;
break;
case SET_PARAM_VDEC_PROPERTY:
set_vdec_vcp_data(inst, VDEC_SET_PROP_MEM_ID, in);
break;
case SET_PARAM_VDEC_VCP_LOG_INFO:
set_vdec_vcp_data(inst, VDEC_VCP_LOG_INFO_ID, in);
break;
case SET_PARAM_MMDVFS:
mtk_v4l2_debug(4, "[VDVFS][VDEC] start to set_param in vcp_if");
msg.data[0] = (__u32)(*param_ptr);
mtk_v4l2_debug(4, "[VDVFS][VDEC] msg data: %u", msg.data[0]);
ret = vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, true);
break;
default:
mtk_vcodec_err(inst, "invalid set parameter type=%d\n", type);
ret = -EINVAL;
break;
}
return ret;
}
// TODO: VSI touch common code shared with vcu
static void get_supported_format(struct vdec_inst *inst,
struct mtk_video_fmt *video_fmt)
{
unsigned int i = 0;
struct vdec_ap_ipi_query_cap msg;
inst->vcu.ctx = inst->ctx;
inst->vcu.id = (inst->vcu.id == IPI_VCU_INIT) ? IPI_VDEC_COMMON : inst->vcu.id;
init_waitqueue_head(&inst->vcu.wq);
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_QUERY_CAP;
msg.id = GET_PARAM_VDEC_CAP_SUPPORTED_FORMATS;
msg.ctx_id = inst->ctx->id;
msg.ap_inst_addr = (uintptr_t)&inst->vcu;
msg.ap_data_addr = (uintptr_t)video_fmt;
inst->vcu.daemon_pid = get_vcp_generation();
vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
for (i = 0; i < MTK_MAX_DEC_CODECS_SUPPORT; i++) {
if (video_fmt[i].fourcc != 0) {
mtk_vcodec_debug(inst, "video_formats[%d] fourcc %d type %d num_planes %d\n",
i, video_fmt[i].fourcc, video_fmt[i].type,
video_fmt[i].num_planes);
}
}
}
static void get_frame_sizes(struct vdec_inst *inst,
struct mtk_codec_framesizes *codec_framesizes)
{
unsigned int i = 0;
struct vdec_ap_ipi_query_cap msg;
inst->vcu.ctx = inst->ctx;
inst->vcu.id = (inst->vcu.id == IPI_VCU_INIT) ? IPI_VDEC_COMMON : inst->vcu.id;
init_waitqueue_head(&inst->vcu.wq);
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_QUERY_CAP;
msg.id = GET_PARAM_VDEC_CAP_FRAME_SIZES;
msg.ctx_id = inst->ctx->id;
msg.ap_inst_addr = (uintptr_t)&inst->vcu;
msg.ap_data_addr = (uintptr_t)codec_framesizes;
inst->vcu.daemon_pid = get_vcp_generation();
vdec_vcp_ipi_send(inst, &msg, sizeof(msg), false, true, false);
for (i = 0; i < MTK_MAX_DEC_CODECS_SUPPORT; i++) {
if (codec_framesizes[i].fourcc != 0) {
mtk_vcodec_debug(inst,
"codec_fs[%d] fourcc %d s %d %d %d %d %d %d P %d L %d\n",
i, codec_framesizes[i].fourcc,
codec_framesizes[i].stepwise.min_width,
codec_framesizes[i].stepwise.max_width,
codec_framesizes[i].stepwise.step_width,
codec_framesizes[i].stepwise.min_height,
codec_framesizes[i].stepwise.max_height,
codec_framesizes[i].stepwise.step_height,
codec_framesizes[i].profile,
codec_framesizes[i].level);
}
}
}
static void get_color_desc(struct vdec_inst *inst,
struct mtk_color_desc *color_desc)
{
inst->vcu.ctx = inst->ctx;
memcpy(color_desc, &inst->vsi->color_desc, sizeof(*color_desc));
}
static void get_aspect_ratio(struct vdec_inst *inst, unsigned int *aspect_ratio)
{
if (inst->vsi == NULL)
return;
inst->vcu.ctx = inst->ctx;
*aspect_ratio = inst->vsi->aspect_ratio;
}
static void get_supported_fix_buffers(struct vdec_inst *inst,
unsigned int *supported)
{
inst->vcu.ctx = inst->ctx;
if (inst->vsi != NULL)
*supported = inst->vsi->fix_buffers;
}
static void get_supported_fix_buffers_svp(struct vdec_inst *inst,
unsigned int *supported)
{
inst->vcu.ctx = inst->ctx;
if (inst->vsi != NULL)
*supported = inst->vsi->fix_buffers_svp;
}
static void get_interlacing(struct vdec_inst *inst,
unsigned int *interlacing)
{
inst->vcu.ctx = inst->ctx;
if (inst->vsi != NULL)
*interlacing = inst->vsi->interlacing;
}
static void get_codec_type(struct vdec_inst *inst,
unsigned int *codec_type)
{
inst->vcu.ctx = inst->ctx;
if (inst->vsi != NULL)
*codec_type = inst->vsi->codec_type;
}
static void get_input_driven(struct vdec_inst *inst,
unsigned int *input_driven)
{
inst->vcu.ctx = inst->ctx;
if (inst->vsi != NULL)
*input_driven = inst->vsi->input_driven;
}
static void get_align_mode(struct vdec_inst *inst,
unsigned int *align_mode)
{
inst->vcu.ctx = inst->ctx;
if (inst->vsi != NULL)
*align_mode = inst->vsi->align_mode;
}
static int vdec_vcp_get_param(unsigned long h_vdec,
enum vdec_get_param_type type, void *out)
{
int ret = 0;
struct vdec_inst *inst = (struct vdec_inst *)h_vdec;
if (inst == NULL)
return -EINVAL;
switch (type) {
case GET_PARAM_FREE_BITSTREAM_BUFFER:
if (inst->vsi == NULL)
return -EINVAL;
vdec_get_bs(inst, &inst->vsi->list_free_bs, out);
break;
case GET_PARAM_DISP_FRAME_BUFFER:
{
struct vdec_fb *pfb;
if (inst->vsi == NULL)
return -EINVAL;
vdec_get_fb(inst, &inst->vsi->list_disp, true, out);
pfb = *((struct vdec_fb **)out);
if (pfb != NULL) {
if (pfb->general_buf_fd >= 0) {
mtk_vcodec_debug(inst, "free pfb->general_buf_fd:%d pfb->dma_general_buf %p\n",
pfb->general_buf_fd,
pfb->dma_general_buf);
pfb->general_buf_fd = -1;
}
}
break;
}
case GET_PARAM_FREE_FRAME_BUFFER:
{
struct vdec_fb *pfb;
if (inst->vsi == NULL)
return -EINVAL;
vdec_get_fb(inst, &inst->vsi->list_free, false, out);
pfb = *((struct vdec_fb **)out);
if (pfb != NULL) {
if (pfb->general_buf_fd >= 0) {
mtk_vcodec_debug(inst, "free pfb->general_buf_fd:%d pfb->dma_general_buf %p\n",
pfb->general_buf_fd,
pfb->dma_general_buf);
pfb->general_buf_fd = -1;
}
}
break;
}
case GET_PARAM_PIC_INFO:
get_pic_info(inst, out);
break;
case GET_PARAM_DPB_SIZE:
get_dpb_size(inst, out);
break;
case GET_PARAM_CROP_INFO:
get_crop_info(inst, out);
break;
case GET_PARAM_VDEC_CAP_SUPPORTED_FORMATS:
get_supported_format(inst, out);
break;
case GET_PARAM_VDEC_CAP_FRAME_SIZES:
get_frame_sizes(inst, out);
break;
case GET_PARAM_COLOR_DESC:
if (inst->vsi == NULL)
return -EINVAL;
get_color_desc(inst, out);
break;
case GET_PARAM_ASPECT_RATIO:
get_aspect_ratio(inst, out);
break;
case GET_PARAM_PLATFORM_SUPPORTED_FIX_BUFFERS:
get_supported_fix_buffers(inst, out);
break;
case GET_PARAM_PLATFORM_SUPPORTED_FIX_BUFFERS_SVP:
get_supported_fix_buffers_svp(inst, out);
break;
case GET_PARAM_INTERLACING:
get_interlacing(inst, out);
break;
case GET_PARAM_CODEC_TYPE:
get_codec_type(inst, out);
break;
case GET_PARAM_INPUT_DRIVEN:
get_input_driven(inst, out);
break;
case GET_PARAM_ALIGN_MODE:
get_align_mode(inst, out);
break;
default:
mtk_vcodec_err(inst, "invalid get parameter type=%d", type);
ret = -EINVAL;
break;
}
return ret;
}
static struct vdec_common_if vdec_vcp_if = {
vdec_vcp_init,
vdec_vcp_decode,
vdec_vcp_get_param,
vdec_vcp_set_param,
vdec_vcp_deinit,
};
struct vdec_common_if *get_dec_vcp_if(void)
{
return &vdec_vcp_if;
}