Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  wave5-hw.c   Sprache: C

 
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Wave5 series multi-standard codec IP - wave5 backend logic
 *
 * Copyright (C) 2021-2023 CHIPS&MEDIA INC
 */


#include <linux/iopoll.h>
#include <linux/bitfield.h>
#include "wave5-vpu.h"
#include "wave5.h"
#include "wave5-regdefine.h"

#define FIO_TIMEOUT   10000000
#define FIO_CTRL_READY   BIT(31)
#define FIO_CTRL_WRITE   BIT(16)
#define VPU_BUSY_CHECK_TIMEOUT  10000000
#define QUEUE_REPORT_MASK  0xffff

/* Encoder support fields */
#define W521_FEATURE_HEVC10BIT_ENC BIT(3)
#define W521_FEATURE_AVC10BIT_ENC BIT(11)
#define W521_FEATURE_AVC_ENCODER BIT(1)
#define W521_FEATURE_HEVC_ENCODER BIT(0)

#define ENC_AVC_INTRA_IDR_PARAM_MASK 0x7ff
#define ENC_AVC_INTRA_PERIOD_SHIFT  6
#define ENC_AVC_IDR_PERIOD_SHIFT  17
#define ENC_AVC_FORCED_IDR_HEADER_SHIFT  28

#define ENC_HEVC_INTRA_QP_SHIFT   3
#define ENC_HEVC_FORCED_IDR_HEADER_SHIFT 9
#define ENC_HEVC_INTRA_PERIOD_SHIFT  16

/* Decoder support fields */
#define W521_FEATURE_AVC_DECODER BIT(3)
#define W521_FEATURE_HEVC_DECODER BIT(2)
#define W515_FEATURE_HEVC10BIT_DEC BIT(1)
#define W515_FEATURE_HEVC_DECODER BIT(0)

#define W521_FEATURE_BACKBONE  BIT(16)
#define W521_FEATURE_VCORE_BACKBONE BIT(22)
#define W521_FEATURE_VCPU_BACKBONE BIT(28)

#define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff)
#define REMAP_CTRL_REGISTER_VALUE(index) (   \
 (BIT(31) | ((index) << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS)\
)

#define FASTIO_ADDRESS_MASK  GENMASK(15, 0)
#define SEQ_PARAM_PROFILE_MASK  GENMASK(30, 24)

static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason,
     const char *func);
#define PRINT_REG_ERR(dev, reason) _wave5_print_reg_err((dev), (reason), __func__)

static inline const char *cmd_to_str(int cmd, bool is_dec)
{
 switch (cmd) {
 case W5_INIT_VPU:
  return "W5_INIT_VPU";
 case W5_WAKEUP_VPU:
  return "W5_WAKEUP_VPU";
 case W5_SLEEP_VPU:
  return "W5_SLEEP_VPU";
 case W5_CREATE_INSTANCE:
  return "W5_CREATE_INSTANCE";
 case W5_FLUSH_INSTANCE:
  return "W5_FLUSH_INSTANCE";
 case W5_DESTROY_INSTANCE:
  return "W5_DESTROY_INSTANCE";
 case W5_INIT_SEQ:
  return "W5_INIT_SEQ";
 case W5_SET_FB:
  return "W5_SET_FB";
 case W5_DEC_ENC_PIC:
  if (is_dec)
   return "W5_DEC_PIC";
  return "W5_ENC_PIC";
 case W5_ENC_SET_PARAM:
  return "W5_ENC_SET_PARAM";
 case W5_QUERY:
  return "W5_QUERY";
 case W5_UPDATE_BS:
  return "W5_UPDATE_BS";
 case W5_MAX_VPU_COMD:
  return "W5_MAX_VPU_COMD";
 default:
  return "UNKNOWN";
 }
}

static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason,
     const char *func)
{
 struct device *dev = vpu_dev->dev;
 u32 reg_val;

 switch (reg_fail_reason) {
 case WAVE5_SYSERR_QUEUEING_FAIL:
  reg_val = vpu_read_reg(vpu_dev, W5_RET_QUEUE_FAIL_REASON);
  dev_dbg(dev, "%s: queueing failure: 0x%x\n", func, reg_val);
  break;
 case WAVE5_SYSERR_RESULT_NOT_READY:
  dev_err(dev, "%s: result not ready: 0x%x\n", func, reg_fail_reason);
  break;
 case WAVE5_SYSERR_ACCESS_VIOLATION_HW:
  dev_err(dev, "%s: access violation: 0x%x\n", func, reg_fail_reason);
  break;
 case WAVE5_SYSERR_WATCHDOG_TIMEOUT:
  dev_err(dev, "%s: watchdog timeout: 0x%x\n", func, reg_fail_reason);
  break;
 case WAVE5_SYSERR_BUS_ERROR:
  dev_err(dev, "%s: bus error: 0x%x\n", func, reg_fail_reason);
  break;
 case WAVE5_SYSERR_DOUBLE_FAULT:
  dev_err(dev, "%s: double fault: 0x%x\n", func, reg_fail_reason);
  break;
 case WAVE5_SYSERR_VPU_STILL_RUNNING:
  dev_err(dev, "%s: still running: 0x%x\n", func, reg_fail_reason);
  break;
 case WAVE5_SYSERR_VLC_BUF_FULL:
  dev_err(dev, "%s: vlc buf full: 0x%x\n", func, reg_fail_reason);
  break;
 default:
  dev_err(dev, "%s: failure:: 0x%x\n", func, reg_fail_reason);
  break;
 }
}

static int wave5_wait_fio_readl(struct vpu_device *vpu_dev, u32 addr, u32 val)
{
 u32 ctrl;
 int ret;

 ctrl = addr & 0xffff;
 wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl);
 ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY,
    0, FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR);
 if (ret)
  return ret;

 if (wave5_vdi_read_register(vpu_dev, W5_VPU_FIO_DATA) != val)
  return -ETIMEDOUT;

 return 0;
}

static void wave5_fio_writel(struct vpu_device *vpu_dev, unsigned int addr, unsigned int data)
{
 int ret;
 unsigned int ctrl;

 wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_DATA, data);
 ctrl = FIELD_GET(FASTIO_ADDRESS_MASK, addr);
 ctrl |= FIO_CTRL_WRITE;
 wave5_vdi_write_register(vpu_dev, W5_VPU_FIO_CTRL_ADDR, ctrl);
 ret = read_poll_timeout(wave5_vdi_read_register, ctrl, ctrl & FIO_CTRL_READY, 0,
    FIO_TIMEOUT, false, vpu_dev, W5_VPU_FIO_CTRL_ADDR);
 if (ret)
  dev_dbg_ratelimited(vpu_dev->dev, "FIO write timeout: addr=0x%x data=%x\n",
        ctrl, data);
}

static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr)
{
 u32 gdi_status_check_value = 0x3f;

 if (vpu_dev->product_code == WAVE515_CODE)
  gdi_status_check_value = 0x0738;
 if (vpu_dev->product_code == WAVE521C_CODE ||
     vpu_dev->product_code == WAVE521_CODE ||
     vpu_dev->product_code == WAVE521E1_CODE)
  gdi_status_check_value = 0x00ff1f3f;

 return wave5_wait_fio_readl(vpu_dev, addr, gdi_status_check_value);
}

static int wave5_wait_vpu_busy(struct vpu_device *vpu_dev, unsigned int addr)
{
 u32 data;

 return read_poll_timeout(wave5_vdi_read_register, data, data == 0,
     0, VPU_BUSY_CHECK_TIMEOUT, false, vpu_dev, addr);
}

static int wave5_wait_vcpu_bus_busy(struct vpu_device *vpu_dev, unsigned int addr)
{
 return wave5_wait_fio_readl(vpu_dev, addr, 0);
}

bool wave5_vpu_is_init(struct vpu_device *vpu_dev)
{
 return vpu_read_reg(vpu_dev, W5_VCPU_CUR_PC) != 0;
}

unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev)
{
 u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER);

 switch (val) {
 case WAVE515_CODE:
  return PRODUCT_ID_515;
 case WAVE521C_CODE:
  return PRODUCT_ID_521;
 case WAVE521_CODE:
 case WAVE521C_DUAL_CODE:
 case WAVE521E1_CODE:
 case WAVE511_CODE:
 case WAVE517_CODE:
 case WAVE537_CODE:
  dev_err(vpu_dev->dev, "Unsupported product id (%x)\n", val);
  break;
 default:
  dev_err(vpu_dev->dev, "Invalid product id (%x)\n", val);
  break;
 }

 return PRODUCT_ID_NONE;
}

static void wave5_bit_issue_command(struct vpu_device *vpu_dev, struct vpu_instance *inst, u32 cmd)
{
 u32 instance_index;
 u32 codec_mode;

 if (inst) {
  instance_index = inst->id;
  codec_mode = inst->std;

  vpu_write_reg(vpu_dev, W5_CMD_INSTANCE_INFO, (codec_mode << 16) |
         (instance_index & 0xffff));
  vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
 }

 vpu_write_reg(vpu_dev, W5_COMMAND, cmd);

 if (inst) {
  dev_dbg(vpu_dev->dev, "%s: cmd=0x%x (%s)\n", __func__, cmd,
   cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC));
 } else {
  dev_dbg(vpu_dev->dev, "%s: cmd=0x%x\n", __func__, cmd);
 }

 vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);
}

