194 lines
5.9 KiB
C
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;
|
|
}
|