194 lines
5.9 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2024 Rockchip Electronics Co., Ltd.
*
* author:
* Ding Wei <leo.ding@rock-chips.com>
*/
#include <linux/delay.h>
#include "../mpp_rkvdec2.h"
#include "../mpp_rkvdec2_link.h"
#include "mpp_hack_rk3576.h"
#define RK3576_HACK_DATA_RLC_OFFSET (0)
#define RK3576_HACK_DATA_PPS_OFFSET (RK3576_HACK_DATA_RLC_OFFSET + 64)
#define RK3576_HACK_DATA_RPS_OFFSET (RK3576_HACK_DATA_PPS_OFFSET + 256)
#define RK3576_HACK_DATA_RCB_STRMD_OFFSET (RK3576_HACK_DATA_RPS_OFFSET + 256)
#define RK3576_HACK_DATA_RCB_INTRA_OFFSET (RK3576_HACK_DATA_RCB_STRMD_OFFSET + 256)
#define RK3576_HACK_DATA_OUT_OFFSET (RK3576_HACK_DATA_RCB_INTRA_OFFSET + 512)
#define RK3576_HACK_DATA_COLMV_OFFSET (RK3576_HACK_DATA_OUT_OFFSET + 1536)
#define RK3576_HACK_REGS_OFFSET (4096)
#define RK3576_HACK_REG_NODE_OFFSET (RK3576_HACK_REGS_OFFSET)
#define RK3576_HACK_REG_SEG0_OFFSET (RK3576_HACK_REGS_OFFSET + 32)
#define RK3576_HACK_REG_SEG1_OFFSET (RK3576_HACK_REGS_OFFSET + 256)
#define RK3576_HACK_REG_SEG2_OFFSET (RK3576_HACK_REGS_OFFSET + 512)
#define RK3576_HACK_REG_DEBUG_OFFSET (RK3576_HACK_REGS_OFFSET + 1024)
static const char rk3576_hack_h264_rlc_data[] = {
0x00, 0x00, 0x01, 0x65,
0x88, 0x82, 0x0b, 0x01,
0x2f, 0x08, 0xc5, 0x00,
0x01, 0x51, 0x78, 0xe0,
0x00, 0x24, 0xf7, 0x1c,
0x00, 0x04, 0xcc, 0xeb,
0x89, 0xd7, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00,
};
static const char rk3576_hack_h264_pps_data[] = {
0x40, 0x26, 0x00, 0x10,
0x04, 0x08, 0x00, 0x08,
0x80, 0x01, 0x00, 0x00,
0x00, 0x40, 0x01, 0xd8,
0x07, 0x7c, 0x7a, 0x00,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00,
};
static bool rk3576_hack_flag;
static bool is_rk3576(struct device *dev)
{
int ret;
ret = device_property_match_string(dev, "compatible", "rockchip,rkv-decoder-rk3576");
return (ret < 0) ? false : true;
}
int rk3576_workaround_init(struct mpp_dev *mpp)
{
struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp);
u32 *reg;
/* check current soc is rk3576 */
rk3576_hack_flag = is_rk3576(mpp->dev);
if (!rk3576_hack_flag)
return 0;
/* alloc buffer for node */
dec->fix = mpp_dma_alloc(mpp->dev, 2 * PAGE_SIZE);
if (!dec->fix) {
dev_err(mpp->dev, "failed to create buffer for hack\n");
return -ENOMEM;
}
memset(dec->fix->vaddr, 0, dec->fix->size);
/* set rlc_in */
memcpy(dec->fix->vaddr + RK3576_HACK_DATA_RLC_OFFSET,
rk3576_hack_h264_rlc_data, sizeof(rk3576_hack_h264_rlc_data));
/* set sps_pps */
memcpy(dec->fix->vaddr + RK3576_HACK_DATA_PPS_OFFSET,
rk3576_hack_h264_pps_data, sizeof(rk3576_hack_h264_pps_data));
/* set core registers */
reg = dec->fix->vaddr + RK3576_HACK_REGS_OFFSET;
reg[8] = 0x00000001; // dec_mode
reg[13] = 0x0000ffff; // core time out
reg[16] = 0x00000101; // disable error proc
reg[20] = 0xffffffff; // cabac error low bits
reg[21] = 0x3ff3ffff; // cabac error high bits
reg[64] = RK3576_HACK_MAGIC; // magic number
reg[65] = 0x00000000; // stream start bit
reg[66] = 0x00000020; // stream_len
reg[67] = 0x000000a8; // global_len
reg[68] = 0x00000002; // hor_virstride
reg[69] = 0x00000002; // ver_virstride
reg[70] = 0x00000040; // y_virstride
reg[128] = dec->fix->iova + RK3576_HACK_DATA_RLC_OFFSET; // rlc_base
reg[129] = dec->fix->iova + RK3576_HACK_DATA_RPS_OFFSET; // rps_base
reg[131] = dec->fix->iova + RK3576_HACK_DATA_PPS_OFFSET; // pps_base
reg[140] = dec->fix->iova + RK3576_HACK_DATA_RCB_STRMD_OFFSET; // streamd_rcb
reg[141] = 0x000000c0;
reg[148] = dec->fix->iova + RK3576_HACK_DATA_RCB_INTRA_OFFSET; // intra_rcb
reg[149] = 0x00000200;
reg[168] = dec->fix->iova + RK3576_HACK_DATA_OUT_OFFSET; // decout_base
reg[216] = dec->fix->iova + RK3576_HACK_DATA_COLMV_OFFSET; // colmv_base
/* set link node */
reg = dec->fix->vaddr + RK3576_HACK_REG_NODE_OFFSET;
reg[0] = dec->fix->iova + RK3576_HACK_DATA_OUT_OFFSET; // next link node
reg[1] = 0x76543212; // table info
reg[2] = dec->fix->iova + RK3576_HACK_REG_DEBUG_OFFSET; // debug regs
reg[3] = dec->fix->iova + RK3576_HACK_REG_SEG0_OFFSET; // seg0 regs
reg[4] = dec->fix->iova + RK3576_HACK_REG_SEG1_OFFSET; // seg1 regs
reg[5] = dec->fix->iova + RK3576_HACK_REG_SEG2_OFFSET; // seg2 regs
return 0;
}
int rk3576_workaround_exit(struct mpp_dev *mpp)
{
struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp);
/* check current soc is rk3576 */
if (!rk3576_hack_flag)
return 0;
if (dec->fix) {
mpp_dma_free(dec->fix);
dec->fix = NULL;
}
return 0;
}
int rk3576_workaround_run(struct mpp_dev *mpp)
{
struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp);
struct rkvdec_link_dev *link = dec->link_dec;
int ret;
u32 irq_val, status;
/* check current soc is rk3576 */
if (!rk3576_hack_flag || !dec->fix || !link)
return 0;
/* disable hardware irq */
writel_relaxed(0x00008000, link->reg_base + 0x58);
/* set link registers */
writel_relaxed(0x0007ffff, link->reg_base + 0x54); // set ip time out
writel_relaxed(0x00010001, link->reg_base + 0x00); // ccu mode
writel_relaxed(dec->fix->iova + RK3576_HACK_REG_NODE_OFFSET, link->reg_base + 0x4); // addr
writel_relaxed(0x00000001, link->reg_base + 0x08); // link mode
writel_relaxed(0x00000001, link->reg_base + 0x18); // link en
/* enable hardware */
wmb();
writel(0x01, link->reg_base + 0x0c); // config link
/* wait hardware */
ret = readl_relaxed_poll_timeout_atomic(link->reg_base + 0x48, irq_val, irq_val, 1, 500);
if (ret == -ETIMEDOUT) {
pr_err("%s timeout.\n", __func__);
// return ret;
} else {
status = readl(link->reg_base + 0x4c);
if (status & 0x3fe)
pr_err("%s not ready, status %08x.\n", __func__, status);
}
/* clear irq and status */
writel_relaxed(0xffff0000, link->reg_base + 0x48);
writel_relaxed(0xffff0000, link->reg_base + 0x4c);
/* reset register */
writel_relaxed(0x00000000, link->reg_base + 0x00); // ccu mode
writel_relaxed(0x00000000, link->reg_base + 0x08); // link mode
writel_relaxed(0x00000000, link->reg_base + 0x18); // link en
/* enable irq */
writel(0x00000000, link->reg_base + 0x58);
udelay(5);
return ret;
}