static int wave5_vpu_firmware_command_queue_error_check(struct vpu_device *dev, u32 *fail_res)
{
 u32 reason = 0;

 /* Check if we were able to add a command into the VCPU QUEUE */
 if (!vpu_read_reg(dev, W5_RET_SUCCESS)) {
  reason = vpu_read_reg(dev, W5_RET_FAIL_REASON);
  PRINT_REG_ERR(dev, reason);

  /*
 * The fail_res argument will be either NULL or 0.
 * If the fail_res argument is NULL, then just return -EIO.
 * Otherwise, assign the reason to fail_res, so that the
 * calling function can use it.
 */

  if (fail_res)
   *fail_res = reason;
  else
   return -EIO;

  if (reason == WAVE5_SYSERR_VPU_STILL_RUNNING)
   return -EBUSY;
 }
 return 0;
}

static int send_firmware_command(struct vpu_instance *inst, u32 cmd, bool check_success,
     u32 *queue_status, u32 *fail_result)
{
 int ret;

 wave5_bit_issue_command(inst->dev, inst, cmd);
 ret = wave5_wait_vpu_busy(inst->dev, W5_VPU_BUSY_STATUS);
 if (ret) {
  dev_warn(inst->dev->dev, "%s: command: '%s', timed out\n", __func__,
    cmd_to_str(cmd, inst->type == VPU_INST_TYPE_DEC));
  return -ETIMEDOUT;
 }

 if (queue_status)
  *queue_status = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);

 /* In some cases we want to send multiple commands before checking
 * whether they are queued properly
 */

 if (!check_success)
  return 0;

 return wave5_vpu_firmware_command_queue_error_check(inst->dev, fail_result);
}

static int wave5_send_query(struct vpu_device *vpu_dev, struct vpu_instance *inst,
       enum query_opt query_opt)
{
 int ret;

 vpu_write_reg(vpu_dev, W5_QUERY_OPTION, query_opt);
 vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
 wave5_bit_issue_command(vpu_dev, inst, W5_QUERY);

 ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
 if (ret) {
  dev_warn(vpu_dev->dev, "command: 'W5_QUERY', timed out opt=0x%x\n", query_opt);
  return ret;
 }

 return wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
}

static void setup_wave5_interrupts(struct vpu_device *vpu_dev)
{
 u32 reg_val = 0;

 if (vpu_dev->attr.support_encoders) {
  /* Encoder interrupt */
  reg_val |= BIT(INT_WAVE5_ENC_SET_PARAM);
  reg_val |= BIT(INT_WAVE5_ENC_PIC);
  reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
 }

 if (vpu_dev->attr.support_decoders) {
  /* Decoder interrupt */
  reg_val |= BIT(INT_WAVE5_INIT_SEQ);
  reg_val |= BIT(INT_WAVE5_DEC_PIC);
  reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
 }

 return vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
}

static int setup_wave5_properties(struct device *dev)
{
 struct vpu_device *vpu_dev = dev_get_drvdata(dev);
 struct vpu_attr *p_attr = &vpu_dev->attr;
 u32 reg_val;
 u8 *str;
 int ret;
 u32 hw_config_def0, hw_config_def1, hw_config_feature;

 ret = wave5_send_query(vpu_dev, NULL, GET_VPU_INFO);
 if (ret)
  return ret;

 reg_val = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_NAME);
 str = (u8 *)®_val;
 p_attr->product_name[0] = str[3];
 p_attr->product_name[1] = str[2];
 p_attr->product_name[2] = str[1];
 p_attr->product_name[3] = str[0];
 p_attr->product_name[4] = 0;

 p_attr->product_id = wave5_vpu_get_product_id(vpu_dev);
 p_attr->product_version = vpu_read_reg(vpu_dev, W5_RET_PRODUCT_VERSION);
 p_attr->fw_version = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION);
 p_attr->customer_id = vpu_read_reg(vpu_dev, W5_RET_CUSTOMER_ID);
 hw_config_def0 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF0);
 hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1);
 hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE);

 if (vpu_dev->product_code == WAVE515_CODE) {
  p_attr->support_hevc10bit_dec = FIELD_GET(W515_FEATURE_HEVC10BIT_DEC,
         hw_config_feature);
  p_attr->support_decoders = FIELD_GET(W515_FEATURE_HEVC_DECODER,
           hw_config_def1) << STD_HEVC;
 } else {
  p_attr->support_hevc10bit_enc = FIELD_GET(W521_FEATURE_HEVC10BIT_ENC,
         hw_config_feature);
  p_attr->support_avc10bit_enc = FIELD_GET(W521_FEATURE_AVC10BIT_ENC,
        hw_config_feature);

  p_attr->support_decoders = FIELD_GET(W521_FEATURE_AVC_DECODER,
           hw_config_def1) << STD_AVC;
  p_attr->support_decoders |= FIELD_GET(W521_FEATURE_HEVC_DECODER,
            hw_config_def1) << STD_HEVC;
  p_attr->support_encoders = FIELD_GET(W521_FEATURE_AVC_ENCODER,
           hw_config_def1) << STD_AVC;
  p_attr->support_encoders |= FIELD_GET(W521_FEATURE_HEVC_ENCODER,
            hw_config_def1) << STD_HEVC;

  p_attr->support_backbone = FIELD_GET(W521_FEATURE_BACKBONE,
           hw_config_def0);
  p_attr->support_vcpu_backbone = FIELD_GET(W521_FEATURE_VCPU_BACKBONE,
         hw_config_def0);
  p_attr->support_vcore_backbone = FIELD_GET(W521_FEATURE_VCORE_BACKBONE,
          hw_config_def0);
 }

 setup_wave5_interrupts(vpu_dev);

 return 0;
}

int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision)
{
 u32 reg_val;
 int ret;

 ret = wave5_send_query(vpu_dev, NULL, GET_VPU_INFO);
 if (ret)
  return ret;

 reg_val = vpu_read_reg(vpu_dev, W5_RET_FW_VERSION);
 if (revision) {
  *revision = reg_val;
  return 0;
 }

 return -EINVAL;
}

static void remap_page(struct vpu_device *vpu_dev, dma_addr_t code_base, u32 index)
{
 vpu_write_reg(vpu_dev, W5_VPU_REMAP_CTRL, REMAP_CTRL_REGISTER_VALUE(index));
 vpu_write_reg(vpu_dev, W5_VPU_REMAP_VADDR, index * W5_REMAP_MAX_SIZE);
 vpu_write_reg(vpu_dev, W5_VPU_REMAP_PADDR, code_base + index * W5_REMAP_MAX_SIZE);
}

int wave5_vpu_init(struct device *dev, u8 *fw, size_t size)
{
 struct vpu_buf *common_vb;
 dma_addr_t code_base, temp_base;
 u32 code_size, temp_size;
 u32 i, reg_val, reason_code;
 int ret;
 struct vpu_device *vpu_dev = dev_get_drvdata(dev);

 common_vb = &vpu_dev->common_mem;

 code_base = common_vb->daddr;

 if (vpu_dev->product_code == WAVE515_CODE)
  code_size = WAVE515_MAX_CODE_BUF_SIZE;
 else
  code_size = WAVE521_MAX_CODE_BUF_SIZE;

 /* ALIGN TO 4KB */
 code_size &= ~0xfff;
 if (code_size < size * 2)
  return -EINVAL;

 temp_base = code_base + code_size;
 temp_size = WAVE5_TEMPBUF_SIZE;

 ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size);
 if (ret < 0) {
  dev_err(vpu_dev->dev, "VPU init, Writing firmware to common buffer, fail: %d\n",
   ret);
  return ret;
 }

 vpu_write_reg(vpu_dev, W5_PO_CONF, 0);

 /* clear registers */

 for (i = W5_CMD_REG_BASE; i < W5_CMD_REG_END; i += 4)
  vpu_write_reg(vpu_dev, i, 0x00);

 remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
 remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);

 vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
 vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
 vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
 vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base);
 vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size);

 /* These register must be reset explicitly */
 vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);

 if (vpu_dev->product_code != WAVE515_CODE) {
  wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
  wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
  vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
 }

 reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
 if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) {
  reg_val = ((WAVE5_PROC_AXI_ID << 28) |
      (WAVE5_PRP_AXI_ID << 24) |
      (WAVE5_FBD_Y_AXI_ID << 20) |
      (WAVE5_FBC_Y_AXI_ID << 16) |
      (WAVE5_FBD_C_AXI_ID << 12) |
      (WAVE5_FBC_C_AXI_ID << 8) |
      (WAVE5_PRI_AXI_ID << 4) |
      WAVE5_SEC_AXI_ID);
  wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
 }

 if (vpu_dev->product_code == WAVE515_CODE) {
  dma_addr_t task_buf_base;

  vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, WAVE515_COMMAND_QUEUE_DEPTH);
  vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, WAVE515_ONE_TASKBUF_SIZE);

  for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) {
   task_buf_base = temp_base + temp_size +
     (i * WAVE515_ONE_TASKBUF_SIZE);
   vpu_write_reg(vpu_dev,
          W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4),
          task_buf_base);
  }

  vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
  vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
 }

 vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
 vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
 vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
 ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
 if (ret) {
  dev_err(vpu_dev->dev, "VPU init(W5_VPU_REMAP_CORE_START) timeout\n");
  return ret;
 }

 ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
 if (ret)
  return ret;

 return setup_wave5_properties(dev);
}

