988 lines
24 KiB
C
988 lines
24 KiB
C
|
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||
|
/*
|
||
|
* Wave5 series multi-standard codec IP - helper functions
|
||
|
*
|
||
|
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
|
||
|
*/
|
||
|
|
||
|
#include <linux/bug.h>
|
||
|
#include <linux/pm_runtime.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include "wave5-vpuapi.h"
|
||
|
#include "wave5-regdefine.h"
|
||
|
#include "wave5.h"
|
||
|
|
||
|
#define DECODE_ALL_TEMPORAL_LAYERS 0
|
||
|
#define DECODE_ALL_SPATIAL_LAYERS 0
|
||
|
|
||
|
static int wave5_initialize_vpu(struct device *dev, u8 *code, size_t size)
|
||
|
{
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = dev_get_drvdata(dev);
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
if (wave5_vpu_is_init(vpu_dev)) {
|
||
|
wave5_vpu_re_init(dev, (void *)code, size);
|
||
|
ret = -EBUSY;
|
||
|
goto err_out;
|
||
|
}
|
||
|
|
||
|
ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT);
|
||
|
if (ret)
|
||
|
goto err_out;
|
||
|
|
||
|
ret = wave5_vpu_init(dev, (void *)code, size);
|
||
|
|
||
|
err_out:
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size)
|
||
|
{
|
||
|
if (!bitcode || size == 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
return wave5_initialize_vpu(dev, bitcode, size);
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_flush_instance(struct vpu_instance *inst)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
int retry = 0;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&inst->dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
do {
|
||
|
/*
|
||
|
* Repeat the FLUSH command until the firmware reports that the
|
||
|
* VPU isn't running anymore
|
||
|
*/
|
||
|
ret = wave5_vpu_hw_flush_instance(inst);
|
||
|
if (ret < 0 && ret != -EBUSY) {
|
||
|
dev_warn(inst->dev->dev, "Flush of %s instance with id: %d fail: %d\n",
|
||
|
inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id,
|
||
|
ret);
|
||
|
mutex_unlock(&inst->dev->hw_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
if (ret == -EBUSY && retry++ >= MAX_FIRMWARE_CALL_RETRY) {
|
||
|
dev_warn(inst->dev->dev, "Flush of %s instance with id: %d timed out!\n",
|
||
|
inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id);
|
||
|
mutex_unlock(&inst->dev->hw_lock);
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
} while (ret != 0);
|
||
|
mutex_unlock(&inst->dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_get_version_info(struct device *dev, u32 *revision, unsigned int *product_id)
|
||
|
{
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = dev_get_drvdata(dev);
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
if (!wave5_vpu_is_init(vpu_dev)) {
|
||
|
ret = -EINVAL;
|
||
|
goto err_out;
|
||
|
}
|
||
|
|
||
|
if (product_id)
|
||
|
*product_id = vpu_dev->product;
|
||
|
ret = wave5_vpu_get_version(vpu_dev, revision);
|
||
|
|
||
|
err_out:
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int wave5_check_dec_open_param(struct vpu_instance *inst, struct dec_open_param *param)
|
||
|
{
|
||
|
if (inst->id >= MAX_NUM_INSTANCE) {
|
||
|
dev_err(inst->dev->dev, "Too many simultaneous instances: %d (max: %u)\n",
|
||
|
inst->id, MAX_NUM_INSTANCE);
|
||
|
return -EOPNOTSUPP;
|
||
|
}
|
||
|
|
||
|
if (param->bitstream_buffer % 8) {
|
||
|
dev_err(inst->dev->dev,
|
||
|
"Bitstream buffer must be aligned to a multiple of 8\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (param->bitstream_buffer_size % 1024 ||
|
||
|
param->bitstream_buffer_size < MIN_BITSTREAM_BUFFER_SIZE) {
|
||
|
dev_err(inst->dev->dev,
|
||
|
"Bitstream buffer size must be aligned to a multiple of 1024 and have a minimum size of %d\n",
|
||
|
MIN_BITSTREAM_BUFFER_SIZE);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_open(struct vpu_instance *inst, struct dec_open_param *open_param)
|
||
|
{
|
||
|
struct dec_info *p_dec_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
dma_addr_t buffer_addr;
|
||
|
size_t buffer_size;
|
||
|
|
||
|
ret = wave5_check_dec_open_param(inst, open_param);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
if (!wave5_vpu_is_init(vpu_dev)) {
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
p_dec_info = &inst->codec_info->dec_info;
|
||
|
memcpy(&p_dec_info->open_param, open_param, sizeof(struct dec_open_param));
|
||
|
|
||
|
buffer_addr = open_param->bitstream_buffer;
|
||
|
buffer_size = open_param->bitstream_buffer_size;
|
||
|
p_dec_info->stream_wr_ptr = buffer_addr;
|
||
|
p_dec_info->stream_rd_ptr = buffer_addr;
|
||
|
p_dec_info->stream_buf_start_addr = buffer_addr;
|
||
|
p_dec_info->stream_buf_size = buffer_size;
|
||
|
p_dec_info->stream_buf_end_addr = buffer_addr + buffer_size;
|
||
|
p_dec_info->reorder_enable = TRUE;
|
||
|
p_dec_info->temp_id_select_mode = TEMPORAL_ID_MODE_ABSOLUTE;
|
||
|
p_dec_info->target_temp_id = DECODE_ALL_TEMPORAL_LAYERS;
|
||
|
p_dec_info->target_spatial_id = DECODE_ALL_SPATIAL_LAYERS;
|
||
|
|
||
|
ret = wave5_vpu_build_up_dec_param(inst, open_param);
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int reset_auxiliary_buffers(struct vpu_instance *inst, unsigned int index)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
|
||
|
if (index >= MAX_REG_FRAME)
|
||
|
return 1;
|
||
|
|
||
|
if (p_dec_info->vb_mv[index].size == 0 && p_dec_info->vb_fbc_y_tbl[index].size == 0 &&
|
||
|
p_dec_info->vb_fbc_c_tbl[index].size == 0)
|
||
|
return 1;
|
||
|
|
||
|
wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[index]);
|
||
|
wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[index]);
|
||
|
wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[index]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
int ret;
|
||
|
int retry = 0;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
int i;
|
||
|
int inst_count = 0;
|
||
|
struct vpu_instance *inst_elm;
|
||
|
|
||
|
*fail_res = 0;
|
||
|
if (!inst->codec_info)
|
||
|
return -EINVAL;
|
||
|
|
||
|
pm_runtime_resume_and_get(inst->dev->dev);
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret) {
|
||
|
pm_runtime_put_sync(inst->dev->dev);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
ret = wave5_vpu_dec_finish_seq(inst, fail_res);
|
||
|
if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
|
||
|
dev_warn(inst->dev->dev, "dec_finish_seq timed out\n");
|
||
|
goto unlock_and_return;
|
||
|
}
|
||
|
|
||
|
if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
|
||
|
retry++ >= MAX_FIRMWARE_CALL_RETRY) {
|
||
|
ret = -ETIMEDOUT;
|
||
|
goto unlock_and_return;
|
||
|
}
|
||
|
} while (ret != 0);
|
||
|
|
||
|
dev_dbg(inst->dev->dev, "%s: dec_finish_seq complete\n", __func__);
|
||
|
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
|
||
|
|
||
|
for (i = 0 ; i < MAX_REG_FRAME; i++) {
|
||
|
ret = reset_auxiliary_buffers(inst, i);
|
||
|
if (ret) {
|
||
|
ret = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task);
|
||
|
|
||
|
list_for_each_entry(inst_elm, &vpu_dev->instances, list)
|
||
|
inst_count++;
|
||
|
if (inst_count == 1)
|
||
|
pm_runtime_dont_use_autosuspend(vpu_dev->dev);
|
||
|
|
||
|
unlock_and_return:
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
pm_runtime_put_sync(inst->dev->dev);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_issue_seq_init(struct vpu_instance *inst)
|
||
|
{
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = wave5_vpu_dec_init_seq(inst);
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_complete_seq_init(struct vpu_instance *inst, struct dec_initial_info *info)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = wave5_vpu_dec_get_seq_info(inst, info);
|
||
|
if (!ret)
|
||
|
p_dec_info->initial_info_obtained = true;
|
||
|
|
||
|
info->rd_ptr = wave5_dec_get_rd_ptr(inst);
|
||
|
info->wr_ptr = p_dec_info->stream_wr_ptr;
|
||
|
|
||
|
p_dec_info->initial_info = *info;
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_register_frame_buffer_ex(struct vpu_instance *inst, int num_of_decoding_fbs,
|
||
|
int num_of_display_fbs, int stride, int height)
|
||
|
{
|
||
|
struct dec_info *p_dec_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
struct frame_buffer *fb;
|
||
|
|
||
|
if (num_of_decoding_fbs >= WAVE5_MAX_FBS || num_of_display_fbs >= WAVE5_MAX_FBS)
|
||
|
return -EINVAL;
|
||
|
|
||
|
p_dec_info = &inst->codec_info->dec_info;
|
||
|
p_dec_info->num_of_decoding_fbs = num_of_decoding_fbs;
|
||
|
p_dec_info->num_of_display_fbs = num_of_display_fbs;
|
||
|
p_dec_info->stride = stride;
|
||
|
|
||
|
if (!p_dec_info->initial_info_obtained)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (stride < p_dec_info->initial_info.pic_width || (stride % 8 != 0) ||
|
||
|
height < p_dec_info->initial_info.pic_height)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
fb = inst->frame_buf;
|
||
|
ret = wave5_vpu_dec_register_framebuffer(inst, &fb[p_dec_info->num_of_decoding_fbs],
|
||
|
LINEAR_FRAME_MAP, p_dec_info->num_of_display_fbs);
|
||
|
if (ret)
|
||
|
goto err_out;
|
||
|
|
||
|
ret = wave5_vpu_dec_register_framebuffer(inst, &fb[0], COMPRESSED_FRAME_MAP,
|
||
|
p_dec_info->num_of_decoding_fbs);
|
||
|
|
||
|
err_out:
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_get_bitstream_buffer(struct vpu_instance *inst, dma_addr_t *prd_ptr,
|
||
|
dma_addr_t *pwr_ptr, size_t *size)
|
||
|
{
|
||
|
struct dec_info *p_dec_info;
|
||
|
dma_addr_t rd_ptr;
|
||
|
dma_addr_t wr_ptr;
|
||
|
int room;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
int ret;
|
||
|
|
||
|
p_dec_info = &inst->codec_info->dec_info;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
rd_ptr = wave5_dec_get_rd_ptr(inst);
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
wr_ptr = p_dec_info->stream_wr_ptr;
|
||
|
|
||
|
if (wr_ptr < rd_ptr)
|
||
|
room = rd_ptr - wr_ptr;
|
||
|
else
|
||
|
room = (p_dec_info->stream_buf_end_addr - wr_ptr) +
|
||
|
(rd_ptr - p_dec_info->stream_buf_start_addr);
|
||
|
room--;
|
||
|
|
||
|
if (prd_ptr)
|
||
|
*prd_ptr = rd_ptr;
|
||
|
if (pwr_ptr)
|
||
|
*pwr_ptr = wr_ptr;
|
||
|
if (size)
|
||
|
*size = room;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_update_bitstream_buffer(struct vpu_instance *inst, size_t size)
|
||
|
{
|
||
|
struct dec_info *p_dec_info;
|
||
|
dma_addr_t wr_ptr;
|
||
|
dma_addr_t rd_ptr;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
if (!inst->codec_info)
|
||
|
return -EINVAL;
|
||
|
|
||
|
p_dec_info = &inst->codec_info->dec_info;
|
||
|
wr_ptr = p_dec_info->stream_wr_ptr;
|
||
|
rd_ptr = p_dec_info->stream_rd_ptr;
|
||
|
|
||
|
if (size > 0) {
|
||
|
if (wr_ptr < rd_ptr && rd_ptr <= wr_ptr + size)
|
||
|
return -EINVAL;
|
||
|
|
||
|
wr_ptr += size;
|
||
|
|
||
|
if (wr_ptr > p_dec_info->stream_buf_end_addr) {
|
||
|
u32 room = wr_ptr - p_dec_info->stream_buf_end_addr;
|
||
|
|
||
|
wr_ptr = p_dec_info->stream_buf_start_addr;
|
||
|
wr_ptr += room;
|
||
|
} else if (wr_ptr == p_dec_info->stream_buf_end_addr) {
|
||
|
wr_ptr = p_dec_info->stream_buf_start_addr;
|
||
|
}
|
||
|
|
||
|
p_dec_info->stream_wr_ptr = wr_ptr;
|
||
|
p_dec_info->stream_rd_ptr = rd_ptr;
|
||
|
}
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = wave5_vpu_dec_set_bitstream_flag(inst, (size == 0));
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_start_one_frame(struct vpu_instance *inst, u32 *res_fail)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
if (p_dec_info->stride == 0) /* this means frame buffers have not been registered. */
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = wave5_vpu_decode(inst, res_fail);
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int update_wr_ptr)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = wave5_dec_set_rd_ptr(inst, addr);
|
||
|
|
||
|
p_dec_info->stream_rd_ptr = addr;
|
||
|
if (update_wr_ptr)
|
||
|
p_dec_info->stream_wr_ptr = addr;
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst)
|
||
|
{
|
||
|
int ret;
|
||
|
dma_addr_t rd_ptr;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&inst->dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
rd_ptr = wave5_dec_get_rd_ptr(inst);
|
||
|
|
||
|
mutex_unlock(&inst->dev->hw_lock);
|
||
|
|
||
|
return rd_ptr;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_info *info)
|
||
|
{
|
||
|
struct dec_info *p_dec_info;
|
||
|
int ret;
|
||
|
struct vpu_rect rect_info;
|
||
|
u32 val;
|
||
|
u32 decoded_index;
|
||
|
u32 disp_idx;
|
||
|
u32 max_dec_index;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
struct dec_output_info *disp_info;
|
||
|
|
||
|
if (!info)
|
||
|
return -EINVAL;
|
||
|
|
||
|
p_dec_info = &inst->codec_info->dec_info;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
memset(info, 0, sizeof(*info));
|
||
|
|
||
|
ret = wave5_vpu_dec_get_result(inst, info);
|
||
|
if (ret) {
|
||
|
info->rd_ptr = p_dec_info->stream_rd_ptr;
|
||
|
info->wr_ptr = p_dec_info->stream_wr_ptr;
|
||
|
goto err_out;
|
||
|
}
|
||
|
|
||
|
decoded_index = info->index_frame_decoded;
|
||
|
|
||
|
/* calculate display frame region */
|
||
|
val = 0;
|
||
|
rect_info.left = 0;
|
||
|
rect_info.right = 0;
|
||
|
rect_info.top = 0;
|
||
|
rect_info.bottom = 0;
|
||
|
|
||
|
if (decoded_index < WAVE5_MAX_FBS) {
|
||
|
if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC)
|
||
|
rect_info = p_dec_info->initial_info.pic_crop_rect;
|
||
|
|
||
|
if (inst->std == W_HEVC_DEC)
|
||
|
p_dec_info->dec_out_info[decoded_index].decoded_poc = info->decoded_poc;
|
||
|
|
||
|
p_dec_info->dec_out_info[decoded_index].rc_decoded = rect_info;
|
||
|
}
|
||
|
info->rc_decoded = rect_info;
|
||
|
|
||
|
disp_idx = info->index_frame_display;
|
||
|
if (info->index_frame_display >= 0 && info->index_frame_display < WAVE5_MAX_FBS) {
|
||
|
disp_info = &p_dec_info->dec_out_info[disp_idx];
|
||
|
if (info->index_frame_display != info->index_frame_decoded) {
|
||
|
/*
|
||
|
* when index_frame_decoded < 0, and index_frame_display >= 0
|
||
|
* info->dec_pic_width and info->dec_pic_height are still valid
|
||
|
* but those of p_dec_info->dec_out_info[disp_idx] are invalid in VP9
|
||
|
*/
|
||
|
info->disp_pic_width = disp_info->dec_pic_width;
|
||
|
info->disp_pic_height = disp_info->dec_pic_height;
|
||
|
} else {
|
||
|
info->disp_pic_width = info->dec_pic_width;
|
||
|
info->disp_pic_height = info->dec_pic_height;
|
||
|
}
|
||
|
|
||
|
info->rc_display = disp_info->rc_decoded;
|
||
|
|
||
|
} else {
|
||
|
info->rc_display.left = 0;
|
||
|
info->rc_display.right = 0;
|
||
|
info->rc_display.top = 0;
|
||
|
info->rc_display.bottom = 0;
|
||
|
info->disp_pic_width = 0;
|
||
|
info->disp_pic_height = 0;
|
||
|
}
|
||
|
|
||
|
p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst);
|
||
|
p_dec_info->frame_display_flag = vpu_read_reg(vpu_dev, W5_RET_DEC_DISP_IDC);
|
||
|
|
||
|
val = p_dec_info->num_of_decoding_fbs; //fb_offset
|
||
|
|
||
|
max_dec_index = (p_dec_info->num_of_decoding_fbs > p_dec_info->num_of_display_fbs) ?
|
||
|
p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs;
|
||
|
|
||
|
if (info->index_frame_display >= 0 &&
|
||
|
info->index_frame_display < (int)max_dec_index)
|
||
|
info->disp_frame = inst->frame_buf[val + info->index_frame_display];
|
||
|
|
||
|
info->rd_ptr = p_dec_info->stream_rd_ptr;
|
||
|
info->wr_ptr = p_dec_info->stream_wr_ptr;
|
||
|
info->frame_display_flag = p_dec_info->frame_display_flag;
|
||
|
|
||
|
info->sequence_no = p_dec_info->initial_info.sequence_no;
|
||
|
if (decoded_index < WAVE5_MAX_FBS)
|
||
|
p_dec_info->dec_out_info[decoded_index] = *info;
|
||
|
|
||
|
if (disp_idx < WAVE5_MAX_FBS)
|
||
|
info->disp_frame.sequence_no = info->sequence_no;
|
||
|
|
||
|
if (info->sequence_changed) {
|
||
|
memcpy((void *)&p_dec_info->initial_info, (void *)&p_dec_info->new_seq_info,
|
||
|
sizeof(struct dec_initial_info));
|
||
|
p_dec_info->initial_info.sequence_no++;
|
||
|
}
|
||
|
|
||
|
err_out:
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_clr_disp_flag(struct vpu_instance *inst, int index)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
if (index >= p_dec_info->num_of_display_fbs)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = wave5_dec_clr_disp_flag(inst, index);
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_set_disp_flag(struct vpu_instance *inst, int index)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
int ret = 0;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
if (index >= p_dec_info->num_of_display_fbs)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
ret = wave5_dec_set_disp_flag(inst, index);
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_reset_framebuffer(struct vpu_instance *inst, unsigned int index)
|
||
|
{
|
||
|
if (index >= MAX_REG_FRAME)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (inst->frame_vbuf[index].size == 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
wave5_vdi_free_dma_memory(inst->dev, &inst->frame_vbuf[index]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_dec_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter)
|
||
|
{
|
||
|
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
|
||
|
int ret = 0;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case DEC_GET_QUEUE_STATUS: {
|
||
|
struct queue_status_info *queue_info = parameter;
|
||
|
|
||
|
queue_info->instance_queue_count = p_dec_info->instance_queue_count;
|
||
|
queue_info->report_queue_count = p_dec_info->report_queue_count;
|
||
|
break;
|
||
|
}
|
||
|
case DEC_RESET_FRAMEBUF_INFO: {
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < MAX_REG_FRAME; i++) {
|
||
|
ret = wave5_vpu_dec_reset_framebuffer(inst, i);
|
||
|
if (ret)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < MAX_REG_FRAME; i++) {
|
||
|
ret = reset_auxiliary_buffers(inst, i);
|
||
|
if (ret)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
|
||
|
break;
|
||
|
}
|
||
|
case DEC_GET_SEQ_INFO: {
|
||
|
struct dec_initial_info *seq_info = parameter;
|
||
|
|
||
|
*seq_info = p_dec_info->initial_info;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_open(struct vpu_instance *inst, struct enc_open_param *open_param)
|
||
|
{
|
||
|
struct enc_info *p_enc_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
ret = wave5_vpu_enc_check_open_param(inst, open_param);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
if (!wave5_vpu_is_init(vpu_dev)) {
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
p_enc_info = &inst->codec_info->enc_info;
|
||
|
p_enc_info->open_param = *open_param;
|
||
|
|
||
|
ret = wave5_vpu_build_up_enc_param(vpu_dev->dev, inst, open_param);
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
|
||
|
{
|
||
|
struct enc_info *p_enc_info = &inst->codec_info->enc_info;
|
||
|
int ret;
|
||
|
int retry = 0;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
int inst_count = 0;
|
||
|
struct vpu_instance *inst_elm;
|
||
|
|
||
|
*fail_res = 0;
|
||
|
if (!inst->codec_info)
|
||
|
return -EINVAL;
|
||
|
|
||
|
pm_runtime_resume_and_get(inst->dev->dev);
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret) {
|
||
|
pm_runtime_resume_and_get(inst->dev->dev);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
ret = wave5_vpu_enc_finish_seq(inst, fail_res);
|
||
|
if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
|
||
|
dev_warn(inst->dev->dev, "enc_finish_seq timed out\n");
|
||
|
pm_runtime_resume_and_get(inst->dev->dev);
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
|
||
|
retry++ >= MAX_FIRMWARE_CALL_RETRY) {
|
||
|
pm_runtime_resume_and_get(inst->dev->dev);
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
} while (ret != 0);
|
||
|
|
||
|
dev_dbg(inst->dev->dev, "%s: enc_finish_seq complete\n", __func__);
|
||
|
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work);
|
||
|
|
||
|
if (inst->std == W_HEVC_ENC || inst->std == W_AVC_ENC) {
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_sub_sam_buf);
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_mv);
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_y_tbl);
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_fbc_c_tbl);
|
||
|
}
|
||
|
|
||
|
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task);
|
||
|
|
||
|
list_for_each_entry(inst_elm, &vpu_dev->instances, list)
|
||
|
inst_count++;
|
||
|
if (inst_count == 1)
|
||
|
pm_runtime_dont_use_autosuspend(vpu_dev->dev);
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
pm_runtime_put_sync(inst->dev->dev);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_register_frame_buffer(struct vpu_instance *inst, unsigned int num,
|
||
|
unsigned int stride, int height,
|
||
|
enum tiled_map_type map_type)
|
||
|
{
|
||
|
struct enc_info *p_enc_info = &inst->codec_info->enc_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
unsigned int size_luma, size_chroma;
|
||
|
int i;
|
||
|
|
||
|
if (p_enc_info->stride)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (!p_enc_info->initial_info_obtained)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (num < p_enc_info->initial_info.min_frame_buffer_count)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (stride == 0 || stride % 8 != 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (height <= 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
p_enc_info->num_frame_buffers = num;
|
||
|
p_enc_info->stride = stride;
|
||
|
|
||
|
size_luma = stride * height;
|
||
|
size_chroma = ALIGN(stride / 2, 16) * height;
|
||
|
|
||
|
for (i = 0; i < num; i++) {
|
||
|
if (!inst->frame_buf[i].update_fb_info)
|
||
|
continue;
|
||
|
|
||
|
inst->frame_buf[i].update_fb_info = false;
|
||
|
inst->frame_buf[i].stride = stride;
|
||
|
inst->frame_buf[i].height = height;
|
||
|
inst->frame_buf[i].map_type = COMPRESSED_FRAME_MAP;
|
||
|
inst->frame_buf[i].buf_y_size = size_luma;
|
||
|
inst->frame_buf[i].buf_cb = inst->frame_buf[i].buf_y + size_luma;
|
||
|
inst->frame_buf[i].buf_cb_size = size_chroma;
|
||
|
inst->frame_buf[i].buf_cr_size = 0;
|
||
|
}
|
||
|
|
||
|
ret = wave5_vpu_enc_register_framebuffer(inst->dev->dev, inst, &inst->frame_buf[0],
|
||
|
COMPRESSED_FRAME_MAP,
|
||
|
p_enc_info->num_frame_buffers);
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int wave5_check_enc_param(struct vpu_instance *inst, struct enc_param *param)
|
||
|
{
|
||
|
struct enc_info *p_enc_info = &inst->codec_info->enc_info;
|
||
|
|
||
|
if (!param)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (!param->source_frame)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (p_enc_info->open_param.bit_rate == 0 && inst->std == W_HEVC_ENC) {
|
||
|
if (param->pic_stream_buffer_addr % 16 || param->pic_stream_buffer_size == 0)
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (param->pic_stream_buffer_addr % 8 || param->pic_stream_buffer_size == 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_start_one_frame(struct vpu_instance *inst, struct enc_param *param, u32 *fail_res)
|
||
|
{
|
||
|
struct enc_info *p_enc_info = &inst->codec_info->enc_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
*fail_res = 0;
|
||
|
|
||
|
if (p_enc_info->stride == 0) /* this means frame buffers have not been registered. */
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = wave5_check_enc_param(inst, param);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
p_enc_info->pts_map[param->src_idx] = param->pts;
|
||
|
|
||
|
ret = wave5_vpu_encode(inst, param, fail_res);
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_get_output_info(struct vpu_instance *inst, struct enc_output_info *info)
|
||
|
{
|
||
|
struct enc_info *p_enc_info = &inst->codec_info->enc_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = wave5_vpu_enc_get_result(inst, info);
|
||
|
if (ret) {
|
||
|
info->pts = 0;
|
||
|
goto unlock;
|
||
|
}
|
||
|
|
||
|
if (info->recon_frame_index >= 0)
|
||
|
info->pts = p_enc_info->pts_map[info->enc_src_idx];
|
||
|
|
||
|
unlock:
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_give_command(struct vpu_instance *inst, enum codec_command cmd, void *parameter)
|
||
|
{
|
||
|
struct enc_info *p_enc_info = &inst->codec_info->enc_info;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case ENABLE_ROTATION:
|
||
|
p_enc_info->rotation_enable = true;
|
||
|
break;
|
||
|
case ENABLE_MIRRORING:
|
||
|
p_enc_info->mirror_enable = true;
|
||
|
break;
|
||
|
case SET_MIRROR_DIRECTION: {
|
||
|
enum mirror_direction mir_dir;
|
||
|
|
||
|
mir_dir = *(enum mirror_direction *)parameter;
|
||
|
if (mir_dir != MIRDIR_NONE && mir_dir != MIRDIR_HOR &&
|
||
|
mir_dir != MIRDIR_VER && mir_dir != MIRDIR_HOR_VER)
|
||
|
return -EINVAL;
|
||
|
p_enc_info->mirror_direction = mir_dir;
|
||
|
break;
|
||
|
}
|
||
|
case SET_ROTATION_ANGLE: {
|
||
|
int angle;
|
||
|
|
||
|
angle = *(int *)parameter;
|
||
|
if (angle && angle != 90 && angle != 180 && angle != 270)
|
||
|
return -EINVAL;
|
||
|
if (p_enc_info->initial_info_obtained && (angle == 90 || angle == 270))
|
||
|
return -EINVAL;
|
||
|
p_enc_info->rotation_angle = angle;
|
||
|
break;
|
||
|
}
|
||
|
case ENC_GET_QUEUE_STATUS: {
|
||
|
struct queue_status_info *queue_info = parameter;
|
||
|
|
||
|
queue_info->instance_queue_count = p_enc_info->instance_queue_count;
|
||
|
queue_info->report_queue_count = p_enc_info->report_queue_count;
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_issue_seq_init(struct vpu_instance *inst)
|
||
|
{
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = wave5_vpu_enc_init_seq(inst);
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int wave5_vpu_enc_complete_seq_init(struct vpu_instance *inst, struct enc_initial_info *info)
|
||
|
{
|
||
|
struct enc_info *p_enc_info = &inst->codec_info->enc_info;
|
||
|
int ret;
|
||
|
struct vpu_device *vpu_dev = inst->dev;
|
||
|
|
||
|
if (!info)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
ret = wave5_vpu_enc_get_seq_info(inst, info);
|
||
|
if (ret) {
|
||
|
p_enc_info->initial_info_obtained = false;
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
p_enc_info->initial_info_obtained = true;
|
||
|
p_enc_info->initial_info = *info;
|
||
|
|
||
|
mutex_unlock(&vpu_dev->hw_lock);
|
||
|
|
||
|
return 0;
|
||
|
}
|