int wave5_vpu_build_up_dec_param(struct vpu_instance *inst,
     struct dec_open_param *param)
{
 int ret;
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 struct vpu_device *vpu_dev = inst->dev;

 p_dec_info->cycle_per_tick = 256;
 if (vpu_dev->sram_buf.size) {
  p_dec_info->sec_axi_info.use_bit_enable = 1;
  p_dec_info->sec_axi_info.use_ip_enable = 1;
  p_dec_info->sec_axi_info.use_lf_row_enable = 1;
 }
 switch (inst->std) {
 case W_HEVC_DEC:
  p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_HEVC;
  break;
 case W_AVC_DEC:
  p_dec_info->seq_change_mask = SEQ_CHANGE_ENABLE_ALL_AVC;
  break;
 default:
  return -EINVAL;
 }

 if (vpu_dev->product == PRODUCT_ID_515)
  p_dec_info->vb_work.size = WAVE515DEC_WORKBUF_SIZE;
 else
  p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE;

 ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work);
 if (ret)
  return ret;

 if (inst->dev->product_code != WAVE515_CODE)
  vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1);

 wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work);

 vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr);
 vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size);

 if (inst->dev->product_code != WAVE515_CODE) {
  vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
  vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
 }

 vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr);
 vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size);

 /* NOTE: SDMA reads MSB first */
 vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN);

 if (inst->dev->product_code != WAVE515_CODE) {
  /* This register must be reset explicitly */
  vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
  vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1,
         WAVE521_COMMAND_QUEUE_DEPTH - 1);
 }
 vpu_write_reg(inst->dev, W5_CMD_ERR_CONCEAL, 0);
 ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
 if (ret) {
  wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
  return ret;
 }

 p_dec_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER);

 return 0;
}

int wave5_vpu_hw_flush_instance(struct vpu_instance *inst)
{
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 u32 instance_queue_count, report_queue_count;
 u32 reg_val = 0;
 u32 fail_res = 0;
 int ret;

 ret = send_firmware_command(inst, W5_FLUSH_INSTANCE, true, ®_val, &fail_res);
 if (ret)
  return ret;

 instance_queue_count = (reg_val >> 16) & 0xff;
 report_queue_count = (reg_val & QUEUE_REPORT_MASK);
 if (instance_queue_count != 0 || report_queue_count != 0) {
  dev_warn(inst->dev->dev,
    "FLUSH_INSTANCE cmd didn't reset the amount of queued commands & reports");
 }

 /* reset our local copy of the counts */
 p_dec_info->instance_queue_count = 0;
 p_dec_info->report_queue_count = 0;

 return 0;
}

static u32 get_bitstream_options(struct dec_info *info)
{
 u32 bs_option = BSOPTION_ENABLE_EXPLICIT_END;

 if (info->stream_endflag)
  bs_option |= BSOPTION_HIGHLIGHT_STREAM_END;
 return bs_option;
}

int wave5_vpu_dec_init_seq(struct vpu_instance *inst)
{
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 u32 bs_option, cmd_option = INIT_SEQ_NORMAL;
 u32 reg_val, fail_res;
 int ret;

 if (!inst->codec_info)
  return -EINVAL;

 vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr);
 vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);

 bs_option = get_bitstream_options(p_dec_info);

 /* Without RD_PTR_VALID_FLAG Wave515 ignores RD_PTR value */
 if (inst->dev->product_code == WAVE515_CODE)
  bs_option |= BSOPTION_RD_PTR_VALID_FLAG;

 vpu_write_reg(inst->dev, W5_BS_OPTION, bs_option);

 vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option);
 vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable);

 ret = send_firmware_command(inst, W5_INIT_SEQ, true, ®_val, &fail_res);
 if (ret)
  return ret;

 p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
 p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);

 dev_dbg(inst->dev->dev, "%s: init seq sent (queue %u : %u)\n", __func__,
  p_dec_info->instance_queue_count, p_dec_info->report_queue_count);

 return 0;
}

static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initial_info *info)
{
 u32 reg_val;
 u32 profile_compatibility_flag;
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;

 p_dec_info->stream_rd_ptr = wave5_dec_get_rd_ptr(inst);
 info->rd_ptr = p_dec_info->stream_rd_ptr;

 p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC);

 reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE);
 info->pic_width = ((reg_val >> 16) & 0xffff);
 info->pic_height = (reg_val & 0xffff);
 info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_DEC_NUM_REQUIRED_FB);

 reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_LEFT_RIGHT);
 info->pic_crop_rect.left = (reg_val >> 16) & 0xffff;
 info->pic_crop_rect.right = reg_val & 0xffff;
 reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_CROP_TOP_BOTTOM);
 info->pic_crop_rect.top = (reg_val >> 16) & 0xffff;
 info->pic_crop_rect.bottom = reg_val & 0xffff;

 reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_COLOR_SAMPLE_INFO);
 info->luma_bitdepth = reg_val & 0xf;
 info->chroma_bitdepth = (reg_val >> 4) & 0xf;

 reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_SEQ_PARAM);
 profile_compatibility_flag = (reg_val >> 12) & 0xff;
 info->profile = (reg_val >> 24) & 0x1f;

 if (inst->std == W_HEVC_DEC) {
  /* guessing profile */
  if (!info->profile) {
   if ((profile_compatibility_flag & 0x06) == 0x06)
    info->profile = HEVC_PROFILE_MAIN; /* main profile */
   else if (profile_compatibility_flag & 0x04)
    info->profile = HEVC_PROFILE_MAIN10; /* main10 profile */
   else if (profile_compatibility_flag & 0x08)
    /* main still picture profile */
    info->profile = HEVC_PROFILE_STILLPICTURE;
   else
    info->profile = HEVC_PROFILE_MAIN; /* for old version HM */
  }
 } else if (inst->std == W_AVC_DEC) {
  info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val);
 }

 if (inst->dev->product_code != WAVE515_CODE) {
  info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
  info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
  p_dec_info->vlc_buf_size = info->vlc_buf_size;
  p_dec_info->param_buf_size = info->param_buf_size;
 }
}

int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info)
{
 int ret;
 u32 reg_val;
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;

 vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr);
 vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size);
 vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN);

 /* send QUERY cmd */
 ret = wave5_send_query(inst->dev, inst, GET_RESULT);
 if (ret)
  return ret;

 reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);

 p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
 p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);

 dev_dbg(inst->dev->dev, "%s: init seq complete (queue %u : %u)\n", __func__,
  p_dec_info->instance_queue_count, p_dec_info->report_queue_count);

 /* this is not a fatal error, set ret to -EIO but don't return immediately */
 if (vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_SUCCESS) != 1) {
  info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_DEC_ERR_INFO);
  ret = -EIO;
 }

 wave5_get_dec_seq_result(inst, info);

 return ret;
}

int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_buffer *fb_arr,
           enum tiled_map_type map_type, unsigned int count)
{
 int ret;
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 struct dec_initial_info *init_info = &p_dec_info->initial_info;
 size_t remain, idx, j, i, cnt_8_chunk, size;
 u32 start_no, end_no;
 u32 reg_val, cbcr_interleave, nv21, pic_size;
 u32 addr_y, addr_cb, addr_cr;
 u32 mv_col_size, frame_width, frame_height, fbc_y_tbl_size, fbc_c_tbl_size;
 struct vpu_buf vb_buf;
 bool justified = WTL_RIGHT_JUSTIFIED;
 u32 format_no = WTL_PIXEL_8BIT;
 u32 color_format = 0;
 u32 pixel_order = 1;
 u32 bwb_flag = (map_type == LINEAR_FRAME_MAP) ? 1 : 0;

 cbcr_interleave = inst->cbcr_interleave;
 nv21 = inst->nv21;
 mv_col_size = 0;
 fbc_y_tbl_size = 0;
 fbc_c_tbl_size = 0;

 if (map_type >= COMPRESSED_FRAME_MAP) {
  cbcr_interleave = 0;
  nv21 = 0;

  switch (inst->std) {
  case W_HEVC_DEC:
   mv_col_size = WAVE5_DEC_HEVC_BUF_SIZE(init_info->pic_width,
             init_info->pic_height);
   break;
  case W_AVC_DEC:
   mv_col_size = WAVE5_DEC_AVC_BUF_SIZE(init_info->pic_width,
            init_info->pic_height);
   break;
  default:
   return -EINVAL;
  }

  if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) {
   size = ALIGN(ALIGN(mv_col_size, 16), BUFFER_MARGIN) + BUFFER_MARGIN;
   ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_mv, count, size);
   if (ret)
    goto free_mv_buffers;
  }

  frame_width = init_info->pic_width;
  frame_height = init_info->pic_height;
  fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(frame_width, frame_height), 16);
  fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(frame_width, frame_height), 16);

  size = ALIGN(fbc_y_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN;
  ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_y_tbl, count, size);
  if (ret)
   goto free_fbc_y_tbl_buffers;

  size = ALIGN(fbc_c_tbl_size, BUFFER_MARGIN) + BUFFER_MARGIN;
  ret = wave5_vdi_allocate_array(inst->dev, p_dec_info->vb_fbc_c_tbl, count, size);
  if (ret)
   goto free_fbc_c_tbl_buffers;

  pic_size = (init_info->pic_width << 16) | (init_info->pic_height);

  if (inst->dev->product_code != WAVE515_CODE) {
   vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) +
    (p_dec_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH);
   vb_buf.daddr = 0;

   if (vb_buf.size != p_dec_info->vb_task.size) {
    wave5_vdi_free_dma_memory(inst->dev,
         &p_dec_info->vb_task);
    ret = wave5_vdi_allocate_dma_memory(inst->dev,
            &vb_buf);
    if (ret)
     goto free_fbc_c_tbl_buffers;

    p_dec_info->vb_task = vb_buf;
   }

   vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
          p_dec_info->vb_task.daddr);
   vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE,
          vb_buf.size);
  }
 } else {
  pic_size = (init_info->pic_width << 16) | (init_info->pic_height);

  if (inst->output_format == FORMAT_422)
   color_format = 1;
 }
 vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size);

 reg_val = (bwb_flag << 28) |
    (pixel_order << 23) |
    (justified << 22) |
    (format_no << 20) |
    (color_format << 19) |
    (nv21 << 17) |
    (cbcr_interleave << 16) |
    (fb_arr[0].stride);
 vpu_write_reg(inst->dev, W5_COMMON_PIC_INFO, reg_val);

 remain = count;
 cnt_8_chunk = DIV_ROUND_UP(count, 8);
 idx = 0;
 for (j = 0; j < cnt_8_chunk; j++) {
  reg_val = (j == cnt_8_chunk - 1) << 4 | ((j == 0) << 3);
  vpu_write_reg(inst->dev, W5_SFB_OPTION, reg_val);
  start_no = j * 8;
  end_no = start_no + ((remain >= 8) ? 8 : remain) - 1;

  vpu_write_reg(inst->dev, W5_SET_FB_NUM, (start_no << 8) | end_no);

  for (i = 0; i < 8 && i < remain; i++) {
   addr_y = fb_arr[i + start_no].buf_y;
   addr_cb = fb_arr[i + start_no].buf_cb;
   addr_cr = fb_arr[i + start_no].buf_cr;
   vpu_write_reg(inst->dev, W5_ADDR_LUMA_BASE0 + (i << 4), addr_y);
   vpu_write_reg(inst->dev, W5_ADDR_CB_BASE0 + (i << 4), addr_cb);
   if (map_type >= COMPRESSED_FRAME_MAP) {
    /* luma FBC offset table */
    vpu_write_reg(inst->dev, W5_ADDR_FBC_Y_OFFSET0 + (i << 4),
           p_dec_info->vb_fbc_y_tbl[idx].daddr);
    /* chroma FBC offset table */
    vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4),
           p_dec_info->vb_fbc_c_tbl[idx].daddr);
    vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2),
           p_dec_info->vb_mv[idx].daddr);
   } else {
    vpu_write_reg(inst->dev, W5_ADDR_CR_BASE0 + (i << 4), addr_cr);
    vpu_write_reg(inst->dev, W5_ADDR_FBC_C_OFFSET0 + (i << 4), 0);
    vpu_write_reg(inst->dev, W5_ADDR_MV_COL0 + (i << 2), 0);
   }
   idx++;
  }
  remain -= i;

  ret = send_firmware_command(inst, W5_SET_FB, false, NULL, NULL);
  if (ret)
   goto free_buffers;
 }

 reg_val = vpu_read_reg(inst->dev, W5_RET_SUCCESS);
 if (!reg_val) {
  ret = -EIO;
  goto free_buffers;
 }

 return 0;

free_buffers:
 wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
free_fbc_c_tbl_buffers:
 for (i = 0; i < count; i++)
  wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_c_tbl[i]);
free_fbc_y_tbl_buffers:
 for (i = 0; i < count; i++)
  wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_fbc_y_tbl[i]);
free_mv_buffers:
 for (i = 0; i < count; i++)
  wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_mv[i]);
 return ret;
}

static u32 wave5_vpu_dec_validate_sec_axi(struct vpu_instance *inst)
{
 u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth;
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 u32 bit_size = 0, ip_size = 0, lf_size = 0, ret = 0;
 u32 sram_size = inst->dev->sram_size;
 u32 width = inst->src_fmt.width;

 if (!sram_size)
  return 0;

 /*
 * TODO: calculate bit_size, ip_size, lf_size from width and bitdepth
 * for Wave521.
 */

 if (inst->dev->product_code == WAVE515_CODE) {
  bit_size = DIV_ROUND_UP(width, 16) * 5 * 8;
  ip_size = ALIGN(width, 16) * 2 * bitdepth / 8;
  lf_size = ALIGN(width, 16) * 10 * bitdepth / 8;
 }

 if (p_dec_info->sec_axi_info.use_bit_enable && sram_size >= bit_size) {
  ret |= BIT(0);
  sram_size -= bit_size;
 }

 if (p_dec_info->sec_axi_info.use_ip_enable && sram_size >= ip_size) {
  ret |= BIT(9);
  sram_size -= ip_size;
 }

 if (p_dec_info->sec_axi_info.use_lf_row_enable && sram_size >= lf_size)
  ret |= BIT(15);

 return ret;
}

int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res)
{
 u32 reg_val;
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 int ret;

 vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr);
 vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);

 vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));

 /* secondary AXI */
 reg_val = wave5_vpu_dec_validate_sec_axi(inst);
 vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val);

 /* set attributes of user buffer */
 vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable);

 vpu_write_reg(inst->dev, W5_COMMAND_OPTION, DEC_PIC_NORMAL);
 vpu_write_reg(inst->dev, W5_CMD_DEC_TEMPORAL_ID_PLUS1,
        (p_dec_info->target_spatial_id << 9) |
        (p_dec_info->temp_id_select_mode << 8) | p_dec_info->target_temp_id);
 vpu_write_reg(inst->dev, W5_CMD_SEQ_CHANGE_ENABLE_FLAG, p_dec_info->seq_change_mask);
 /* When reordering is disabled we force the latency of the framebuffers */
 vpu_write_reg(inst->dev, W5_CMD_DEC_FORCE_FB_LATENCY_PLUS1, !p_dec_info->reorder_enable);

 ret = send_firmware_command(inst, W5_DEC_ENC_PIC, true, ®_val, fail_res);
 if (ret == -ETIMEDOUT)
  return ret;

 p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
 p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);

 dev_dbg(inst->dev->dev, "%s: dec pic sent (queue %u : %u)\n", __func__,
  p_dec_info->instance_queue_count, p_dec_info->report_queue_count);

 if (ret)
  return ret;

 return 0;
}

int wave5_vpu_dec_get_result(struct vpu_instance *inst, struct dec_output_info *result)
{
 int ret;
 u32 index, nal_unit_type, reg_val, sub_layer_info;
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 struct vpu_device *vpu_dev = inst->dev;

 vpu_write_reg(inst->dev, W5_CMD_DEC_ADDR_REPORT_BASE, p_dec_info->user_data_buf_addr);
 vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_SIZE, p_dec_info->user_data_buf_size);
 vpu_write_reg(inst->dev, W5_CMD_DEC_REPORT_PARAM, REPORT_PARAM_ENDIANNESS_BIG_ENDIAN);

 /* send QUERY cmd */
 ret = wave5_send_query(vpu_dev, inst, GET_RESULT);
 if (ret)
  return ret;

 reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);

 p_dec_info->instance_queue_count = (reg_val >> 16) & 0xff;
 p_dec_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);

 dev_dbg(inst->dev->dev, "%s: dec pic complete (queue %u : %u)\n", __func__,
  p_dec_info->instance_queue_count, p_dec_info->report_queue_count);

 reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_TYPE);

 nal_unit_type = (reg_val >> 4) & 0x3f;

 if (inst->std == W_HEVC_DEC) {
  if (reg_val & 0x04)
   result->pic_type = PIC_TYPE_B;
  else if (reg_val & 0x02)
   result->pic_type = PIC_TYPE_P;
  else if (reg_val & 0x01)
   result->pic_type = PIC_TYPE_I;
  else
   result->pic_type = PIC_TYPE_MAX;
  if ((nal_unit_type == 19 || nal_unit_type == 20) && result->pic_type == PIC_TYPE_I)
   /* IDR_W_RADL, IDR_N_LP */
   result->pic_type = PIC_TYPE_IDR;
 } else if (inst->std == W_AVC_DEC) {
  if (reg_val & 0x04)
   result->pic_type = PIC_TYPE_B;
  else if (reg_val & 0x02)
   result->pic_type = PIC_TYPE_P;
  else if (reg_val & 0x01)
   result->pic_type = PIC_TYPE_I;
  else
   result->pic_type = PIC_TYPE_MAX;
  if (nal_unit_type == 5 && result->pic_type == PIC_TYPE_I)
   result->pic_type = PIC_TYPE_IDR;
 }
 index = vpu_read_reg(inst->dev, W5_RET_DEC_DISPLAY_INDEX);
 result->index_frame_display = index;
 index = vpu_read_reg(inst->dev, W5_RET_DEC_DECODED_INDEX);
 result->index_frame_decoded = index;
 result->index_frame_decoded_for_tiled = index;

 sub_layer_info = vpu_read_reg(inst->dev, W5_RET_DEC_SUB_LAYER_INFO);
 result->temporal_id = sub_layer_info & 0x7;

 if (inst->std == W_HEVC_DEC || inst->std == W_AVC_DEC) {
  result->decoded_poc = -1;
  if (result->index_frame_decoded >= 0 ||
      result->index_frame_decoded == DECODED_IDX_FLAG_SKIP)
   result->decoded_poc = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_POC);
 }

 result->sequence_changed = vpu_read_reg(inst->dev, W5_RET_DEC_NOTIFICATION);
 reg_val = vpu_read_reg(inst->dev, W5_RET_DEC_PIC_SIZE);
 result->dec_pic_width = reg_val >> 16;
 result->dec_pic_height = reg_val & 0xffff;

 if (result->sequence_changed) {
  memcpy((void *)&p_dec_info->new_seq_info, (void *)&p_dec_info->initial_info,
         sizeof(struct dec_initial_info));
  wave5_get_dec_seq_result(inst, &p_dec_info->new_seq_info);
 }

 result->dec_host_cmd_tick = vpu_read_reg(inst->dev, W5_RET_DEC_HOST_CMD_TICK);
 result->dec_decode_end_tick = vpu_read_reg(inst->dev, W5_RET_DEC_DECODING_ENC_TICK);

 if (!p_dec_info->first_cycle_check) {
  result->frame_cycle =
   (result->dec_decode_end_tick - result->dec_host_cmd_tick) *
   p_dec_info->cycle_per_tick;
  vpu_dev->last_performance_cycles = result->dec_decode_end_tick;
  p_dec_info->first_cycle_check = true;
 } else if (result->index_frame_decoded_for_tiled != -1) {
  result->frame_cycle =
   (result->dec_decode_end_tick - vpu_dev->last_performance_cycles) *
   p_dec_info->cycle_per_tick;
  vpu_dev->last_performance_cycles = result->dec_decode_end_tick;
  if (vpu_dev->last_performance_cycles < result->dec_host_cmd_tick)
   result->frame_cycle =
    (result->dec_decode_end_tick - result->dec_host_cmd_tick) *
    p_dec_info->cycle_per_tick;
 }

 /* no remaining command. reset frame cycle. */
 if (p_dec_info->instance_queue_count == 0 && p_dec_info->report_queue_count == 0)
  p_dec_info->first_cycle_check = false;

 return 0;
}

int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size)
{
 struct vpu_buf *common_vb;
 dma_addr_t code_base, temp_base;
 dma_addr_t old_code_base, temp_size;
 u32 code_size, reason_code;
 u32 reg_val;
 struct vpu_device *vpu_dev = dev_get_drvdata(dev);

 common_vb = &vpu_dev->common_mem;

 code_base = common_vb->daddr;

 if (vpu_dev->product_code == WAVE515_CODE)
  code_size = WAVE515_MAX_CODE_BUF_SIZE;
 else
  code_size = WAVE521_MAX_CODE_BUF_SIZE;

 /* ALIGN TO 4KB */
 code_size &= ~0xfff;
 if (code_size < size * 2)
  return -EINVAL;

 temp_base = code_base + code_size;
 temp_size = WAVE5_TEMPBUF_SIZE;

 old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR);

 if (old_code_base != code_base + W5_REMAP_INDEX1 * W5_REMAP_MAX_SIZE) {
  int ret;

  ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size);
  if (ret < 0) {
   dev_err(vpu_dev->dev,
    "VPU init, Writing firmware to common buffer, fail: %d\n", ret);
   return ret;
  }

  vpu_write_reg(vpu_dev, W5_PO_CONF, 0);

  ret = wave5_vpu_reset(dev, SW_RESET_ON_BOOT);
  if (ret < 0) {
   dev_err(vpu_dev->dev, "VPU init, Resetting the VPU, fail: %d\n", ret);
   return ret;
  }

  remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
  remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);

  vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
  vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
  vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);
  vpu_write_reg(vpu_dev, W5_ADDR_TEMP_BASE, temp_base);
  vpu_write_reg(vpu_dev, W5_TEMP_SIZE, temp_size);

  /* These register must be reset explicitly */
  vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);

  if (vpu_dev->product_code != WAVE515_CODE) {
   wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
   wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
   vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
  }

  reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
  if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) {
   reg_val = ((WAVE5_PROC_AXI_ID << 28) |
     (WAVE5_PRP_AXI_ID << 24) |
     (WAVE5_FBD_Y_AXI_ID << 20) |
     (WAVE5_FBC_Y_AXI_ID << 16) |
     (WAVE5_FBD_C_AXI_ID << 12) |
     (WAVE5_FBC_C_AXI_ID << 8) |
     (WAVE5_PRI_AXI_ID << 4) |
     WAVE5_SEC_AXI_ID);
   wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
  }

  if (vpu_dev->product_code == WAVE515_CODE) {
   dma_addr_t task_buf_base;
   u32 i;

   vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF,
          WAVE515_COMMAND_QUEUE_DEPTH);
   vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE,
          WAVE515_ONE_TASKBUF_SIZE);

   for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) {
    task_buf_base = temp_base + temp_size +
      (i * WAVE515_ONE_TASKBUF_SIZE);
    vpu_write_reg(vpu_dev,
           W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4),
           task_buf_base);
   }

   vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI,
          vpu_dev->sram_buf.daddr);
   vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE,
          vpu_dev->sram_buf.size);
  }

  vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
  vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
  vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);

  ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
  if (ret) {
   dev_err(vpu_dev->dev, "VPU reinit(W5_VPU_REMAP_CORE_START) timeout\n");
   return ret;
  }

  ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
  if (ret)
   return ret;
 }

 return setup_wave5_properties(dev);
}

int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code,
    size_t size)
{
 u32 reg_val;
 struct vpu_buf *common_vb;
 dma_addr_t code_base, temp_base;
 u32 code_size, temp_size, reason_code;
 struct vpu_device *vpu_dev = dev_get_drvdata(dev);
 int ret;

 if (i_sleep_wake) {
  ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
  if (ret)
   return ret;

  /*
 * Declare who has ownership for the host interface access
 * 1 = VPU
 * 0 = Host processor
 */

  vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
  vpu_write_reg(vpu_dev, W5_COMMAND, W5_SLEEP_VPU);
  /* Send an interrupt named HOST to the VPU */
  vpu_write_reg(vpu_dev, W5_VPU_HOST_INT_REQ, 1);

  ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
  if (ret)
   return ret;

  ret = wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
  if (ret)
   return ret;
 } else { /* restore */
  common_vb = &vpu_dev->common_mem;

  code_base = common_vb->daddr;

  if (vpu_dev->product_code == WAVE515_CODE)
   code_size = WAVE515_MAX_CODE_BUF_SIZE;
  else
   code_size = WAVE521_MAX_CODE_BUF_SIZE;

  /* ALIGN TO 4KB */
  code_size &= ~0xfff;
  if (code_size < size * 2) {
   dev_err(dev, "size too small\n");
   return -EINVAL;
  }

  temp_base = code_base + code_size;
  temp_size = WAVE5_TEMPBUF_SIZE;

  /* Power on without DEBUG mode */
  vpu_write_reg(vpu_dev, W5_PO_CONF, 0);

  remap_page(vpu_dev, code_base, W5_REMAP_INDEX0);
  remap_page(vpu_dev, code_base, W5_REMAP_INDEX1);

  vpu_write_reg(vpu_dev, W5_ADDR_CODE_BASE, code_base);
  vpu_write_reg(vpu_dev, W5_CODE_SIZE, code_size);
  vpu_write_reg(vpu_dev, W5_CODE_PARAM, (WAVE5_UPPER_PROC_AXI_ID << 4) | 0);

  /* These register must be reset explicitly */
  vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);

  if (vpu_dev->product_code != WAVE515_CODE) {
   wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
   wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
   vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
  }

  setup_wave5_interrupts(vpu_dev);

  reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
  if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) {
   reg_val = ((WAVE5_PROC_AXI_ID << 28) |
     (WAVE5_PRP_AXI_ID << 24) |
     (WAVE5_FBD_Y_AXI_ID << 20) |
     (WAVE5_FBC_Y_AXI_ID << 16) |
     (WAVE5_FBD_C_AXI_ID << 12) |
     (WAVE5_FBC_C_AXI_ID << 8) |
     (WAVE5_PRI_AXI_ID << 4) |
     WAVE5_SEC_AXI_ID);
   wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
  }

  if (vpu_dev->product_code == WAVE515_CODE) {
   dma_addr_t task_buf_base;
   u32 i;

   vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF,
          WAVE515_COMMAND_QUEUE_DEPTH);
   vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE,
          WAVE515_ONE_TASKBUF_SIZE);

   for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) {
    task_buf_base = temp_base + temp_size +
      (i * WAVE515_ONE_TASKBUF_SIZE);
    vpu_write_reg(vpu_dev,
           W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4),
           task_buf_base);
   }

   vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI,
          vpu_dev->sram_buf.daddr);
   vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE,
          vpu_dev->sram_buf.size);
  }

  vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
  vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU);
  /* Start VPU after settings */
  vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);

  ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_BUSY_STATUS);
  if (ret) {
   dev_err(vpu_dev->dev, "VPU wakeup(W5_VPU_REMAP_CORE_START) timeout\n");
   return ret;
  }

  return wave5_vpu_firmware_command_queue_error_check(vpu_dev, &reason_code);
 }

 return 0;
}

int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode)
{
 u32 val = 0;
 int ret = 0;
 struct vpu_device *vpu_dev = dev_get_drvdata(dev);
 struct vpu_attr *p_attr = &vpu_dev->attr;
 /* VPU doesn't send response. force to set BUSY flag to 0. */
 vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 0);

 if (reset_mode == SW_RESET_SAFETY) {
  ret = wave5_vpu_sleep_wake(dev, true, NULL, 0);
  if (ret)
   return ret;
 }

 val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
 if ((val >> 16) & 0x1)
  p_attr->support_backbone = true;
 if ((val >> 22) & 0x1)
  p_attr->support_vcore_backbone = true;
 if ((val >> 28) & 0x1)
  p_attr->support_vcpu_backbone = true;

 /* waiting for completion of bus transaction */
 if (p_attr->support_backbone) {
  dev_dbg(dev, "%s: backbone supported\n", __func__);

  if (p_attr->support_vcore_backbone) {
   if (p_attr->support_vcpu_backbone) {
    /* step1 : disable request */
    wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0xFF);

    /* step2 : waiting for completion of bus transaction */
    ret = wave5_wait_vcpu_bus_busy(vpu_dev,
              W5_BACKBONE_BUS_STATUS_VCPU);
    if (ret) {
     wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00);
     return ret;
    }
   }
   /* step1 : disable request */
   wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x7);

   /* step2 : waiting for completion of bus transaction */
   if (wave5_wait_bus_busy(vpu_dev, W5_BACKBONE_BUS_STATUS_VCORE0)) {
    wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00);
    return -EBUSY;
   }
  } else {
   /* step1 : disable request */
   wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x7);

   /* step2 : waiting for completion of bus transaction */
   if (wave5_wait_bus_busy(vpu_dev, W5_COMBINED_BACKBONE_BUS_STATUS)) {
    wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00);
    return -EBUSY;
   }
  }
 } else {
  dev_dbg(dev, "%s: backbone NOT supported\n", __func__);
  /* step1 : disable request */
  wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x100);

  /* step2 : waiting for completion of bus transaction */
  ret = wave5_wait_bus_busy(vpu_dev, W5_GDI_BUS_STATUS);
  if (ret) {
   wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00);
   return ret;
  }
 }

 switch (reset_mode) {
 case SW_RESET_ON_BOOT:
 case SW_RESET_FORCE:
 case SW_RESET_SAFETY:
  val = W5_RST_BLOCK_ALL;
  break;
 default:
  return -EINVAL;
 }

 if (val) {
  vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, val);

  ret = wave5_wait_vpu_busy(vpu_dev, W5_VPU_RESET_STATUS);
  if (ret) {
   vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0);
   return ret;
  }
  vpu_write_reg(vpu_dev, W5_VPU_RESET_REQ, 0);
 }
 /* step3 : must clear GDI_BUS_CTRL after done SW_RESET */
 if (p_attr->support_backbone) {
  if (p_attr->support_vcore_backbone) {
   if (p_attr->support_vcpu_backbone)
    wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCPU, 0x00);
   wave5_fio_writel(vpu_dev, W5_BACKBONE_BUS_CTRL_VCORE0, 0x00);
  } else {
   wave5_fio_writel(vpu_dev, W5_COMBINED_BACKBONE_BUS_CTRL, 0x00);
  }
 } else {
  wave5_fio_writel(vpu_dev, W5_GDI_BUS_CTRL, 0x00);
 }
 if (reset_mode == SW_RESET_SAFETY || reset_mode == SW_RESET_FORCE)
  ret = wave5_vpu_sleep_wake(dev, false, NULL, 0);

 return ret;
}

int wave5_vpu_dec_finish_seq(struct vpu_instance *inst, u32 *fail_res)
{
 return send_firmware_command(inst, W5_DESTROY_INSTANCE, true, NULL, fail_res);
}

int wave5_vpu_dec_set_bitstream_flag(struct vpu_instance *inst, bool eos)
{
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;

 p_dec_info->stream_endflag = eos ? 1 : 0;
 vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
 vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);

 return send_firmware_command(inst, W5_UPDATE_BS, true, NULL, NULL);
}

int wave5_dec_clr_disp_flag(struct vpu_instance *inst, unsigned int index)
{
 struct dec_info *p_dec_info = &inst->codec_info->dec_info;
 int ret;

 vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, BIT(index));
 vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, 0);

 ret = wave5_send_query(inst->dev, inst, UPDATE_DISP_FLAG);
 if (ret)
  return ret;

 p_dec_info->frame_display_flag = vpu_read_reg(inst->dev, W5_RET_DEC_DISP_IDC);

 return 0;
}

int wave5_dec_set_disp_flag(struct vpu_instance *inst, unsigned int index)
{
 int ret;

 vpu_write_reg(inst->dev, W5_CMD_DEC_CLR_DISP_IDC, 0);
 vpu_write_reg(inst->dev, W5_CMD_DEC_SET_DISP_IDC, BIT(index));

 ret = wave5_send_query(inst->dev, inst, UPDATE_DISP_FLAG);
 if (ret)
  return ret;

 return 0;
}

int wave5_vpu_clear_interrupt(struct vpu_instance *inst, u32 flags)
{
 u32 interrupt_reason;

 interrupt_reason = vpu_read_reg(inst->dev, W5_VPU_VINT_REASON_USR);
 interrupt_reason &= ~flags;
 vpu_write_reg(inst->dev, W5_VPU_VINT_REASON_USR, interrupt_reason);

 return 0;
}

dma_addr_t wave5_dec_get_rd_ptr(struct vpu_instance *inst)
{
 int ret;

 ret = wave5_send_query(inst->dev, inst, GET_BS_RD_PTR);
 if (ret)
  return inst->codec_info->dec_info.stream_rd_ptr;

 return vpu_read_reg(inst->dev, W5_RET_QUERY_DEC_BS_RD_PTR);
}

int wave5_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr)
{
 int ret;

 vpu_write_reg(inst->dev, W5_RET_QUERY_DEC_SET_BS_RD_PTR, addr);

 ret = wave5_send_query(inst->dev, inst, SET_BS_RD_PTR);

 return ret;
}

/************************************************************************/
/* ENCODER functions */
/************************************************************************/

int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst,
     struct enc_open_param *open_param)
{
 int ret;
 struct enc_info *p_enc_info = &inst->codec_info->enc_info;
 u32 reg_val;
 struct vpu_device *vpu_dev = dev_get_drvdata(dev);
 dma_addr_t buffer_addr;
 size_t buffer_size;

 p_enc_info->cycle_per_tick = 256;
 if (vpu_dev->sram_buf.size) {
  p_enc_info->sec_axi_info.use_enc_rdo_enable = 1;
  p_enc_info->sec_axi_info.use_enc_lf_enable = 1;
 }

 p_enc_info->vb_work.size = WAVE521ENC_WORKBUF_SIZE;
 ret = wave5_vdi_allocate_dma_memory(vpu_dev, &p_enc_info->vb_work);
 if (ret) {
  memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work));
  return ret;
 }

 wave5_vdi_clear_memory(vpu_dev, &p_enc_info->vb_work);

 vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_enc_info->vb_work.daddr);
 vpu_write_reg(inst->dev, W5_WORK_SIZE, p_enc_info->vb_work.size);

 vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
 vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);

 reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN;
 vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val);
 vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
 vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, WAVE521_COMMAND_QUEUE_DEPTH - 1);

 /* This register must be reset explicitly */
 vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_VCORE_INFO, 1);

 ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
 if (ret)
  goto free_vb_work;

 buffer_addr = open_param->bitstream_buffer;
 buffer_size = open_param->bitstream_buffer_size;
 p_enc_info->stream_rd_ptr = buffer_addr;
 p_enc_info->stream_wr_ptr = buffer_addr;
 p_enc_info->line_buf_int_en = open_param->line_buf_int_en;
 p_enc_info->stream_buf_start_addr = buffer_addr;
 p_enc_info->stream_buf_size = buffer_size;
 p_enc_info->stream_buf_end_addr = buffer_addr + buffer_size;
 p_enc_info->stride = 0;
 p_enc_info->initial_info_obtained = false;
 p_enc_info->product_code = vpu_read_reg(inst->dev, W5_PRODUCT_NUMBER);

 return 0;
free_vb_work:
 if (wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_work))
  memset(&p_enc_info->vb_work, 0, sizeof(p_enc_info->vb_work));
 return ret;
}

static void wave5_set_enc_crop_info(u32 codec, struct enc_wave_param *param, int rot_mode,
        int src_width, int src_height)
{
 int aligned_width = (codec == W_HEVC_ENC) ? ALIGN(src_width, 32) : ALIGN(src_width, 16);
 int aligned_height = (codec == W_HEVC_ENC) ? ALIGN(src_height, 32) : ALIGN(src_height, 16);
 int pad_right, pad_bot;
 int crop_right, crop_left, crop_top, crop_bot;
 int prp_mode = rot_mode >> 1; /* remove prp_enable bit */

 if (codec == W_HEVC_ENC &&
     (!rot_mode || prp_mode == 14)) /* prp_mode 14 : hor_mir && ver_mir && rot_180 */
  return;

 pad_right = aligned_width - src_width;
 pad_bot = aligned_height - src_height;

 if (param->conf_win_right > 0)
  crop_right = param->conf_win_right + pad_right;
 else
  crop_right = pad_right;

 if (param->conf_win_bot > 0)
  crop_bot = param->conf_win_bot + pad_bot;
 else
  crop_bot = pad_bot;

 crop_top = param->conf_win_top;
 crop_left = param->conf_win_left;

 param->conf_win_top = crop_top;
 param->conf_win_left = crop_left;
 param->conf_win_bot = crop_bot;
 param->conf_win_right = crop_right;

 switch (prp_mode) {
 case 0:
  return;
 case 1:
 case 15:
  param->conf_win_top = crop_right;
  param->conf_win_left = crop_top;
  param->conf_win_bot = crop_left;
  param->conf_win_right = crop_bot;
  break;
 case 2:
 case 12:
  param->conf_win_top = crop_bot;
  param->conf_win_left = crop_right;
  param->conf_win_bot = crop_top;
  param->conf_win_right = crop_left;
  break;
 case 3:
 case 13:
  param->conf_win_top = crop_left;
  param->conf_win_left = crop_bot;
  param->conf_win_bot = crop_right;
  param->conf_win_right = crop_top;
  break;
 case 4:
 case 10:
  param->conf_win_top = crop_bot;
  param->conf_win_bot = crop_top;
  break;
 case 8:
 case 6:
  param->conf_win_left = crop_right;
  param->conf_win_right = crop_left;
  break;
 case 5:
 case 11:
  param->conf_win_top = crop_left;
  param->conf_win_left = crop_top;
  param->conf_win_bot = crop_right;
  param->conf_win_right = crop_bot;
  break;
 case 7:
 case 9:
  param->conf_win_top = crop_right;
  param->conf_win_left = crop_bot;
  param->conf_win_bot = crop_left;
  param->conf_win_right = crop_top;
  break;
 default:
  WARN(1, "Invalid prp_mode: %d, must be in range of 1 - 15\n", prp_mode);
 }
}

int wave5_vpu_enc_init_seq(struct vpu_instance *inst)
{
 u32 reg_val = 0, rot_mir_mode, fixed_cu_size_mode = 0x7;
 struct enc_info *p_enc_info = &inst->codec_info->enc_info;
 struct enc_open_param *p_open_param = &p_enc_info->open_param;
 struct enc_wave_param *p_param = &p_open_param->wave_param;

 /*
 * OPT_COMMON:
 * the last SET_PARAM command should be called with OPT_COMMON
 */

 rot_mir_mode = 0;
 if (p_enc_info->rotation_enable) {
  switch (p_enc_info->rotation_angle) {
  case 0:
   rot_mir_mode |= NONE_ROTATE;
   break;
  case 90:
   rot_mir_mode |= ROT_CLOCKWISE_90;
   break;
  case 180:
   rot_mir_mode |= ROT_CLOCKWISE_180;
   break;
  case 270:
   rot_mir_mode |= ROT_CLOCKWISE_270;
   break;
  }
 }

 if (p_enc_info->mirror_enable) {
  switch (p_enc_info->mirror_direction) {
  case MIRDIR_NONE:
   rot_mir_mode |= NONE_ROTATE;
   break;
  case MIRDIR_VER:
   rot_mir_mode |= MIR_VER_FLIP;
   break;
  case MIRDIR_HOR:
   rot_mir_mode |= MIR_HOR_FLIP;
   break;
  case MIRDIR_HOR_VER:
   rot_mir_mode |= MIR_HOR_VER_FLIP;
   break;
  }
 }

 wave5_set_enc_crop_info(inst->std, p_param, rot_mir_mode, p_open_param->pic_width,
    p_open_param->pic_height);

 /* SET_PARAM + COMMON */
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SET_PARAM_OPTION, OPT_COMMON);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SRC_SIZE, p_open_param->pic_height << 16
   | p_open_param->pic_width);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MAP_ENDIAN, VDI_LITTLE_ENDIAN);

 reg_val = p_param->profile |
  (p_param->level << 3) |
  (p_param->internal_bit_depth << 14);
 if (inst->std == W_HEVC_ENC)
  reg_val |= (p_param->tier << 12) |
   (p_param->tmvp_enable << 23) |
   (p_param->sao_enable << 24) |
   (p_param->skip_intra_trans << 25) |
   (p_param->strong_intra_smooth_enable << 27) |
   (p_param->en_still_picture << 30);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val);

 reg_val = (p_param->lossless_enable) |
  (p_param->const_intra_pred_flag << 1) |
  (p_param->lf_cross_slice_boundary_enable << 2) |
  (p_param->wpp_enable << 4) |
  (p_param->disable_deblk << 5) |
  ((p_param->beta_offset_div2 & 0xF) << 6) |
  ((p_param->tc_offset_div2 & 0xF) << 10) |
  ((p_param->chroma_cb_qp_offset & 0x1F) << 14) |
  ((p_param->chroma_cr_qp_offset & 0x1F) << 19) |
  (p_param->transform8x8_enable << 29) |
  (p_param->entropy_coding_mode << 30);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_PPS_PARAM, reg_val);

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_GOP_PARAM, p_param->gop_preset_idx);

 if (inst->std == W_AVC_ENC)
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, p_param->intra_qp |
         ((p_param->intra_period & ENC_AVC_INTRA_IDR_PARAM_MASK)
    << ENC_AVC_INTRA_PERIOD_SHIFT) |
         ((p_param->avc_idr_period & ENC_AVC_INTRA_IDR_PARAM_MASK)
    << ENC_AVC_IDR_PERIOD_SHIFT) |
         (p_param->forced_idr_header_enable
          << ENC_AVC_FORCED_IDR_HEADER_SHIFT));
 else if (inst->std == W_HEVC_ENC)
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM,
         p_param->decoding_refresh_type |
         (p_param->intra_qp << ENC_HEVC_INTRA_QP_SHIFT) |
         (p_param->forced_idr_header_enable
          << ENC_HEVC_FORCED_IDR_HEADER_SHIFT) |
         (p_param->intra_period << ENC_HEVC_INTRA_PERIOD_SHIFT));

 reg_val = (p_param->rdo_skip << 2) |
  (p_param->lambda_scaling_enable << 3) |
  (fixed_cu_size_mode << 5) |
  (p_param->intra_nx_n_enable << 8) |
  (p_param->max_num_merge << 18);

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RDO_PARAM, reg_val);

 if (inst->std == W_AVC_ENC)
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH,
         p_param->intra_mb_refresh_arg << 16 | p_param->intra_mb_refresh_mode);
 else if (inst->std == W_HEVC_ENC)
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_REFRESH,
         p_param->intra_refresh_arg << 16 | p_param->intra_refresh_mode);

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_FRAME_RATE, p_open_param->frame_rate_info);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_TARGET_RATE, p_open_param->bit_rate);

 reg_val = p_open_param->rc_enable |
  (p_param->hvs_qp_enable << 2) |
  (p_param->hvs_qp_scale << 4) |
  ((p_param->initial_rc_qp & 0x3F) << 14) |
  (p_open_param->vbv_buffer_size << 20);
 if (inst->std == W_AVC_ENC)
  reg_val |= (p_param->mb_level_rc_enable << 1);
 else if (inst->std == W_HEVC_ENC)
  reg_val |= (p_param->cu_level_rc_enable << 1);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_PARAM, reg_val);

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_WEIGHT_PARAM,
        p_param->rc_weight_buf << 8 | p_param->rc_weight_param);

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_MIN_MAX_QP, p_param->min_qp_i |
        (p_param->max_qp_i << 6) | (p_param->hvs_max_delta_qp << 12));

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_INTER_MIN_MAX_QP, p_param->min_qp_p |
        (p_param->max_qp_p << 6) | (p_param->min_qp_b << 12) |
        (p_param->max_qp_b << 18));

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_0_3, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode);

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT,
        p_param->conf_win_bot << 16 | p_param->conf_win_top);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_LEFT_RIGHT,
        p_param->conf_win_right << 16 | p_param->conf_win_left);

 if (inst->std == W_AVC_ENC)
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE,
         p_param->avc_slice_arg << 16 | p_param->avc_slice_mode);
 else if (inst->std == W_HEVC_ENC)
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INDEPENDENT_SLICE,
         p_param->independ_slice_mode_arg << 16 |
         p_param->independ_slice_mode);

 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_USER_SCALING_LIST_ADDR, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NUM_UNITS_IN_TICK, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_TIME_SCALE, 0);
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NUM_TICKS_POC_DIFF_ONE, 0);

 if (inst->std == W_HEVC_ENC) {
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU04, 0);
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU08, 0);
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU16, 0);
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_PU32, 0);
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU08, 0);
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU16, 0);
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_MD_CU32, 0);
  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_DEPENDENT_SLICE,
         p_param->depend_slice_mode_arg << 16 | p_param->depend_slice_mode);

  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_PARAM, 0);

  vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_NR_WEIGHT,
         p_param->nr_intra_weight_y |
         (p_param->nr_intra_weight_cb << 5) |
         (p_param->nr_intra_weight_cr << 10) |
         (p_param->nr_inter_weight_y << 15) |
         (p_param->nr_inter_weight_cb << 20) |
         (p_param->nr_inter_weight_cr << 25));
 }
 vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_VUI_HRD_PARAM, 0);

 return send_firmware_command(inst, W5_ENC_SET_PARAM, true, NULL, NULL);
}

int wave5_vpu_enc_get_seq_info(struct vpu_instance *inst, struct enc_initial_info *info)
{
 int ret;
 u32 reg_val;
 struct enc_info *p_enc_info = &inst->codec_info->enc_info;

 /* send QUERY cmd */
 ret = wave5_send_query(inst->dev, inst, GET_RESULT);
 if (ret)
  return ret;

 dev_dbg(inst->dev->dev, "%s: init seq\n", __func__);

 reg_val = vpu_read_reg(inst->dev, W5_RET_QUEUE_STATUS);

 p_enc_info->instance_queue_count = (reg_val >> 16) & 0xff;
 p_enc_info->report_queue_count = (reg_val & QUEUE_REPORT_MASK);

 if (vpu_read_reg(inst->dev, W5_RET_ENC_ENCODING_SUCCESS) != 1) {
  info->seq_init_err_reason = vpu_read_reg(inst->dev, W5_RET_ENC_ERR_INFO);
  ret = -EIO;
 } else {
  info->warn_info = vpu_read_reg(inst->dev, W5_RET_ENC_WARN_INFO);
 }

 info->min_frame_buffer_count = vpu_read_reg(inst->dev, W5_RET_ENC_NUM_REQUIRED_FB);
 info->min_src_frame_count = vpu_read_reg(inst->dev, W5_RET_ENC_MIN_SRC_BUF_NUM);
 info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
 info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
 p_enc_info->vlc_buf_size = info->vlc_buf_size;
 p_enc_info->param_buf_size = info->param_buf_size;

 return ret;
}

static u32 calculate_luma_stride(u32 width, u32 bit_depth)
{
 return ALIGN(ALIGN(width, 16) * ((bit_depth > 8) ? 5 : 4), 32);
}

static u32 calculate_chroma_stride(u32 width, u32 bit_depth)
{
 return ALIGN(ALIGN(width / 2, 16) * ((bit_depth > 8) ? 5 : 4), 32);
}

int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *inst,
           struct frame_buffer *fb_arr, enum tiled_map_type map_type,
           unsigned int count)
{
 struct vpu_device *vpu_dev = dev_get_drvdata(dev);
 int ret = 0;
 u32 stride;
 u32 start_no, end_no;
 size_t remain, idx, j, i, cnt_8_chunk;
 u32 reg_val = 0, pic_size = 0, mv_col_size, fbc_y_tbl_size, fbc_c_tbl_size;
 u32 sub_sampled_size = 0;
 u32 luma_stride, chroma_stride;
 u32 buf_height = 0, buf_width = 0;
 u32 bit_depth;
 bool avc_encoding = (inst->std == W_AVC_ENC);
 struct vpu_buf vb_mv = {0};
 struct vpu_buf vb_fbc_y_tbl = {0};
 struct vpu_buf vb_fbc_c_tbl = {0};
 struct vpu_buf vb_sub_sam_buf = {0};
 struct vpu_buf vb_task = {0};
 struct enc_open_param *p_open_param;
 struct enc_info *p_enc_info = &inst->codec_info->enc_info;

 p_open_param = &p_enc_info->open_param;
 mv_col_size = 0;
 fbc_y_tbl_size = 0;
 fbc_c_tbl_size = 0;
 stride = p_enc_info->stride;
 bit_depth = p_open_param->wave_param.internal_bit_depth;

 if (avc_encoding) {
  buf_width = ALIGN(p_open_param->pic_width, 16);
  buf_height = ALIGN(p_open_param->pic_height, 16);

  if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
      !(p_enc_info->rotation_angle == 180 &&
     p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
   buf_width = ALIGN(p_open_param->pic_width, 16);
   buf_height = ALIGN(p_open_param->pic_height, 16);
  }

  if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) {
   buf_width = ALIGN(p_open_param->pic_height, 16);
   buf_height = ALIGN(p_open_param->pic_width, 16);
  }
 } else {
  buf_width = ALIGN(p_open_param->pic_width, 8);
  buf_height = ALIGN(p_open_param->pic_height, 8);

  if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
      !(p_enc_info->rotation_angle == 180 &&
     p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
   buf_width = ALIGN(p_open_param->pic_width, 32);
   buf_height = ALIGN(p_open_param->pic_height, 32);
  }

  if (p_enc_info->rotation_angle == 90 || p_enc_info->rotation_angle == 270) {
   buf_width = ALIGN(p_open_param->pic_height, 32);
   buf_height = ALIGN(p_open_param->pic_width, 32);
  }
 }

 pic_size = (buf_width << 16) | buf_height;

 if (avc_encoding) {
  mv_col_size = WAVE5_ENC_AVC_BUF_SIZE(buf_width, buf_height);
  vb_mv.daddr = 0;
  vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
 } else {
  mv_col_size = WAVE5_ENC_HEVC_BUF_SIZE(buf_width, buf_height);
  mv_col_size = ALIGN(mv_col_size, 16);
  vb_mv.daddr = 0;
  vb_mv.size = ALIGN(mv_col_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
 }

 ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_mv);
 if (ret)
  return ret;

 p_enc_info->vb_mv = vb_mv;

 fbc_y_tbl_size = ALIGN(WAVE5_FBC_LUMA_TABLE_SIZE(buf_width, buf_height), 16);
 fbc_c_tbl_size = ALIGN(WAVE5_FBC_CHROMA_TABLE_SIZE(buf_width, buf_height), 16);

 vb_fbc_y_tbl.daddr = 0;
 vb_fbc_y_tbl.size = ALIGN(fbc_y_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
 ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_y_tbl);
 if (ret)
  goto free_vb_fbc_y_tbl;

 p_enc_info->vb_fbc_y_tbl = vb_fbc_y_tbl;

 vb_fbc_c_tbl.daddr = 0;
 vb_fbc_c_tbl.size = ALIGN(fbc_c_tbl_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
 ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_fbc_c_tbl);
 if (ret)
  goto free_vb_fbc_c_tbl;

 p_enc_info->vb_fbc_c_tbl = vb_fbc_c_tbl;

 if (avc_encoding)
  sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE_AVC(buf_width, buf_height);
 else
  sub_sampled_size = WAVE5_SUBSAMPLED_ONE_SIZE(buf_width, buf_height);
 vb_sub_sam_buf.size = ALIGN(sub_sampled_size * count, BUFFER_MARGIN) + BUFFER_MARGIN;
 vb_sub_sam_buf.daddr = 0;
 ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_sub_sam_buf);
 if (ret)
  goto free_vb_sam_buf;

 p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf;

 vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) +
   (p_enc_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH);
 vb_task.daddr = 0;
 if (p_enc_info->vb_task.size == 0) {
  ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task);
  if (ret)
   goto free_vb_task;

  p_enc_info->vb_task = vb_task;

  vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
         p_enc_info->vb_task.daddr);
  vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_task.size);
 }

 /* set sub-sampled buffer base addr */
 vpu_write_reg(inst->dev, W5_ADDR_SUB_SAMPLED_FB_BASE, vb_sub_sam_buf.daddr);
 /* set sub-sampled buffer size for one frame */
 vpu_write_reg(inst->dev, W5_SUB_SAMPLED_ONE_FB_SIZE, sub_sampled_size);

 vpu_write_reg(inst->dev, W5_PIC_SIZE, pic_size);

 /* set stride of luma/chroma for compressed buffer */
 if ((p_enc_info->rotation_angle || p_enc_info->mirror_direction) &&
     !(p_enc_info->rotation_angle == 180 &&
     p_enc_info->mirror_direction == MIRDIR_HOR_VER)) {
  luma_stride = calculate_luma_stride(buf_width, bit_depth);
  chroma_stride = calculate_chroma_stride(buf_width / 2, bit_depth);
 } else {
  luma_stride = calculate_luma_stride(p_open_param->pic_width, bit_depth);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=92 G=94

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge